Estructura de una API Rest con NodeJS, Express y MongoDB

En este artículo encontrarán algunas anotaciones y la forma en la que organizo la estructura de mi API Rest con nodejs, express y mongoDB.

Pasos que he realizado:

  1. Instalar MongoDB.
  2. Crear una base de datos con MongoDB.
  3. Instalar las dependencias (Fichero de configuración principal package.json).
  4. Hacer la conexión a MongoDB desde NodeJS con Mongoose. (en el index.js).
  5. Crear el servidor web con NodeJS. API RESTful en Node.js (en el app.js).
  6. Cargar el fichero app.js con la configuración de Express (en el index.js).
  7. Crear los esquemas y modelos.
  8. Crear un controlador.
  9. Crear la ruta para el controlador.
  10. Importar y cargar las rutas en el servidor (app.js).
  11. Testear mi api con Potman.

Algunos conceptos y dependencias básicas

NodeJS: Es una plataforma o entorno de ejecucion para desarrollar con javascript del lado del servidor.

ExpressJS: Es un framework sobre nodejs que nos permite trabajar con el protocolo http y tener sistemas de rutas.

MongoDB: Es una base de datos NoSQL que nos permite trabajar con documentos json binarios (bson), en lugar de utilizar el sistema clasico de tablas y relaciones, lo que hace que se muy veloz. Tiene colecciones de documentos y estos documentos son objetos json (bson) y los datos se almacenan de manera binaria para aumentar le rendimiento.

Nodemon: Utilidad para monitorear y reiniciar el servidor automaticamente ante cualquier cambio.

body-parser: Nos permite convertir los datos que nos llegan en las peticiones al servidor en objetos JSON.

Bcrypt-nodejs: Librería para encriptar contraseñas con el metodo bcrypt.

Connect-multiparty: Librería para subir ficheros.

Mongoose: Es un modulo y una especie de ORM que nos provee metodos y funcionalidades para trabajar mejor con mongoDB.

Jwt-simple: Librería para la gestión de tokens (nos permite realizar autenticación y cifrado de tokens).

Moment: Librería para trabajar con fechar.

Moongose-pagination: Librería para hacer paginados.

Middlewares: Es un bloque de código que se ejecuta entre la petición que hace el usuario (request) hasta que la petición llega al servidor.

Modelos: Representan una entidad de nuestra base de datos, más concretamente un único registro o documento. Son una abstracción que vamos a utilizar para realizar operaciones en la base de datos. (Están definidos por un esquema).

Esquemas: Son la estructura de una colección, con estructura me refiero a los atributos de cada colección que se vaya a crear.

Estructura del proyecto

La estructura inicial del proyecto contiene en la raíz un archivo package.json donde vamos a especificar la información de nuestro paquete con sus dependencias, un archivo index.js para la conexión a la base de datos y configuración general de mongoose, un app.js para crear el servidor web con NodeJS y la configuración de express, un directorio para nuestros middlewares, un directorio de models para crear los modelos y esquemas, un directorio de controllers para crear las acciones y operaciones sobre nuestra base de datos, y finalmente, un directorio de rutas el que definimos las rutas a las que responderá nuestra aplicación.

package.json

Es el archivo de configuración principal del proyecto y debe encontrarse en la raíz del mismo. En el debe estar reflejado el nombre del proyecto, versión, descripción, scripts, autor, tipo de licencia y algo muy importante las dependencias.

