2

Creacion API con NodeJs, mongoose y express

<h1>Creacion de una API desde 0 usando NodeJs</h1>

The Basics

Introduccion

En este tutorial vamos a crear una API REST con un CRUD(Create, Read, Update, Delete) en el cual vamos a poder dentro de una base de datos hacer las siguientes acciones:

  • Crear productos
  • Listar productos
  • Buscar productos por parametros
  • Actualizar los productos
  • Eliminar productos

Para el desarrollo de este ejercicio es recomendable usar una terminal linux/unix ya que algunos comandos que se usaran para la elaboracion de la API no son compatibles con windows.

Crear Archivo base

Vamos a crear un folder llamado api con el comando mkdir api, nos movemos a este folder con el comando cd <{path}/api>en este folder vamos a inicializar nuestro proyecto con el comando npm init -y (suponiendo que en tu computadora ya tienes instalado node y npm, de no ser asi, instalalos y sigue los pasos), cuando termine de hacer la creacion del proyecto, veremos que en nuestro folder va a aparecer un archivo llamado package.json, en este archivo esta toda la configuracion de nuestro proyecto, tendras algo asi :

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
}

Vamos a instalar las dependencias.

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "mongoose": "^5.9.22"
  },
  "devDependencies": {
    "nodemon": "^2.0.4"
  }
}

Package.json: Scripts

Ya con todo instalado vamos a empezar, para esto es recomendable modificar los scripts del archivo package, por lo tanto en donde dice “scripts” agregaremos la siguiente linea "dev": "nodemon server.js"

{
  "name": "api",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "nodemon server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "body-parser": "^1.19.0",
    "express": "^4.17.1",
    "mongoose": "^5.9.22"
  },
  "devDependencies": {
    "nodemon": "^2.0.4"
  }
}

En mi caso borre el script que viene por defecto y deje el que les mencione anteriormente, porque en realidad no uso ese script, lo que hace este script es inicializar nodemon usando como base el archivo server.js. Para poder ejecutar el script crearemos primero en nuestro folder api el archico server.js con el comando touch server.js en la terminal. despues de crear el archivo, en la terminal escribe npm run dev y obtendras lo siguiente:
Imgur

Estructura

Para la estructura de este servicio web vamos a crear un folder llamado app, en este folder vamos a tener toda nuestra logica, server.js se ocupara unica y exclusivamente de lanzar el servidor.

El arbol de la logica te debe de quedar de esta manera:

  • app
    • config
      • config.js
      • database.js
    • controllers
      • ProductController.js
    • models
      • Product.js
    • routes
      • product.js
    • app.js

Ten en cuenta el unico que no todos son folders los nombres que tienen un “.*” son archivos, app.js es un archivo que va a contener toda nuestra aplicacion del servidor, que mas adelante lo vamos a lanzar en server.js.

<h3>app.js</h3>
const express = require('express');
const bodyParser = require('body-parser');

const app = express();

//Nos permite manejar peticiones y enviar respuesta en formato json
app.use(bodyParser.json());
//De esta manera indicamos que no vamos a recibir peticiones enviadas directamente de un formulario, sino que sera todo enviado en json
app.use(bodyParser.urlencoded({extended: false}))

module.exports = app

De esta manera ya tenemos configurado nuestro servidor.

<h3>config/config.js</h3>
module.exports = {
    PORT: process.env.PORT || 3000,
    DB: process.env.DB || 'mongodb://localhost:27017/api-example'
}

Este archivo config.js tiene dentro un objeto que vamos a exportar y tiene como atributos variables de configuracion.

<h3>config/database.js</h3>
const mongoose = require('mongoose');
const CONFIG = require('./config');

module.exports = {
    connection: null,
    connect: () => {
        if (this.connection) returnthis.connection;
        return mongoose.connect(CONFIG.DB, {useUnifiedTopology: true,useNewUrlParser: true}).then(connection => {
            this.connection = connection;
            console.log('Conexion a DB exitosa');
        }).catch(err => console.log(err))
    }
}

En este archivo database.js tenemos toda la configuracion para que se conecte a la base de datos mongo.

<h3>server.js</h3>
const Database = require('./app/config/database');
const CONFIG = require('./app/config/config');
const app = require('./app/app');

Database.connect();

app.listen(CONFIG.PORT, err => {
    if (err) returnconsole.log(err)
    console.log(`Servidor corriendo en el puerto: ${CONFIG.PORT}`);
})

En este archivo vamos a inicializar nuestro servidor, por esto instanciamos el archivo database.js, config,js y app.js y las guardamos en las constantes Database, CONFIG, app, respectivamente. Ejecutamos entonces, con todo ya listo, el comando npm run dev y el resultado es el siguiente:

Imgur

Si tienes problema de conexion con la base de datos debes de iniciar la base de datos con el siguiente comando: sudo mongod esto te deberia de solucionar el problema de conexion y permitite ejecutar el servidor sin problema.

<h3>models/Product.js</h3>

