# Node js Express con MongoDB

0

WARNING

# 1. Creación del proyecto

  • Cree una carpeta donde guste, esta contendrá el proyecto, por ejemplo:
Creació de la carpeta
  • Dentro de la carpeta abriremos el cmd, y nos debe salir la siguiente ruta, debemos darnos cuenta que esté el nombre de la carpeta creada en el punto anterior
Creació de la carpeta
  • Escribiremos el siguiente comando mkdir seguido del nombre de la carpeta que tendrá el proyecto, por ejemplo:
mkdir proyecto
Creació de la carpeta
  • Luego entraremos a la carpeta con cd seguido del nombre que se le dió en el punto anterior
cd proyecto
Creació de la carpeta
  • Estando dentro de nuestro proyecto, toca crear el archivo package.json, pero no te preocupes, esto se lo crea con el siguiente comando:
npm init
Creació de la carpeta
  • Luego nos solicitará algunos datos para configurar el proyecto, puedes darle enter omitiendo o puedes ingresar, como sea tu elección.
Creació de la carpeta

Con esto ya tenemos creado el proyecto, para abrirlo basta escribir code . para que se nos abra Visual Studio Code

Creació de la carpeta

Veremos únicamente el archivo json creado en puntos anteriores

Creació de la carpeta

# 2. Instalación de dependencias

  • La primera dependencia que instalaremos será express, y para ello podemos dijitar el siguiente comando en el cmd si aún no la has cerrado o abriendo el terminal en el Visual Studio Code
npm install express --save

cmd

Creació de la carpeta

Visual Studio Code

Creació de la carpeta

instalación

Creació de la carpeta

  • Luego de la instalación veremos que nos aparece otro archivo json este tendrá las dependencias y la carpeta node_modules tendrá las carpetas de dichas dependencias
Creació de la carpeta
Creació de la carpeta

Y puedes observar en package.json la nueva dependencia

npm install nodemon
Creació de la carpeta

Luego, debemos hacer un pequeño cambio al package.json agregando al scripts lo siguiente:

"start": "nodemon app.js"

Y ese será el archivo que se reiniciará con cada cambio.

Creació de la carpeta
npm install body-parser
Creació de la carpeta

# 3. Ejecución del proyecto

  • Primero crearemos un archivo app.js debe tener nombre dado cuando se instaló nodemon.
  • Luego dentro del archivo escribieremos lo siguiente
// importamos express
const express = require('express');
// llamanos al función
const app = express();
// escuchamos el puerto 3000 y mostramos un mensaje por consola
app.listen(3000, () =>{
    console.log('Conectado');
});

Y para ejecutar, en el terminal escribiremos

npm start

Y, listo 🤓 hemos ejecutado nuestro proyecto pero aún falta para hacerlo api-rest 🥴

Creació de la carpeta

# 4. Estructura del proyecto

La estructura será la siguiente

  • PROYECTO

    • node_modules
    • src
      • app
      • config
      • controller
      • repository
      • service
      • exception
      • collection
      • route
    • app.js
    • package-lock.json
    • package.json
  • Dentro de la carpeta config crearemos un archivo server.js que contrendrá las credenciales del servidor y el puerto que escuchará nuestra aplicación.

/**
 * BD: mongodb 
 * user: aprendiendo
 * pass: 123456
 * server: localhost
 * port: 27017
 * bd: pruebas
 */
module.exports = {
    port : process.env.PORT || 3000, //puerto que escuchará nuestra aplicación
    mongodb : process.env.MONGODB || 'mongodb://aprendiendo:123456@localhost:27017/proyecto'
}
  • Dentro de la carpeta exception creamos un archivo exception.js que contrendrá nuestras excepciones personalizadas, y estas son algunas, podemos tener más dependiendo la necesidad del proyecto.
// excepción al crear una colección
const create = (res, payload) => {
    message(res, 201, payload, false);    
}
// excepción al enviar una petición errónea
const badRequest = (res, payload) => {
    message(res, 400, payload, true);
}
// excepción interna del proyecto 
const internalError = (res, payload) => {
    message(res, 500, payload, true);
}
// mensaje a devolver
function message(res, code, payload, flag){
    res.status(code).json({"error": flag, "payload": payload??null});
}
// exportamos las excepciones para poder importarlas en otros archivos
module.exports = {
    create,
    badRequest,
    internalError
}
  • Dentro de la carpeta collection crearemos nuestros modelos o esquemas de nuestra base, por esta práctica crearemos un sólo esquema persona y el archivo será personCollection.js.
