CRUD de usuarios en NodeJS: Parte 3

Ahora que ya tenemos el servidor de Node levantado y la base de datos en MongoDB operativa, hay que diseñar la entidad de usuario y sus acciones. Para ello vamos a empezar por crear el modelo, pero antes vamos a añadir extensiones que necesitaremos más adelante, empezando por mongoose-unique-validator, que agrega validación previa al guardado para campos únicos dentro de un esquema Mongoose:

   $ npm install --save mongoose-unique-validator

Vamos a instalar también la extensión bcrypt, con ella vamos a cifrar la contraseña para "hashearla". Esto quiere decir que vamos a usar contraseñas con un método de cifrado del tipo hash.

   $ npm install --save bcrypt

Otra librería que nos vendrá muy bien para recuperar los campos de un formulario para actualizar nuestro usuario será underscore y para instalarla lanzamos:

   $ npm install --save underscore

Para acabar, vamos a añadir la librería body-parser que nos permitirá coger cualquier dato (de usuario, paginación, limite para mostrar. etc) que enviemos al servidor al consumir los servicios API Rest, directamente desde el objeto body dentro de req, así req.body.foo. A parte, podemos añadir validaciones de tipo de dato de una manera sencilla. Por ejemplo: req.body.foo.toString()

   $ npm install --save body-parser

Cuando hayamos terminado de instalar las librerías, creamos una carpeta nueva llamada models dentro de la carpeta server:

   $ mkdir server/models

Dentro de la carpeta creamos un fichero llamado User.js, este fichero contendrá el esquema de nuestra entidad usuario y será el encargado de crear este esquema en la base de datos:

    const mongoose = require('mongoose');
    const uniqueValidator = require('mongoose-unique-validator');

    let Schema = mongoose.Schema;

    let userSchema = new Schema({
        name: {
            type: String,
            required: [true, 'El nombre es requerido']
        },
        email:{
            type: String,
            unique: true,
            required: [true, 'El correo electrónico es requerido']
        },
        password: {
            type: String,
            required: [true, 'La contraseña es requerido']
        },
        img: {
            type: String,
            required: false
        },
        status: {
            type: Boolean,
            default: true
        }
    });

    userSchema.methods.toJSON = function(){
        let user = this;
        let userObject = user.toObject();
        delete userObject.password;
        return userObject;
    }

    userSchema.plugin(uniqueValidator, {
        message: '{PATH} debe de ser único'
    });

    module.exports = mongoose.model('User', userSchema);

Una vez hayamos creado el modelo volemos a crear una carpeta nueva llamada routes, dentro de server y al mismo nivel que models.

   $ mkdir server/routes

Dentro de ella vamos a crear un fichero llamado users.js, y será el fichero que contendrá las acciones que se pueden hacer sobre nuestros usuarios. Es el fichero que contiene las acciones del CRUD. Los servicios serán del tipo:

  • POST para crear.
  • PUT para editar.
  • GET para listar.
  • DELETE para borrar o desactivar un usuario.

Este fichero user.js contiene lo siguiente:

    const express = require('express');
    const bcrypt = require('bcrypt');
    const _ = require('underscore');
    const User = require('../models/User');
    const app = express();

    // Listado de usuarios
    app.get('/usuario', (req, res) => {

        let from = req.query.from || 0;
        from = Number(from);

        let limit = req.query.limit || 5;
        limit = Number(limit);

        let conditions = {
            status: true
        }

        User.find(conditions, 'name email status')
            .skip(from)
            .limit(limit)
            .exec((err, users) => {
            if(err){
                return res.status(400).json({
                ok: false,
                err  
                });
            }
        
            User.countDocuments(conditions,(err, sumUsers) => {
                res.json({
                    ok: true,
                    users,
                    sumUsers
                });
            });
        });
    });

    // Crear usuario
    app.post('/usuario', (req, res) => {
        let body = req.body;

        let user = new User({
            name: body.name,
            email: body.email,
            password: bcrypt.hashSync(body.password, 10)
        });

        user.save((err, userDB) => {
            if(err){
                return res.status(400).json({
                ok: false,
                err  
                });
            }

            res.json({
                ok: true,
                usuario: userDB
            });
        });
    });

    // Actualizar usuario
    app.put('/usuario/:id', (req, res) => {
        let id = req.params.id;
        let body = _.pick(req.body, ['name', 'email', 'img', 'status']);

        User.findByIdAndUpdate(id, body, {new: true, runValidators: true}, (err, userBD) => {
            if(err){
                return res.status(400).json({
                ok: false,
                err  
                });
            }

            res.json({
                ok: true,
                usuario: userBD
            });
        });
    });

    // Eliminar/Desactivar usuario
    app.delete('/usuario/:id', (req, res) => {
        let id = req.params.id;
        User.findByIdAndUpdate(id, {status: false}, {new: true}, (err, userBD) => {
            if(err){
                return res.status(400).json({
                ok: false,
                err  
                });
            }

            if(!userBD){
                return res.status(400).json({
                    ok: false,
                    err:{
                        message: 'Usuario no encontrado'
                    } 
                });
            }
            
            res.json({
                ok: true,
                userBD
            });
        });
    });

    module.exports = app;

En mi caso, no quiero eliminar ningún usuario, así que el DELETE es para desactivar cambiando el estado del usuario por false. Ahora, para poder usar este controlador que tiene las funciones de nuestro CRUD, hay que importarlo en nuestro fichero server.js con la siguiente línea de código:

    app.use(require('./routes/users'));

Además hay que importar la librería que hemos instalado antes, llamada body-parser, que nos permitirá acceder a los datos de una manera más sencilla:

    const bodyParser = require('body-parser');

De este modo, nuestro fichero server.js quedará así:

    const express = require('express');
    const mongoose = require('mongoose');
    const bodyParser = require('body-parser');

    // Parse application/x-www-form-urlencoded
    app.use(bodyParser.urlencoded({ extended: false }));
    // Parse application/json
    app.use(bodyParser.json());
    // Include controller of the entity
    app.use(require('./routes/users'));

    const run = async () => {
        await mongoose.connect(SERVER_BBDD, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            useCreateIndex: true,
        });
    };

    run().catch(error => console.error(error));

    app.listen(process.env.PORT, () => {
         console.log(`Escuchando en el puerto 3000`);
    });

Una vez tengamos nuestro servidor levantado, podemos hacer las peticiones para crear, editar, desactivar o listar los usuarios registrados en nuestra base de datos de mangodb. En mi caso, uso Postman y aquí puedes encontrar una breve documentación de los comandos que uso para ejecutar las apis que acabamos de hacer. Esto es lo básico funcional para tener un CRUD completo de usuarios.