-
Notifications
You must be signed in to change notification settings - Fork 0
Node App | [7] Middlewares
- Autores: Jorge Becerra de la Torre (jabecerra@uc.cl) - Ian Basly (igbasly@uc.cl)
- Video: Enlace a la capsula
El desarrollo de este material sigue la línea de desarrollo de las cápsulas anteriores App Node [4]. El material en que se basa se encuentra disponible en este repositorio, específicamente en la ruta material/carrito+mws
.
Para una correcta explicación del contenido visto y aplicado en esta cápsula, se hicieron las siguientes modificaciones al material base.
Primero es necesario definir las variables de entorno
DB_USERNAME=postgres
DB_PASSWORD=password
DB_NAME=carrito
DB_HOST=localhost
Luego se debe crear la base de datos para alojar el proyecto. Para esto es necesario correr los siguientes comandos:
npm install
npx sequelize db:create
Para correr el proyecto por primera vez es necesario ejecutar las migraciones:
npx sequelize db:migrate
npx sequelize db:seed:all
Luego, para levantar la aplicación se debe ejecutar:
npm start
Para el correcto funcionamiento del proyecto se instalaron las siguientes dependencias:
dotenv // leer variables de entorno
bcrypt // hashear contraseñas
jsonwebtoken // identificador de usuario único a ser usado en cada request
Directorio:
/migrations
Se agregaron migraciones para incorporar password
y token
para el customer
. Además se agregó un stock
para los products
. Finalmente se corrigió la asociación entre órdenes y productos, ya que un producto puede estar presente en varias órdenes y una orden se compone de varios productos (relación N:M). Para ello se agregó la tabla intermedia products_order
la cual tiene como columnas productId
y orderId
.
Directorio:
/models
Se agregaron los atributos extras mencionados en el apartado anterior a customer
y orders
. Además de la asociación entre orders
y products
a través de la tabla products_order
.
Directorio:
/seeders
Se actualizaron las seeds de customers
y products
en base a lo mencionado anteriormente.
Directorio:
/middlewares
Recordar que los middlewares son programas que nos ayudan a separar las reponsabilidades de los controladores (routes) y evitar repetir código de manera excesiva. Estos programas actuan de forma intermediaria al flujo normal, donde una consulta pasa primero por un middleware, desde donde puede ser pasar por varios hasta llegar al controlador mismo. El mejor ejemplo (y visto en la cápsula) es la autentificación de un usuario antes de continuar con la función de una ruta en cuestión.
Se agregaron 3 archivos de middlewares, para auth
, customer
y orders
. A continuación se detalla la finalidad de cada uno.
Directorio:
/middlewares/auth.js
Este middleware se encarga de revisar que exista un token en el Header de la request. En caso de existir el token, busca al usuario con ese token y lo agrega al ctx
, de modo que ya no es necesario volver a consultar a la base de datos por el currentCustomer
. En caso de que no exista el token o que no haya un cliente con ese token asociado, se retorna un mensaje de error.
Directorio:
/middlewares/customers.js
Dentro de este archivo se encuentra un tipo especial de middleware llamado customerGuard
que se encarga de proteger un recurso (se aplica a las rutas de /customers
que requieren un :id
). La idea es proteger el recurso y solo si el currentCustomer
tiene el mismo id
que el customer
a ver o modificar se permita la acción.
Directorio:
/middleware/orders.js
Dentro de este directorio, podemos encontrar dos middlewares, los cuales ayudan a procesar las solicitudes de órdenes.
-
orderGuard
Es el primer middleware, el cual se encarga de validar que la:id
ingresada en la ruta corresponda a una orden válida y a su vez que esta orden sea del usuario que está realizando la consulta. En caso de que lo anterior se cumpla, entonces la orden a la cual se está ingresando quedará referenciada en la variablecurrentOrder
del contexto (ctx
). En caso contrario se retornará el mensaje de error correspondiente. -
validateStock
Por otro lado este middleware se encarga de validar que aún exista stock suficiente para agregar el producto a la orden, en caso de que alguno de los productos no cuenten con stock, se responderá con el error indicando que productos no están disponibles. (Ver sección Endpoint).
Para la relizar la gran mayoría de las consultas es necesario haber iniciado sesión, a lo cual se recibirá un token que debe ser utilizado para cada consulta que requiera autentificación.
-
Log In POST
/auth/login
{ "email": "algun@email.com", "password": "constraseña" }
Responses:
//Good request { "msg": "Has iniciado sesión correctamente", "token": "TOKEN_SESION" } //Bad request { "error": "Credenciales inválidas" }
-
Nuevo Cliente POST
/costumer/new
{ "name": "Juan Perez", "email": "juan@mail.com", "password": "password", }
Para las siguientes consultas, es necesario haber iniciado sesión con el endpoint señalado anteriormente y disponer de un token (Bearer token), el cual debe ir en el Header Authorization
.
Ej:
Authorization: "Bearer {TOKEN_SESION}"
Donde {TOKEN_SESION}
es el token provisto.
-
Log out DELETE
auth/logout
Responses:
//Good Request { "msg": "Se ha cerrado sesión correctamente" } //Bad Request { "error": "Debe iniciar sesión" }
-
Nueva Orden POST
/orders/new
{ "name": "Nueva Orden", "description": "Esta es una nueva orden" }
Response:
//Good Request { "id": X, "name": "Nueva Orden", "description": "Esta es una nueva orden" } //Bad Request { "error": "Debes iniciar sesión" }
-
Agregar Productos POST
/orders/:id/add
{ "products": [ 1, 2 ... ] }
Response:
//Good Request { "order": { "id": 0, "name": "Esta es la Orden 0", "description": "Esta es una orden" }, "products": [ { "id": 1, "name": "Producto 1", "description": "Esta es la descripción de un producto", "price": 100, "stock": 1 }, { "id": 2, "name": "Producto 2", "description": "Esta es la descripción de un producto", "price": 100, "stock": 1 }, ] } //Bad Request { "error": "No se ha podido crear la orden algunos productos no tienen suficiente stock", "declinedProducts": [ 5 ], }
-
Products GET
/products
Response:
{ "products": [ { "id": 1, "name": "Producto 1", "description": "Esta es la descripción de un producto", "price": 100, "stock": 1 }, { "id": 2, "name": "Producto 2", "description": "Esta es la descripción de un producto", "price": 100, "stock": 1 }, { "id": 3, "name": "Producto 3", "description": "Esta es la descripción de un producto", "price": 100, "stock": 1 }, ... ] }
-
Customers GET
/customers/:id
Response:
{ "id": 1, "name": "Juan", "email": "j@uc.cl", "password": "HASHED_PASSWORD", "token": "SECRET_TOKEN", "createdAt": "2020-11-01T17:54:46.263Z", "updatedAt": "2020-11-01T17:54:59.525Z" }
PATCH
/customers/:id
{ "name": "NEW_NAME", "email": "NEW_EMAIL", }
Response:
{ "id": 1, "name": "NEW_NAME", "email": "NEW_EMAIL", "password": "HASHED_PASSWORD", "token": "SECRET_TOKEN", "createdAt": "2020-11-01T17:54:46.263Z", "updatedAt": "2020-11-01T17:54:59.525Z" }
DELETE
/customers/:id
Response:
{ "msg": "Cliente eliminado correctamente" }