{
"name": "mi-api-rest",
"version": "1.0.0",
"description": "Proyecto api rest ejemplo",
"main": "index.js",
"scripts": {
"start": "nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "William Bastidas",
"license": "MIT",
"dependencies": {
"bcrypt-nodejs": "0.0.3",
"body-parser": "^1.18.2",
"connect-multiparty": "^2.1.0",
"express": "^4.16.2",
"jwt-simple": "^0.5.1",
"moment": "^2.20.1",
"mongoose": "^5.0.1",
"mongoose-pagination": "^1.0.0"
},
"devDependencies": {
"nodemon": "^1.14.12"
}
}

index.js

Es el archivo para conectar NodeJS con MongoDB (en el hacemos la conexión a la base de datos y configuración general de mongoose)

// Utilizar funcionalidades del Ecmascript 6
'use strict'
// Cargamos el módulo de mongoose para poder conectarnos a MongoDB
var mongoose = require('mongoose');
// *Cargamos el fichero app.js con la configuración de Express
var app = require('./app');
// Creamos la variable PORT para indicar el puerto en el que va a funcionar el servidor
var port = 3800;
// Le indicamos a Mongoose que haremos la conexión con Promesas
mongoose.Promise = global.Promise;
// Usamos el método connect para conectarnos a nuestra base de datos
mongoose.connect('mongodb://localhost:27017/curso_mean_social', { useMongoClient: true})
.then(() => {
        // Cuando se realiza la conexión, lanzamos este mensaje por consola
console.log("La conexión a la base de datos curso_mean_social se ha realizado correctamente")

// CREAR EL SERVIDOR WEB CON NODEJS
app.listen(port, () => {
console.log("servidor corriendo en http://localhost:3800");
});
})
// Si no se conecta correctamente escupimos el error
.catch(err => console.log(err));

app.js

Es el archivo para crear el servidor web con NodeJS, contiene la configuración de express. En el también importamos y cargamos las rutas después de crearlas.

// Utilizar funcionalidades del Ecmascript 6
'use strict'
// Cargamos los módulos de express y body-parser
var express = require('express');
var bodyParser = require('body-parser');
// Llamamos a express para poder crear el servidor
var app = express();
// Importamos las rutas
var user_routes = require('./routes/user');
//cargar middlewares
//un metodo que se ejecuta antes que llegue a un controlador
//Configuramos bodyParser para que convierta el body de nuestras peticiones a JSON
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
// Cargamos las rutas
app.use('/api', user_routes);
// exportamos este módulo para poder usar la variable app fuera de este archivo
module.exports = app;

models/user.js

archivo para crear los esquema y modelo User.

'use strict'
// Cargamos el módulo de mongoose
var mongoose = require('mongoose');
// Usaremos los esquemas
var Schema = mongoose.Schema;
// Creamos el objeto del esquema y sus atributos
var UserSchema = Schema({
name: String,
surname: String,
nick: String,
email: String,
password: String,
role: String,
image: String
});
// Exportamos el modelo para usarlo en otros ficheros
module.exports = mongoose.model('User', UserSchema);

middlewares/authenticated.js

Archivo con el middleware para validar la autenticacion del usuario.

'user strict'
var jwt = require('jwt-simple');
var moment = require('moment');
var secret = 'clave_secreta_';
exports.ensureAuth = function(req, res, next){
if(!req.headers.authorization){
return res.status(403).send({message: 'La peticion no tiene la cabecera de autenticación'});
} else {
var token = req.headers.authorization.replace(/['"]+/g, '');
try{
var payload = jwt.decode(token, secret);
if(payload.exp > moment().unix()){
return res.status(401).send({
message: 'EL token ha expirado'
});
}
} catch (ex){
return res.status(404).send({
message: 'EL token no es valido'
});
}
req.user = payload;
next();
}
}

controller/user.js

Archivo controlador de ejemplo. En el programamos las acciones y operaciones sobre nuestra base de datos, en este caso para obtener los datos de un usuario.

'use strict'
// Cargamos los modelos para usarlos posteriormente
var User = require('../models/user');

// Conseguir datos de un usuario
function getUser(req, res){
var userId = req.params.id;
//buscar un documento por un  id
User.findById(userId, (err, user) => {
if(err)return res.status(500).send({message: 'Error en la petición'});
if(!user) return res.status(404).send({message: 'EL usuario no existe'});
followThisUser(req.user.sub, userId).then((value) => {
user.password = undefined;
return res.status(200).send({
user,
following: value.following,
followed: value.followed
});
});

});
}

routes/user.js

Archivo en el que definimos las rutas (path) a las que responderá nuestra aplicación y en ellas se encontrará la lógica a ejecutar.

'use strict'
// Cargamos el módulo de express para poder crear rutas
var express = require('express');
// Cargamos el controlador
var UserController = require('../controllers/user');
// Llamamos al router
var api = express.Router();
var md_auth = require('../middlewares/authenticated');
// Creamos una ruta para los métodos que tenemos en nuestros controladores
api.get('/user/:id', md_auth.ensureAuth, UserController.getUser);
// Exportamos la configuración
module.exports = api;