En esta lección vamos a aprender cómo crear y utilizar middleware en Fastify para mejorar el rendimiento y la seguridad de tu aplicación. Un middleware es una función que se ejecuta antes de que el servidor procese una solicitud. Su propósito puede variar: puede verificar permisos de acceso, registrar información, transformar datos, entre otras tareas.
Envoltura
Antes de pasar a estudiar directamente el middleware, vamos a entender su principio de funcionamiento con ejemplos de funciones comunes.
Imagina que tenemos una función que suma 5 al argumento:
const plusFive = (x) => x + 5;
Nuestra tarea es ampliar la funcionalidad de esta función sin cambiar la función en sí. Por ejemplo, queremos sumar 5 al doble del valor del argumento. Para resolver esta tarea, podemos crear una nueva función que utilice la función existente en su interior:
const doublePlusFive = (x) => plusFive(x * 2);
De la misma manera, podemos extender la segunda función:
const nextFunc = (x) => doublePlusFive(x - 10) - 3;
nextFunc(20); // ((((20 - 10) * 2) + 5) - 3) = 22
En general, la extensión de una función se ve así:
const nextF = (/* args */) => {
// preprocesamiento
const result = prevF(/* updatedArgs */);
// posprocesamiento
return /* newResult */;
};
Para extender el comportamiento de una función, necesitas escribir una nueva función que utilice la primera. La única condición a cumplir es que los interfaces de estas funciones coincidan: el número, el tipo de argumentos y el resultado devuelto deben ser los mismos. De esta manera, el código que utiliza tu función envuelta ni siquiera puede adivinar que está envuelta, y lo más importante, no necesita ser reescrito, ya que el interfaz de la función no ha cambiado, aunque haya aparecido un nuevo comportamiento. Este método también se conoce como decoración, y en los manuales de patrones de diseño se describe como "patrón decorador".
El middleware funciona de manera similar. Amplían la funcionalidad de los manejadores de solicitudes envolviéndolos en funciones similares.
Para que el middleware funcione, debes conectar el plugin @fastify/middie o @fastify/express al la aplicación usando el método register().
Instalación del plugin:
npm i @fastify/middie
Ejemplo de código con conexión:
import fastify from 'fastify';
import middie from '@fastify/middie';
import morgan from 'morgan';
const app = fastify();
const port = 3000;
await app.register(middie);
app.use((req, res) => {
res.end('Hello from middleware!');
});
app.listen({ port }, () => {
console.log(`Example app listening on port ${port}`);
});
En el ejemplo anterior se conecta un middleware que devuelve el texto 'Hello from middleware!'. Luego, explicaremos en detalle cómo funciona esto.
Middleware
El plugin proporciona un mecanismo que extiende nuestra aplicación con funciones llamadas middleware. Cada vez que usamos use, otro middleware se agrega a la cola general. Al final, obtendremos un objeto lleno de middleware. Cada solicitud enviada para su procesamiento en la aplicación pasa por una cadena de estos middleware hasta que se encuentra con un middleware terminal.
A su vez, cada middleware acepta tres parámetros de entrada: request, response y next. Puede cambiarlos y al final debe llamar a next para pasar el control al siguiente middleware en la lista. En esto reside todo el poder de los microframeworks. Un diseño exitoso permite dividir fácilmente el sistema en módulos-middleware y extenderlos gracias a los middleware, que, en gran cantidad, son escritos por desarrolladores externos.
app.use((req, res, next) => {
res.setHeader('key', 'value');
next();
});
En el ejemplo anterior, el middleware agrega una cabecera a cada solicitud.
Middleware de Rutas Específicas
Lo más interesante de los middleware es que los manejadores de rutas específicas también son simplemente middleware. Su particularidad es que están vinculados a una ruta específica, a diferencia de los middleware que se ejecutan para todas las solicitudes.
app.use('/foo', (req, res, next) => {
// req.url empieza con "/foo"
next();
});
app.use('/bar', (req, res, next) => {
// req.url empieza con "/bar"
next();
});
Estos middleware permiten implementar enrutamiento básico sin vinculación a un verbo HTTP específico y sin soporte para rutas dinámicas.
Terminar
Pero no siempre queremos profundizar. De hecho, en algún momento uno de los middleware debe asumir el control del proceso.
app.use((req, res) => {
res.end('Hello from first!');
});
app.use((req, res) => {
res.send('Hello from second!');
});
Este comportamiento, donde hay una cadena de funciones y cualquiera de ellas durante el procesamiento puede decidir detener la cadena y devolver una respuesta, tiene un nombre. Este tipo de cadenas se llaman chain responsibility, y es también un patrón.
Bibliotecas
Muchas bibliotecas que añaden funcionalidad a la aplicación trabajan con middleware. Veamos un ejemplo con la biblioteca morgan. Esta es una biblioteca para registrar solicitudes. Antes de usarla, instalamos el paquete:
npm i morgan
Ejemplo de código:
import fastify from 'fastify';
import middie from '@fastify/middie';
import morgan from 'morgan';
const app = fastify();
const port = 3000;
const logger = morgan('combined');
await app.register(middie);
app.use(logger);
app.listen({ port }, () => {
console.log(`Aplicación de ejemplo escuchando en el puerto ${port}`);
});
En el ejemplo anterior, creamos una función de registro usando la función morgan() y luego la pasamos como middleware. Ahora, para cada solicitud, aparecerá un registro detallado en la aplicación. Por ejemplo:
Aplicación de ejemplo escuchando en el puerto 3000
127.0.0.1 - - [18/Apr/2024:08:15:59 +0000] "GET / HTTP/1.1" 404 72 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0"
Para acceder completo a curso necesitas un plan básico
El plan básico te dará acceso completo a todos los cursos, ejercicios y lecciones de Códica, proyectos y acceso de por vida a la teoría de las lecciones completadas. La suscripción se puede cancelar en cualquier momento.