// importamos mongoose para poder hacer uso del schema
const mongoose = require('mongoose');
// creamos nuestro esquema
const Person = mongoose.Schema({
    name: String,
    city: String
})
// creamos una constante de nuestro modelo para poderlo exportar
const Person = mongoose.model('Person', collectionPerson, 'Person');
// exportamos nuestro esquema
module.exports = Person;
  • Dentro de la carpeta repository crearemos el archivo personRepository.js y aquí haremos todo las setencias a nuestra base, es decir, es aquí donde guardaremos, buscaremos, actualizaremos y eliminaremos nuestros documentos.
// importamos nuestro esquema
const Person = require('../collection/personCollection.js');
// el método es async porque devuelve una promesa
const guardar = async (req) => {
    const p =  new Person(req.body);    
    return await p.save();
}

const buscar = async() =>{
    return await Person.find();
}
const actualizar = async(req) =>{
    return await Person.findByIdAndUpdate(
        {
            _id: req.params.id
        },
        {
            $set:
            {
                name : req.body.name,
                city : req.body.city
            }
        }
    );
}
const eliminar = async(req) =>{
    return await Person.findByIdAndDelete(
        {
            _id: req.params.id
        }
    );
}
// exporamos nuestro método para poder importarlo en nuestro servicio
module.exports = {
    guardar,
    buscar,
    actualizar,
    eliminar
}
  • Dentro de la carpeta service crearemos un archivo personService.js es la parte media entre el controllador y repositorio, y es aquí donde se valida los datos, lanza excepciones y demás.
