MongoDB es un base de datos no-relacional, y es la más común (popular) a día de hoy en el ecosistema de Node.js.
Una vez creado tu fork y clonado el repo en tu computadora, haces npm install
para instalar las dependencias básicas.
Antes de poder comenzar a codear, tenemos que crear nuestro entorno de desarrollo.
Para ello te recomendamos seguir los siguientes pasos:
- 1. Instalar MongoDB y Compass
- 2. Levanta la base de datos y servidor
- 3. Elegir módulo (cliente)
- 4. Definir esquemas
- 5. Implementar los primeros TODOs
- 6. Definir estrategia de pruebas unitarias
- 7. Familiarizarte con las pruebas de integración (e2e)
Hay 3 formas diferentes obtener MongoDB: instalación local, usando docker y usando Atlas. La instalación local es la forma más rápida y recomendadata. El sitio de MongoDB tiene tutoriales de cómo instalar la Community Edition localmente en tu computadora. Elige tu plataforma (Windows, Mac, Ubuntu etc) y sigue el tutorial.
Con los tutoriales, vas a descargar y instalar MongoDB en tu sistema, con algunas herramientas y comandos para usar en la línea de comando.
También recomendamos que instales Compass, que es un GUI (Graphical User Interface) para interactuar con la base de datos. Puedes interactuar con MongoDB sin Compass y solo en la linea de comando, pero un GUI puede ayudarte visualizar y entender lo que está en la base de datos. Sigue las instrucciones de instalación de Compass.
Si ya estas haciendo la hacker edition de este proyecto y vas a trabajar con MongoDB y Docker, lee la guía de primeros pasos con Docker.
Podemos decir que este proyecto tiene dos "servicios", uno es la base de datos para almacenar los productos, usuarios, etc., y el otro es el servidor para exponer el API.
Estos servicios tienen que estar corriendo, para que el API pueda funcionar.
Correr el servidor es bien simple: con npm start
va a ejecutar
index.js
que tiene la lógica básica de un servidor con express.
Cómo levantar el servidor, este depende de tu instalación y sistema.
Por ejemplo, en MacOS si instalaste con homebrew
, puede usar
brew services start mongodb-community@6.0
para levantarlo. O sin homebrew
,
mongod --config /usr/local/etc/mongod.conf --fork
. En Windows, hay que
levantarlo desde Services console.
Revisa la guía de instalación de , guía de instalación de Windows, o tu instalación en particular, para ejemplos en cómo levantarlo.
Ahora que ya tenemos un servidor de base de datos, vamos a necesitar elegir un módulo o librería diseñado para interactuar con nuestra base de datos desde Node.js. Existen un montón de opciones, pero para este proyecto te recomendamos usar el Node.js driver de MongoDB que es la forma más directa de interactuar con tu instancia de MongoDB.
Hay que instalar el Node.js Driver en este proyecto usando npm
,
revisa la documentación oficial para más información.
El boilerplate ya incluye un archivo config.js
donde se leen las
variables de entorno, y entre ellas está DB_URL
. Como vemos ese valor lo
estamos asignando en la propiedad dbUrl
del módulo config
.
// `config.js`
exports.dbUrl = process.env.MONGO_URL || process.env.DB_URL || 'mongodb://127.0.0.1:27017/test';
Ahora que ya sabemos dónde encontrar el connection string (en el módulo config), podemos proceder a establecer una conexión con la base de datos usando el cliente elegido.
const { MongoClient } = require('mongodb');
const config = require("./config");
const client = new MongoClient(config.dbUrl);
async function connect() {
try {
await client.connect();
const db = client.db(<NOMBRE_DB>); // Reemplaza <NOMBRE_DB> por el nombre del db
return db;
} catch (error) {
//
}
}
Puedes encontrar mas ejemplos en la documentación de MongoDB.
Como parte del proceso de diseño de nuestra base de datos vamos a tener que especificar los esquemas de nuestros modelos de datos. Con esto nos referimos a que tenemos que describir de alguna forma la estructura de las colecciones que vamos a usar y la forma de los objetos que vayamos a guardar en esas colecciones.
Puedes usar la documentación de API
que describe la estructura de products
, orders
, etc. para guiar el diseño.
El boilerplate del proyecto viene con archivos con lógica para arrancar el server
y otros que contienen funciones de rutas y autenticación, pero muchas están vacías.
Hemos marcado las primeras partes esenciales con comentarios TODO
(del inglés por hacer), que es una convención en el desarrollo de software,
cuando queda algo pendiente de hacer.
Aquí te guiamos un poco sobre esto TODO's.
En el archivo connect.js
hay que hacer la conexión con la base de datos.
const { dbUrl } = config;
async function connect() {
// TODO: Database Connection
}
Aquí debes usar el dbUrl
que importamos del config para establecer la conexión.
Las funciones que van a interactuar con la base de datos tienen que invocar
connect
.
El proyecto depende en la existencia de un usuario en la base de datos que tiene privilegios de administrador, para así poder crear otros usuarios, etc.
En routes/users.js
invocamos una función initAdminUser(app, next)
al final de archivo, y definimos esta función arriba en este misma archivo.
initAdminUser
está incompleto, y hay un TODO para crear el usuario admin,
donde tienes que primero chequear si un admin ya existe, y si no agregar uno
a la base de datos.
const initAdminUser = (app, next) => {
const { adminEmail, adminPassword } = app.get('config');
if (!adminEmail || !adminPassword) {
return next();
}
const adminUser = {
email: adminEmail,
password: bcrypt.hashSync(adminPassword, 10),
roles: { admin: true },
};
// TODO: Create admin user
next();
};
Puedes confirmar si tu código funciona revisando la base de datos y con un testeo unitario.
En routes/auth.js
está la ruta '/login' definida, con un
// TODO: Authenticate the user
Aquí es donde debes verificar que el correo y password coinciden con datos de algún usuario en la base de datos. Si coinciden, hay que generar un JWT token y mandarlo en la respuesta, para que el usuario puede usarlo en sus peticiones futuras. Para ejemplos con mas detalle, busca tutoriales de autenticación con JWT y node/express.
En middleware/auth.js
hay varios TODOs
. Usa esta oportunidad para familiarizarte
con el concepto de middleware en express.
La aplicación va a usar este middleware para chequear que las peticiones que requieren autenticación sean autorizados, es decir, que tienen un token válido.
Para juntar y verificar todo el trabajo que has hecho, seria bueno implementar
una ruta básica del API, en este caso recomendamos /users
porque ya debes
tener el user admin en tu base de datos, y además porque esta ruta usa
el middleware de auth.
Vas a ver que la ruta /users
usa la función getUsers
que está definido en
controller/users.js
. El concepto de controller nos sirve para separar más
la lógica de la definición de rutas con la implementación que va a hablar con
el base de datos. Hay que implementar getUsers
para obtener la lista de
users de la colección en tu base de datos.
Revisa el tutorial de Node y express en Mozilla que habla de controllers.
Los tests e2e invocan la ruta POST /users
para agregar la usuaria para los tests.
Por lo tanto, antes de poder ejecutar los tests e2e, esta ruta debe funcionar
correctamente.
Además de las pruebas e2e
que ya incluye el boilerplate del proyecto, se
espera que puedas también usar pruebas unitarias para el desarrollo de los
diferentes endpoints o rutas así como otros módulos internos que decidas
desarrollar.
Para hacer pruebas unitarias de rutas de Express, te recomendamos explorar la
librería supertest
, que puedes usar
en combinación con jest
.
Otro punto a tener en cuenta en las pruebas unitarias, es cómo mockear la base
de datos. Algunas bases de datos ofrecen herramientas (como
mongodb-memory-server
) que
nos permiten usar una base de datos en memoria y así evitar hacer mocks per
se, pero por lo general querremos considerar cómo abstraer la interacción
con la base de datos para facilitar mocks que nos permitan concentrarnos en
la lógica de las rutas.
El boilerplate de este proyecto ya incluye pruebas e2e
(end-to-end) o de
integración, que se encargan de probar nuestra aplicación en conjunto,
a través de la interfaz HTTP. A diferencia de las pruebas unitarias, en vez
de importar o requerir un módulo y probar una función de forma aislada, lo que
vamos a hacer es arrancar toda la aplicación, y probarla tal como se usaría en
el mundo real, para ello la aplicación de prueba necesitará una base de datos y
escuchar en un puerto de red.
Para correr pruebas e2e sobre instancia local podemos usar:
npm run test:e2e
Esto levanta la aplicación con npm start
y corre los tests contra la URL de
esta instancia (por defecto http://127.0.0.1:8080
).
Los pruebas e2e dependen del código de (globalSetup.js
)[../e2e/globalSetup.js]
que jest
ejecuta en primer lugar, antes de los tests.
Este paso de setup levanta un mock db (como hemos hablado de
mongodb-memory-server
)
y jest se conecta a este mock db.
Para este configuración usamos mongodb-memory-server
y
un preset jest-mongodb
que ya están incluido en el boilerplate.