Skip to main content

Plugins de Fastify: bloques de construcción para una API de backend con Node.js

Escrito por
blog feature snyk container custom base recommendations

28 de mayo de 2024

0 minutos de lectura

En el mundo de la creación de APIs de backend con Node.js, Fastify se destaca por su ecosistema de plugins y su enfoque arquitectónico, y ofrece una alternativa atractiva al tradicional framework Express. Este framework web, muy eficiente y con baja sobrecarga, se distingue por su notable velocidad y simplicidad. En el centro del diseño de Fastify está su sólida arquitectura de plugins, que aprovecha al máximo las capacidades asíncronas de Node.js y establece un nuevo estándar de rendimiento entre los frameworks de Node.js al destacar el papel fundamental de los plugins de Fastify.

El desarrollo de Fastify está impulsado por una creciente comunidad de código abierto, lo que permite que se mantenga al día con las últimas tendencias en el desarrollo de aplicaciones web. Si te dedicas al desarrollo web de backend en 2024, Fastify ha demostrado ser un framework valioso para crear aplicaciones web rápidas, escalables y seguras.

Por qué Fastify y sus plugins permiten crear una API de backend sólida con Node.js

Para desarrollar aplicaciones web modernas, Fastify se ha convertido en una opción atractiva para los desarrolladores. Este framework web, creado para Node.js, combina rendimiento, flexibilidad y funciones que facilitan el trabajo de los desarrolladores. En esta sección, exploraremos las ventajas de usar Fastify para desarrollar aplicaciones modernas.

Compatibilidad moderna con ESM

Fastify ofrece compatibilidad directa con los módulos de ECMAScript (ESM). Esto es importante porque ESM es el formato oficial de los módulos de JavaScript en los paquetes. Con Fastify, puedes usar de forma nativa la sintaxis import y export sin necesidad de transpilar.

Este es un ejemplo sencillo de un servidor Fastify que usa ESM:

import fastify from 'fastify';

const server = fastify();

server.get('/', async (request, reply) => {
  return { hello: 'world' }
});

server.listen({port: 3000}, (err, address) => {
  if (err) throw err
  server.log.info(`Server listening at ${address}`)
});

Definición adecuada de rutas con promesas y async/await

Fastify facilita la gestión de rutas con la sintaxis async/await. Esto permite escribir código más limpio y legible en comparación con los patrones tradicionales de callbacks.

Express no admite async/await de forma nativa en las definiciones de rutas, lo que suele provocar un infierno de callbacks o la necesidad de envolver los controladores de rutas en bloques try/catch para gestionar errores. La compatibilidad nativa de Fastify con async/await facilita la escritura y el mantenimiento de código asíncrono que usa promesas.

Este ejemplo muestra cómo Fastify usa async/await en la definición de rutas:

server.get('/api/tasks', async (request, reply) => {
  const tasks = await Tasks.findAll();
  return tasks;
});

Alto rendimiento gracias a la resolución rápida del árbol radix de rutas

Fastify está diseñado para ofrecer un alto rendimiento. Cuenta con una resolución rápida del árbol radix de rutas, un algoritmo de enrutamiento muy eficiente. Esto permite que Fastify gestione muchas rutas con una sobrecarga mínima y responda rápidamente incluso con una carga elevada.

Una arquitectura de plugins de Fastify completa y con encapsulación

Fastify tiene una arquitectura de plugins completa que admite la encapsulación. Esto significa que puedes dividir fácilmente tu aplicación en componentes aislados, cada uno con sus propias rutas, plugins y decoradores. Así, organizas mejor el código y reduces el riesgo de conflictos de nombres.

Este es un ejemplo de un plugin de Fastify que agrega una función de utilidad al controlador de rutas:

import fp from 'fastify-plugin';

export default fp(async function (fastify, opts) {
  fastify.decorate('utility', () => 'This is a utility function');
});

Usarías este plugin de la siguiente manera:

// import and get a fastify server instance,
// and then:
server.register(import('./utility-plugin.js'));

server.get('/', async (request, reply) => {
  return server.utility();
});

Inyección de dependencias ligera

El sistema de plugins de Fastify también funciona como un sistema ligero de inyección de dependencias (DI). Esto facilita compartir utilidades y servicios comunes en toda la aplicación, sin recurrir a instancias singleton ni a variables globales.

En el siguiente ejemplo, se comparte una conexión a la base de datos entre distintas partes de la aplicación:

fastify.register(async function (fastify, opts) {
  const db = await someDbConnectionLibrary(opts.connectionString);

  fastify.decorate('db', db);
});

Una API sencilla de backend con Node.js y Fastify

Esta publicación se centra en los componentes fundamentales para crear APIs de backend con Node.js usando Fastify y sus plugins recomendados en 2024. Analizaremos en profundidad los componentes esenciales de una aplicación Fastify, desde el enrutamiento y el middleware hasta la validación y la serialización.

Además, dada la importancia de la ciberseguridad en el mundo digital actual, exploraremos brevemente algunos aspectos de la seguridad web y cómo usar plugins de Fastify para gestionarlos, un aspecto crucial que a menudo se pasa por alto por las prisas del desarrollo de aplicaciones.

Para empezar, asegúrate de tener instalada en tu sistema la versión LTS más reciente de Node.js (a la fecha de publicación, es la v20.11.1). Luego, inicializa un proyecto nuevo de Node.js e instala Fastify con npm, como se muestra a continuación:

$ mkdir fastify-app && cd fastify-app
$ npm init -y
$ npm install --save fastify

Edita el archivo package.json para agregar una definición de la clave de nivel superior type: module y habilitar la compatibilidad con ESM:

{
  "type": "module",
}

Luego, podemos continuar con la estructura básica de una aplicación Fastify en un archivo server.js en el directorio raíz del proyecto:

// Import Fastify using ESM syntax
import Fastify from "fastify";

const fastify = Fastify({ logger: true });

// Defining a route
fastify.get("/", async (request, reply) => {
  return { hello: "world" };
});