// importamos nuestro repositorio
const repository = require('../repository/personRepository.js');
// importamos nuestras excepciones
const exception = require('../exception/exception.js');
// esta función recibe el requerimiento (body) y respuesta (http)
function guardar(req, res){  
    try {                
        if(req.body.name == "" || req.body.name == null || req.body.city == "" || req.body.city == null)
            // exception si los datos no son los esperados
           exception.badRequest(res, "Ingrese los datos");            
        else{
            repository.guardar(req)
            .then(doc => {
                // exception de creación
                exception.success(res, "Datos ingresados");
            })
            .catch(err => {
                // exception si hubo algo inesperado con la solicitud
                exception.badRequest(res, err);
            });
        }            
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}
// esta función recibe el requerimiento (body) y respuesta (http)
function buscar(res){       
    try {                       
        repository.buscar()
        .then(doc => {
            // exception de creación            
            if(doc.length > 0)
                exception.found(res, doc);
            else
                exception.notFound(res, "No hay documentos que mostrar");
        })
        .catch(err => {
            // exception si hubo algo inesperado con la solicitud
            exception.badRequest(res, err);
        });                 
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}
// esta función recibe el requerimiento (body) y respuesta (http)
function actualizar(req, res){       
    try {   
        if(req.body.name == "" || req.body.name == null || req.body.city == "" || req.body.city == null)
            // exception si los datos no son los esperados
           exception.badRequest(res, "Ingrese los datos");            
        else{                                
            repository.actualizar(req)
            .then(doc => {
                // exception de actualización  
                if(doc != null)
                    exception.success(res, "Documento actualizado");
                else
                    exception.notFound(res, "No existe el documento");               
            })
            .catch(err => {
                // exception si hubo algo inesperado con la solicitud
                exception.badRequest(res, "El id tiene un formato erróneo");
            }); 
        }                
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}
// esta función recibe el requerimiento (body) y respuesta (http)
function eliminar(req, res){       
    try {   
        if(req.params.id == "" || req.params.id == null )
            // exception si los datos no son los esperados
           exception.badRequest(res, "Ingrese los datos");            
        else{                                
            repository.eliminar(req)
            .then(doc => {
                // exception de creación      
                if(doc != null)
                    exception.success(res, "Documento eliminado");
                else
                    exception.notFound(res, "No existe el documento");
                
            })
            .catch(err => {
                // exception si hubo algo inesperado con la solicitud
                exception.badRequest(res, "El id tiene un formato erróneo");
            }); 
        }                
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}


// exportamos el método para poderlo importar en el controllador
module.exports = {
    guardar,
    buscar,
    actualizar,
    eliminar
}
  • Dentro de la carpeta controller crearemos un archivo personController.js, aquí llamaremos a todos los métodos de nuestro servicio persona.
// importamos nuestro servicio
const service = require('../service/personService.js');
// importamos nuestras excepciones
const exception = require('../exception/exception.js');
// método insertar
function insert(req, res){    
    try {     
        // llamamos a nuestro método
        service.guardar(req, res);        
    } catch (error) {
        // exception inesperada
        exception.internalError(res, "Problema inesperado");
    }
}

function select(req, res){
    try {     
        // llamamos a nuestro método
        service.buscar(res);        
    } catch (error) {
        // exception inesperada
        exception.internalError(res, "Problema inesperado");
    }
}

function update(req, res){
    try {     
        // llamamos a nuestro método
        service.actualizar(req, res);        
    } catch (error) {
        // exception inesperada
        exception.internalError(res, "Problema inesperado");
    }
}

function borrar(req, res){
    try {     
        // llamamos a nuestro método
        service.eliminar(req, res);        
    } catch (error) {
        // exception inesperada
        exception.internalError(res, "Problema inesperado");
    }
}

module.exports = {
    insert,
    select,
    update,
    borrar
}
  • Dentro de la carpeta route crearemos un archivo personRoute.js que contrendrá las rutas de nuestros endpoints de persona.
// importamos express
const express = require('express');
// hacemos uso de la función Router()
const api = express.Router();
// importamos nuestro controlador
const personCroller = require('../controller/personController.js');
// hacemos uso de los métodos http
api.post('/add',personCroller.insert);
api.get('/select', personCroller.select);
api.put('/update/:id/' , personCroller.update);
api.delete('/delete/:id',personCroller.borrar);
// exportamos api
module.exports = api;
  • Dentro de la carpeta app crearemos un archivo app.js que contrendrá o hará de almacenar todos los accesos a las rutas de nuestra api-rest
// importamos express
const express = require('express');
// importamos body-parser
const bodyParser = require('body-parser');
// llamanos al función
const app = express();
// importamos nuestro archivo que contiene las rutas o endpoints de persona
const api = require('../route/personRoute.js');

// hacemos uso de body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

// hacemos uso de nuestra ruta de persona
app.use('/api', api);
/* si tuvieramos más rutas de otras collections haríamos todo como se hiso con persona
 * y aquí haríamos udo de la ruta por ejemplo
 * app.use('/api', api2);
 */
// exportamos app para llamarlo en nuestro archivo app.js que está en la raíz de nuestro proyecto
module.exports = app;

Teniendo lista la estructura con sus archivos correspondientes, ahora toca modificar el archivo app.js que está en la raíz del proyecto para acceder a la base y modificar el escuchar nuestra aplicación.

// importamos mongoose para la base
const mongoose = require('mongoose');
// importamos app
const personApp = require('./src/app/app.js');
// importamos la configuración del servidor
const server = require('./src/config/server.js');
// nos conectamos al la base
mongoose.connect(server.mongodb);
//creamos una constante de la connection
const connection = mongoose.connection;
// mostramos un console si ha fue exitosa
connection.once('open', () => {
    console.log('MongoDb success');
});
// mostramos un console si ha fue errónea
connection.on('error', (err) => {
    console.log('Echale agua para que encienda', err.message);
});
// escuchamos el puerto
personApp.listen(server.port, () =>{
    console.log('Conectado');
});

Proyecto terminado

Creació de la carpeta

Dame click para ver la ejecución
Dame click para descargar el proyecto (opens new window)

# 5. Encripta la contraseña de tu usuario

Para esta práctica vale tener un schema que haga referencia a usuarios, sin embargo por motivo de práctica, aumentaremos un campo pass al schema person y quedaría de la siguiente forma:

// importamos mongoose para poder hacer uso del schema
const mongoose = require('mongoose');
// creamos nuestro esquema
const collectionPerson = mongoose.Schema({
    name: String,
    city: String,
    pass: String
})
// creamos una constante de nuestro modelo para poderlo exportar
const Person = mongoose.model('Person', collectionPerson);
// exportamos nuestro esquema
module.exports = Person;

De igual manera debemos validar que al ingresar una documento, este campo esté lleno y el cambio quedaría de la siguiente forma:

function guardar(req, res){  
    try {                
        if(req.body.name == "" || req.body.name == null || 
           req.body.city == "" || req.body.city == null || 
           req.body.pass == "" || req.body.pass == null)
            // exception si los datos no son los esperados
           exception.badRequest(res, "Ingrese los datos");            
        else{
            repository.guardar(req)
            .then(doc => {
                // exception de creación
                exception.success(res, "Datos ingresados");
            })
            .catch(err => {
                // exception si hubo algo inesperado con la solicitud
                exception.badRequest(res, err);
            });
        }            
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}

Teniendo listo los cambios, ahora debemos elegir una dependencia que cumpla con la encriptación, hay varias, sos libre de elegir la que mejor te parezca, y bueno yo he instalado la siguiente cripto-js (opens new window)

npm install crypto-js

Ahora solo queda encriptar la contraseña, ¿Pero cómo 🤔?. Presta atención

// importamos 
const CryptoJS = require("crypto-js");
// nuestra contraseña
const pass = "123456";
// nuestra palabra clave de encriptación
const secretKey = "Aprendiendo";
// encriptar
const passCrypto = CryptoJS.AES.encrypt(pass, secretKey).toString();
// mostrar
console.log(passCrypto);//salida => U2FsdGVkX18jpRlY5yNjiuyBSdbjPo3EjzaGBsdiEa4=

Viste?, súper fácil, ahora solo basta acomodar eso en el servicio y listo.

TIP

Configura tu secretKey en config

// importamos nuestro repositorio
const repository = require('../repository/personRepository.js');
// importamos nuestras excepciones
const exception = require('../exception/exception.js');
// importamos config
const server = require('../config/server.js');
// importamos
const CryptoJS = require("crypto-js");

// esta función recibe el requerimiento (body) y respuesta (http)
function guardar(req, res){  
    try {                
        if(req.body.name == "" || req.body.name == null || 
           req.body.city == "" || req.body.city == null || 
           req.body.pass == "" || req.body.pass == null)
            // exception si los datos no son los esperados
           exception.badRequest(res, "Ingrese los datos");            
        else{
            req.body.pass =  CryptoJS.AES.encrypt(req.body.pass, server.secretKey).toString();
            repository.guardar(req)
            .then(doc => {
                // exception de creación
                onst token = {
                    "token": t.createToken(doc)
                }
                // enviamos el token
                exception.success(res, "Datos ingresados",token);
            })
            .catch(err => {
                // exception si hubo algo inesperado con la solicitud
                exception.badRequest(res, err);
            });
        }            
    } catch (error) {
        // exception interna del programa
        exception.internalError(res, "Problema inesperado");
    }
}

Dame click para ver la encriptación

# 7. Desencripta la contraseña de tu usuario

Esto super sencillo, basta con una línea, te daré la línea pero es tu deber saber donde lo colocas, te mostraré la ejecución con un console.log(); descriptando la contraseña del paso 6 que es Ecuado2021.

// consultamos de nuestra base la contraseña encriptada
const passCryto = "U2FsdGVkX1/rxCuneD4XFDTkILTnTa0xx0HfJUPtURQ="; //la puedes ver en la clave en Pruebas
// nuestra palabra clase de desencriptación
const secretKey = "Aprendiendo-con-Guillermo"; // esta clave está en mi config, recuerda hacer ese cambio
// desencriptar
const passOriginal = CryptoJS.AES.decrypt(passCryto, secretKey);
// mostrar
console.log(passOriginal.toString(CryptoJS.enc.Utf8));//salida => Ecuador2021

# 6. Dale seguridad a tu Api con JWT

Para darle seguridad a nuestra api usaremos jwt (opens new window) y se compone de tres partes:

  1. Header o cabecera
  2. Payload o carga útil
  3. Signature

Primero que tenemos que hacer es instalar la dependencia jwt-simple (opens new window), y dayjs (opens new window) de igual manera, puedes usar la que mejor te parezca

npm install jwt-simple
npm install dayjs

Ver documentación de dayjs (opens new window)

Segundo es determinar la clave que se usará para cifrar tu token, recuerda que mientras más extensa, más segura será.

/**
 * BD: mongodb 
 * user: aprendiendo
 * pass: 123456
 * server: localhost
 * port: 27017
 * bd: pruebas
 */
 module.exports = {
    port : process.env.PORT || 3000, //puerto que escuchará nuestra aplicación
    mongodb : process.env.MONGODB || 'mongodb://aprendiendo:123456@localhost:27017/proyecto',
    secretKey : process.env.SECRET_KEY || 'Aprendiendo-con-Guillermo',
    secretToken : process.env.SECRET_TOKEN || '@pr3nd13nd0c0nGu1ll3rm@'
}

Tercero será reestructurar nuestro proyecto de la siguiente forma:

Creació de la carpeta

Cuarto será crear y descodificar nuestro token, para esto nos iremos a tokenService.js y escribiremos los siguiente:

// importaciones
const jwt = require('jwt-simple');
const Day = require('dayjs');
const server = require('../../config/server.js');

// creación del token
const createToken = (doc) => {  
    // creamos el payload de nuestro token
    const payload = {
        // información del token
        sub: doc._id, // no se recomienda colocar el id, pero es un ejemplo
        // creacion token
        iat: Day().unix(),
        // expirar token
        p: Day().add(1, 'm').unix() // m -> minuto <- por ejemplo
    }
    /** Podemos usar
     *  HS256
     *  HS384
     *  HS512
     */
    return jwt.encode(payload, server.secretToken, 'HS256');
}

// decodificación del token
const decodeToken = (token) => {
    // creamos una promesa
    const decode = new Promise((res, err) => {
        try{            
            // decoficamos el token
            const payload = jwt.decode(token, server.secretToken, 'HS256');
            // verificamos si no ha expirado
            if(payload.exp <= Day().unix())
                err({
                    message: 'Su sesión ha expirado'
                })
            // devolvemos algo de ser necesario, para indicar que todo está correcto
            res(payload._id)                             
        }catch(e){
            // en caso de que el token no se pueda decodificar
            err({
                message: 'Tokén inválido'
            })
        }
    })
    return decode;
}

Quinto en el archivo authorization.js importaremos a nuestro servicio de token para determinar y dar acceso al recurso solicitado si este tiene permiso, caso contrario le mandamos una excepción (debes agregarla 😉)

// importamos
const exception = require('../exception/exception.js');
const tokenService = require('../service/token/tokenService.js');
// creamos el método que recibe el requerimiento, el result y el next para dar paso al recurso
const authorization = (req, res, next) => { 
    /** este if es para hacer público nuestro endpoint http://localhost:3000/api/add    
     *  puedes usar un arreglo, si deseas liberar más enpoint
     */ 
    if(req.path.replace ('/', '') == 'add')
        next(); // continua con el recurso solicitado
    else{
        // verificamos si la solicitud tiene un token
        if(!req.headers.authorization){
            // muestra una excepción
            exception.forbidden(res, "No tiene permiso");
        }else{              
            // obtenemos el token
            const token = req.headers.authorization;            
            // llamamos al método que decofica el token
            tokenService.decodeToken(token)
            .then(res => {        
                next() // continua con el recurso solicitado
            })
            .catch(err => {
                // muestra una excepción ya sea por token inválido o sesión expirada
                exception.unauthorized(res, err.message);
            })
        }
    }
}
// exportamos
module.exports = authorization;

Sexto debemos enviarle el token al cliente para que este lo almacene y nos lo envíe en cada solicitud, para esto debemos hacer un pequeño cambio en nuestro personService.js y quedaría de la siguiente manera.

// importamos nuestro token
const token = require('../services/token/token.js');
// dentro del then invocamos nuestro servicio
.then(doc => {
    // guardamos nuestro token
    const token = {
        "token": tokenService.createToken(doc)
    }
    // exception de creación y enviamos el token
    exception.success(res, "Datos ingresados",token);
})

Septimo, ahora solo queda implementar nuestro middleware, y ¿dónde lo implementamos 🤔?, puedes ya sea en personRoute.js o en app.js.

Pensemos, si colocamos en personRoute.js, tenemos que colocar nuestro middleware en cada endpoint, sin embargo, si lo colocamos en app.js basta con colocarlo despúes del nuestro de nuestra api, de todos modos ten las dos formas.

personRoute.js

// importamos express
const express = require('express');
// hacemos uso de la función Router()
const api = express.Router();
// importamos nuestro controlador
const personCroller = require('../controller/personController.js');
// importamos nuestro middleware
const authorization = require('../middleware/authorization.js');
// hacemos uso de los métodos http
api.post('/add', authorization, personCroller.insert);
api.get('/select', authorization, personCroller.select);
api.put('/update/:id/' , authorization,  personCroller.update);
api.delete('/delete/:id', authorization, personCroller.borrar);

// exportamos api
module.exports = api;

app.js

// importamos express
const express = require('express');
// importamos body-parser
const bodyParser = require('body-parser');
// llamanos al función
const app = express();
// importamos nuestro archivo que contiene las rutas o endpoints de persona
const api = require('../route/personRoute.js');
// importamos nuestro middleware
const authorization = require('../middleware/authorization.js');

// hacemos uso de body-parser
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));

// hacemos uso de nuestra ruta de persona
app.use('/api', authorization, api);
/* si tuvieramos más rutas de otras collections haríamos todo como se hiso con persona
 * y aquí haríamos udo de la ruta por ejemplo
 * app.use('/api', api2);
 */
// exportamos app para llamarlo en nuestro archivo app.js que está en la raíz de nuestro proyecto
module.exports = app;

Dame click para ver la ejecución implemtando app.js
Dame click para descargar el proyecto (opens new window)