En este archivo vamos a crear toda la logica de nuestra base de datos en mongoDB.

const mongoose = require('mongoose');

const ProductSchema = new mongoose.Schema({
    name: {
        type: String,
        unique: true,
        required: true
    },
    price: {
        type: Number,
        required: true
    },
    category: {
        type: String,
        required: true,
        enum:['Hogar', 'Cocina', 'Higiene']
    },
    stock: {
        type: Number,
        default: 10
    },
    date: {
        type: Date,
        default: Date.now()
    }
});

const Product = mongoose.model('Product', ProductSchema);

module.exports = Product;

Como puedes ver tenemos un objeto dentro de nuestro objeto, esto es porque necesitamos que los atributos principales, tambien tengan atributos, como por ejemplo que tiempo de dato va a ser, o si es requerido o no, tambien si necesitamos que sea unico o no… Hay mas opciones pero por el momento solo vamos a trabajar con las siguientes.

<h3>controllers/ProductController.js</h3>

En este archivo vamos a crear toda la logica de las funciones para nuestras rutas

const Product = require('../models/Products');

functionlistall(req, res) {
    Product.find({})
        .then(products => {
            if(products.length) return res.status(200).send({products})
            return res.status(204).send({message: 'NO CONTENT'});
        }).catch(err => res.status(500).send({err}))
}

functioncreate(req, res) {
    let product = new Product(req.body);
    product.save()
        .then(product => 
            res.status(201).send({product})
        ).catch(err => res.status(500).send({err}))
    
}

functionshow(req, res) {
    if(req.body.error) return res.status(500).send({error});
    if(!req.body.products) return res.status(404).send({message: 'Not Found'});
    let products = req.body.products;
    return res.status(200).send({products});
}

functionupdate(req, res) {
    if(req.body.error) return res.status(500).send({error});
    if(!req.body.products) return res.status(404).send({message: 'Not Found'});
    let product = req.body.products[0];
    product = Object.assign(product, req.body);
    product.save()
        .then(product => res.status(200).send({message: 'Product Updated', product})
    ).catch(err => res.status(500).send({err}))
}

functiondeleted(req, res) {
    if(req.body.error) return res.status(500).send({error});
    if(!req.body.products) return res.status(404).send({message: 'Not Found'});
    req.body.products[0].remove()
        .then(product => {
            res.status(200).send({message:'Product removed', product})
        }
        ).catch(err => res.status(500).send({err}));
}

functionfind(req, res, next){
    let query = {};
    query[req.params.key] = req.params.value
    Product.find(query).then(products => {
        if(!products.length) return next();
        req.body.products = products;
        return next();
    }).catch(err =>{
        req.body.error = err;
        next();
    })
}

module.exports = {
    listall,
    show,
    create,
    update,
    deleted,
    find,
}

Como puedes ver tenemos aqui nuestro CRUD, donde podremos listar todos los productos con la funcion listall, tambien vamos a poder ver un solo producto con la funcion show, con la funcion create, agregaremos un producto a nuestra BD, la funcion update actualiza nuestro producto en base de datos, deleted elimina un producto y tenemos un middleware llamado find, este se encarga de hacer una revision de existencia del producto antes de llegar a las funciones de show, update o deleted.

<h3>routes/product.js</h3>

En este archivo vamos a crear toda la logica del router.

const express = require('express');
const ProductController = require('../controllers/ProductController');

const router = express.Router();

router.get('/', ProductController.listall)
      .post('/', ProductController.create)
      .get('/:key/:value', ProductController.find, ProductController.show)
      .put('/:key/:value', ProductController.find, ProductController.update)
      .delete('/:key/:value', ProductController.find, ProductController.deleted)

module.exports = router;

Como puedes ver tenemos un archivo muy limpio el cual lo que hace es direccionar segun el metodo que nos llegue.

<h3>Modificacion app.js</h3>

Finalmente y para que el servidor funcione con toda nuestra logica vamos a modificar nuestro archivo app para incluir lo que acabamos de construir

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

const app = express();

const Product = require('./routes/product');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));

app.use('/product', Product);

module.exports = app

Con esta modificacion final ya podemos realizar la pruebas en nuestro servidor para ello entonces ejecutamos npm run dev

Resumen

  • Creamos un folder llamado api con el comando mkdir <folder_name> , desde la terminal
  • Ingresamos al folder creado con el comando cd <{path}/folder_name>.
  • Iniciamos el proyecto en nodeJs con el comando npm init -y
  • Instalamos las dependencias para trabajar
    • npm i express body-parser mongoose
    • npm i -D nodemon
  • Agregamos "dev": "nodemon server.js" en la seccion scripts de nuestro archivo package.json
  • Creamos el archivo server.js en nuestro folder usando en terminal touch server.js
  • Creamos la logica en carpetas para llegar al modelo MVC
  • Creamos los archivos js dentro del modelo MVC
  • Modificamos nuestro archivo principal app.js para ejecutaren modo desarrollo nuestra API con CRUD
Escribe tu comentario
+ 2