Editar en GitHub

Escritura de middleware para uso en aplicaciones Express

Las funciones de middleware son funciones que tienen acceso al objeto de solicitud (req), al objeto de respuesta (res), y a la función next en el ciclo solicitud-respuesta de la aplicación. La función next es una función del router de Express que, cuando se invoca, ejecuta el middleware que sigue al middleware actual.

Las funciones de middleware pueden realizar las siguientes tareas:

  • Ejecutar cualquier código.
  • Hacer cambios en los objetos de solicitud y respuesta.
  • Finalizar el ciclo solicitud-respuesta.
  • Llamar al siguiente middleware en la pila.

Si la función de middleware actual no finaliza el ciclo solicitud-respuesta, debe llamar a next() para pasar el control a la siguiente función de middleware. De lo contrario, la solicitud quedará colgada.

La siguiente figura muestra los elementos de una llamada a una función de middleware:

Elementos de una llamada a una función de middleware

A partir de Express 5, las funciones de middleware que devuelven una Promise llamarán a next(value) cuando se rechacen o lancen un error. next será llamado con el valor rechazado o el Error lanzado.

Ejemplo

Aquí hay un ejemplo de una aplicación Express simple "Hello World". El resto de este artículo definirá y añadirá tres funciones de middleware a la aplicación: una llamada myLogger que imprime un mensaje de log simple, una llamada requestTime que muestra el timestamp de la solicitud HTTP, y una llamada validateCookies que valida las cookies entrantes.

index.cjs
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);

Función de middleware myLogger

Aquí hay un ejemplo simple de una función de middleware llamada "myLogger". Esta función simplemente imprime "LOGGED" cuando una solicitud a la aplicación pasa por ella. La función de middleware se asigna a una variable llamada myLogger.

const myLogger = function (req, res, next) {
console.log('LOGGED');
next();
};

Precaución

Nota la llamada anterior a next(). Llamar a esta función invoca la siguiente función de middleware en la aplicación. La función next() no es parte de la API de Node.js o Express, pero es el tercer argumento que se pasa a la función de middleware. La función next() podría llamarse de cualquier manera, pero por convención siempre se llama "next". Para evitar confusiones, usa siempre esta convención.

Para cargar la función de middleware, llama a app.use(), especificando la función de middleware. Por ejemplo, el siguiente código carga la función de middleware myLogger antes de la ruta a la ruta raíz (/).

index.cjs
const express = require('express');
const app = express();
const myLogger = function (req, res, next) {
console.log('LOGGED');
next();
};
app.use(myLogger);
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(3000);

Cada vez que la aplicación recibe una solicitud, imprime el mensaje "LOGGED" en la terminal.

El orden de carga del middleware es importante: las funciones de middleware que se cargan primero también se ejecutan primero.

Si myLogger se carga después de la ruta a la ruta raíz, la solicitud nunca la alcanza y la aplicación no imprime "LOGGED", porque el manejador de ruta de la ruta raíz termina el ciclo solicitud-respuesta.

La función de middleware myLogger simplemente imprime un mensaje, luego pasa la solicitud a la siguiente función de middleware en la pila llamando a la función next().

Función de middleware requestTime

A continuación, crearemos una función de middleware llamada "requestTime" y añadiremos una propiedad llamada requestTime al objeto de solicitud.

const requestTime = function (req, res, next) {
req.requestTime = Date.now();
next();
};

La aplicación ahora usa la función de middleware requestTime. Además, la función de callback de la ruta raíz usa la propiedad que la función de middleware añade a req (el objeto de solicitud).

index.cjs
const express = require('express');
const app = express();
const requestTime = function (req, res, next) {
req.requestTime = Date.now();
next();
};
app.use(requestTime);
app.get('/', (req, res) => {
let responseText = 'Hello World!<br>';
responseText += `<small>Requested at: ${req.requestTime}</small>`;
res.send(responseText);
});
app.listen(3000);

Cuando haces una solicitud a la raíz de la aplicación, la aplicación ahora muestra el timestamp de tu solicitud en el navegador.

Función de middleware validateCookies

Finalmente, crearemos una función de middleware que valide las cookies entrantes y envíe una respuesta 400 si las cookies son inválidas.

Aquí hay una función de ejemplo que valida cookies con un servicio asincrónico externo.

async function cookieValidator(cookies) {
try {
await externallyValidateCookie(cookies.testCookie);
} catch {
throw new Error('Invalid cookies');
}
}

Aquí, usamos el middleware cookie-parser para analizar las cookies entrantes del objeto req y pasarlas a nuestra función cookieValidator. El middleware validateCookies devuelve una Promise que al rechazarse activará automáticamente nuestro manejador de errores.

index.cjs
const express = require('express');
const cookieParser = require('cookie-parser');
const cookieValidator = require('./cookieValidator');
const app = express();
async function validateCookies(req, res, next) {
await cookieValidator(req.cookies);
next();
}
app.use(cookieParser());
app.use(validateCookies);
// error handler
app.use((err, req, res, next) => {
res.status(400).send(err.message);
});
app.listen(3000);

Precaución

Nota cómo se llama a next() después de await cookieValidator(req.cookies). Esto asegura que si cookieValidator se resuelve, el siguiente middleware en la pila será llamado. Si pasas cualquier cosa a la función next() (excepto la cadena 'route' o 'router'), Express considera que la solicitud actual es un error y omitirá cualquier función de enrutamiento y middleware restante que no sea de manejo de errores.

Como tienes acceso al objeto de solicitud, al objeto de respuesta, a la siguiente función de middleware en la pila, y a toda la API de Node.js, las posibilidades con las funciones de middleware son infinitas.

Para más información sobre el middleware de Express, consulta: Uso de middleware de Express.

Middleware configurable

Si necesitas que tu middleware sea configurable, exporta una función que acepte un objeto de opciones u otros parámetros, que luego devuelva la implementación del middleware basada en los parámetros de entrada.

my-middleware.cjs
module.exports = function (options) {
return function (req, res, next) {
// Implement the middleware function based on the options object
next();
};
};

El middleware ahora puede usarse como se muestra a continuación.

index.cjs
const mw = require('./my-middleware.cjs');
app.use(mw({ option1: '1', option2: '2' }));

Consulta cookie-session y compression para ejemplos de middleware configurable.