async function startServer() {
  try {
    // Start the server
    const address = await fastify.listen({
      port: 3000,
      host: "localhost",
    });

    // Log the server start information using the address returned by Fastify
    // Fastify will already log the server start information by default
    // But here is an example of how you can log the server start information
    // fastify.log.info(`Server starting up at ${address}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
}

startServer();

Encapsular la ruta de la API en un plugin de Fastify

Fastify promueve el uso de plugins para organizar el código y habilitar la encapsulación. Esto te permite mantener agrupado el código relacionado y ayuda a evitar conflictos entre distintas partes de la aplicación.

Refactoricemos nuestra ruta "Hello World" para convertirla en un plugin de Fastify:

const fastify = require('fastify')({ logger: true });

const helloRoute = async (fastify, options) => {
  fastify.get('/', async (request, reply) => {
    return { hello: 'world' };
  });
};

fastify.register(helloRoute);

const start = async () => {
  try {
    await fastify.listen(3000);
    fastify.log.info(`server listening on ${fastify.server.address().port}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};
start();

Usar esquemas para las solicitudes y respuestas de las rutas

Fastify permite validar las solicitudes entrantes y definir la estructura de las respuestas salientes mediante esquemas JSON. Esto ayuda a garantizar la integridad de los datos de la aplicación y simplifica la creación de documentación.

Así puedes definir un esquema de solicitud y respuesta para la ruta "Hello World":

const helloRoute = async (fastify, options) => {
  fastify.route({
    method: 'GET',
    url: '/',
    schema: {
      response: {
        200: {
          type: 'object',
          properties: {
            hello: { type: 'string' },
          },
        },
      },
    },
    handler: async (request, reply) => {
      return { hello: 'world' };
    },
  });
};

fastify.register(helloRoute);

Ahora, Fastify validará automáticamente la respuesta de la ruta "Hello World" con el esquema proporcionado. Esto ayuda a detectar posibles errores y garantiza que la aplicación funcione como se espera.

Los plugins de Fastify como bloques de construcción de aplicaciones Fastify

En esta sección, veremos cuatro plugins clave de Fastify: @fastify/pino, @fastify/cors, @fastify/env y @fastify/swagger, junto con @fastify/swagger-ui. Estos plugins son útiles para sentar las bases de cualquier aplicación Fastify al crear una API de backend con Node.js.

El logger Pino para Fastify

Pino es un logger de alto rendimiento para Node.js, con sus propios plugins para integrarse con otros sistemas de registro, como Sentry y otros.

Puedes aprovechar que Fastify usa Pino como logger predeterminado. Ya estás usando Pino internamente cuando inicializas Fastify con { logger: true }. Sin embargo, puedes pasar un objeto de configuración a la propiedad logger si quieres controlar mejor el nivel de registro u otras configuraciones específicas de Pino.

Durante el desarrollo, quizá prefieras una salida de registros más amigable y colorida. Para conseguirlo, podemos usar el paquete pino-pretty. Primero, instala pino y pino-pretty:

npm install --save pino pino-pretty

Luego, configura pino-pretty de la siguiente manera:

import pino from "pino";

const logger = pino({
  transport: {
    target: "pino-pretty",
    options: {
      colorize: true,
    },
  },
});

logger.info("Hello from pino-pretty");

const fastify = Fastify({
  logger: logger,
});

En el ejemplo anterior, inicializamos un logger con pino y lo configuramos para que use pino-pretty como destino de transporte, con la opción colorize para habilitar una salida de registros colorida. Luego, pasamos nuestra variable personalizada logger a la opción logger de Fastify.

Esto transforma la salida de registros en un formato más fácil de leer y entender:

[12:13:43.499] INFO (95477): hello from pino-pretty
[12:13:43.517] INFO (95477): Server listening at http://[::1]:3000
[12:13:43.518] INFO (95477): Server listening at http://127.0.0.1:3000

Entre las ventajas de usar Pino en Fastify se incluyen:

  • Registro más rápido: el principal objetivo de Pino es ser el logger más rápido para Node.js.

  • Logger versátil: admite distintos niveles de registro y puede dar formato JSON a los registros.

El plugin de Fastify @fastify/cors

El plugin @fastify/cors ofrece una forma sencilla de usar CORS (intercambio de recursos de origen cruzado) en tu aplicación Fastify.

Ventajas:

  • Seguro: ayuda a gestionar el flujo de datos entre distintos orígenes.

  • Personalizable: te permite definir una función personalizada para establecer las opciones de CORS.

Este es un ejemplo sencillo que muestra cómo agregar y usar el plugin @fastify/cors:

// after creating the fastify instance
// let's register the fastify cors plugin:
fastify.register(require('@fastify/cors'), { 
  origin: '*',
  methods: ['GET','POST', 'PUT', 'DELETE']
})

Luego, podemos probar si la configuración de CORS funciona enviando una solicitud cURL al servidor Fastify:

curl -i http://localhost:3000

La respuesta debería incluir el encabezado Access-Control-Allow-Origin, que indica que la configuración de CORS funciona como se espera:

HTTP/1.1 200 OK
access-control-allow-origin: *
content-type: application/json; charset=utf-8
content-length: 17
Date: Sun, 18 Feb 2024 10:20:15 GMT
Connection: keep-alive
Keep-Alive: timeout=72

El plugin de Fastify @fastify/env

Es común que los desarrolladores de Node.js usen variables de entorno para configurar sus aplicaciones y, más aún, que usen el archivo .env para gestionarlas.

Aquí es donde resulta útil el plugin de Fastify @fastify/env. Puedes usar dotenv o la opción integrada de Node.js --env-file para cargar la configuración, pero este plugin de Fastify te permite interactuar con ella directamente desde tu aplicación Fastify y, además, validar la configuración y convertir tipos.

Este es un ejemplo sencillo que muestra cómo agregar y usar el plugin @fastify/env. Empieza por instalarlo y crear un archivo .env sencillo:

npm install --save @fastify/env
echo "PORT=3001" > .env

Luego, actualiza el código de server.js para registrar este nuevo plugin de Fastify:

fastify.register(import('@fastify/env'), {
  dotenv: true,
  schema: {
    type: 'object',
    required: [ 'PORT' ],
    properties: {
      PORT: {
        type: 'string',
        default: 3000
      }
    }
  }
})

Al registrar el plugin de Fastify @fastify/env, puedes acceder a la configuración definida en el archivo .env mediante el objeto fastify.config. Sin embargo, no podemos usarlo directamente. Debemos introducir un nuevo concepto relacionado con la arquitectura de plugins de Fastify.

Como la arquitectura de plugins de Fastify es asíncrona, es necesario esperar a que Fastify inicialice todos los plugins antes de que se apliquen. Esto garantiza que los plugins estén registrados correctamente y listos para usarse. Si te saltaras este paso, se produciría un error al intentar acceder al objeto fastify.config.

Por lo tanto, debemos actualizar nuestra nueva función startServer() de la siguiente manera:

try {
    // wait for all plugins to run 
    await fastify.ready();
    // Start the server
    const address = await fastify.listen({
      // now we can access the fastify.config.PORT configuration
      port: fastify.config.PORT,
      host: "localhost",
    });

Ventajas de usar el plugin @fastify/env:

  • Validación de variables de entorno: garantiza que estén presentes las variables de entorno obligatorias.

  • Conversión de tipos: convierte las variables de entorno a los tipos necesarios.

  • Inyección de dependencias ligera: facilita compartir la configuración en toda la aplicación, siempre que puedas acceder a la instancia de la aplicación fastify.

Documentar APIs de backend con Node.js usando los plugins de Fastify @fastify/swagger y @fastify/swagger-ui

El plugin @fastify/swagger ayuda a generar y mostrar una página de documentación de Swagger para tu aplicación Fastify. @fastify/swagger-ui es un plugin que ofrece una interfaz de usuario integrada para la documentación de Swagger. Se puede acceder a ella desde un navegador web y la propia aplicación Fastify sirve esta página de Swagger UI.

Como antes, empezaremos por agregar estos plugins a nuestra aplicación Fastify:

npm install --save @fastify/swagger @fastify/swagger-ui

Luego, registraremos estos dos plugins en el archivo server.js:

fastify.register(import('@fastify/swagger'), {
  routePrefix: '/documentation',
  swagger: {
    info: {
      title: 'Test swagger',
      description: 'testing the fastify swagger api',
      version: '0.1.0'
    },
    host: 'localhost',
    schemes: ['http'],
    consumes: ['application/json'],
    produces: ['application/json']
  },
  exposeRoute: true
})

fastify.register(import('@fastify/swagger-ui'))

Reinicia el servidor Fastify y abre esta URL en tu navegador: http://localhost:3001/documentation. Allí verás la página de documentación de Swagger UI generada. Así de fácil: ya tienes una página de documentación de Swagger completamente funcional para tu aplicación Fastify.

El plugin Fastify @fastify/swagger agrega documentación de Swagger para las API de backend de Node.js.

Una nota sobre seguridad: en un entorno de producción, considera proteger la página de documentación de Swagger UI con autenticación y autorización. Evita usar tus servidores Node.js de producción para servir esta página.

Ventajas:

  • Documentación de API sencilla: genera documentación de API automáticamente.

  • Personalizable: permite personalizar la página de documentación.

En resumen

Como vimos, el dinámico ecosistema de Fastify ofrece diversos plugins que pueden mejorar notablemente el proceso de desarrollo de APIs de backend con Node.js. Desde mejorar las capacidades de registro con pino y pino-pretty hasta reforzar la seguridad del navegador con CORS mediante @fastify/cors, gestionar variables de entorno con @fastify/env y documentar la API con @fastify/swagger y @fastify/swagger-ui, estas herramientas están diseñadas para agilizar tu flujo de trabajo, mejorar el rendimiento y elevar la calidad de tus aplicaciones.

Podemos resumir lo que aprendimos sobre los plugins de Fastify así:

Adoptar un desarrollo modular: la arquitectura de plugins de Fastify promueve el desarrollo modular y fomenta una base de código limpia y fácil de mantener. Al integrar estos plugins, los desarrolladores pueden centrarse en escribir la lógica de negocio en lugar de código repetitivo, y desarrollar rápidamente sin sacrificar el rendimiento ni la escalabilidad.

Mejorar la productividad y el rendimiento: Cada plugin, con su enfoque específico, aborda desafíos comunes del desarrollo web y te permite crear API robustas, eficientes y seguras. La facilidad con la que se integran en Fastify, junto con los beneficios que ofrecen, como un registro mejorado, el uso compartido de recursos entre distintos orígenes, la configuración del entorno y la documentación de API, contribuye directamente a aumentar la productividad de los desarrolladores y el rendimiento de las aplicaciones.

Fomentar una mayor exploración: Si bien destacamos algunos plugins esenciales, el ecosistema de Fastify cuenta con muchos otros que responden a diversas necesidades, desde la autenticación hasta la integración con bases de datos, la limitación de solicitudes y mucho más. Te invitamos a explorar la página de plugins de Fastify y experimentar con estas herramientas para descubrir cómo pueden optimizar y mejorar aún más tu proceso de desarrollo de API.

Puedes consultar el código fuente completo de todos los ejemplos anteriores de una API backend de Node.js con los plugins de Fastify que mencionamos.