Fastify suele presentarse por sus números de rendimiento, pero los números son un efecto secundario de una elección arquitectónica más interesante: Fastify está construido como un árbol de contextos de plugins aislados, no una única cadena plana como Express, y compila los esquemas anticipadamente en lugar de interpretarlos por solicitud.
Ambas decisiones se tomaron específicamente para solucionar problemas que el modelo plano y de estado compartido de Express encuentra a escala: el acoplamiento accidental entre características no relacionadas y el trabajo repetido por solicitud que podría haberse realizado una vez.
Esta página es el modelo mental detrás de esas dos decisiones: qué es realmente un plugin, cómo funciona la encapsulación y cuándo se rompe deliberadamente, y por qué el diseño "schema-first" es una estrategia de rendimiento, no solo una conveniencia de validación.
Conceptos básicos de Fastify cubre esto de forma práctica con código funcional; Plugins de Fastify profundiza en la carga automática y las convenciones de directorio construidas sobre el modelo descrito aquí.
Fastify estructura una aplicación como un árbol de contextos de plugins anidados (cada llamada a register() crea un nuevo ámbito hijo aislado) y compila los esquemas de solicitud/respuesta en validadores y serializadores rápidos una vez al inicio.
Por qué es importante: La encapsulación evita el acoplamiento de "todo comparte todo" que dificulta la comprensión de las grandes aplicaciones Express, y la compilación anticipada elimina la sobrecarga de validación y serialización por solicitud que un enfoque interpretado paga cada vez.
Conceptos clave:plugin, contexto de encapsulación, fastify-plugin, hook de ciclo de vida, compilación de esquemas, avvio.
Cuándo usar este modelo: Estructurar una aplicación como módulos de características independientes, decidir dónde debe residir un recurso compartido (base de datos, autenticación) en el árbol y comprender por qué las rutas definidas por esquemas superan en rendimiento a la validación ad hoc.
Limitaciones / Compromisos: La encapsulación añade una sobrecarga conceptual real para aplicaciones pequeñas, y olvidar fastify-plugin cuando realmente se quería un ámbito compartido es una fuente común de errores de "por qué las rutas hermanas no pueden ver este decorador".
Temas relacionados: El pipeline de middleware de Express, el grafo de módulos de NestJS, la validación de JSON Schema, la carga automática de plugins.
Un plugin de Fastify es simplemente una función (async (fastify, opts) => { ... }) registrada con fastify.register(), y es la única unidad a partir de la cual se construye Fastify: las rutas, los decoradores, los hooks e incluso la propia aplicación raíz son, estructuralmente, plugins.
Lo que lo diferencia de "simplemente llamar a una función de configuración" es la encapsulación: cada llamada a register() crea un nuevo contexto hijo que hereda todo de su padre en el momento de la creación, pero cuyas propias adiciones (nuevos decoradores, nuevos hooks, nuevas rutas) permanecen invisibles para los plugins hermanos y para el padre.
app.register(async function userRoutes(fastify) { fastify.decorate("cache", new Map()); // solo visible dentro de userRoutes fastify.get("/users", async () => []);}, { prefix: "/users" });// app.cache es indefinido aquí: el decorador nunca salió de ese ámbito hijo
Imagina un árbol, no un pasillo: la aplicación raíz es el tronco, cada llamada a register() hace crecer una nueva rama, y una rama puede ver todo lo que su tronco padre ya tenía, pero nada de lo que una rama hermana creció independientemente.
Fastify fue construido (2016) específicamente para abordar dos cosas que el modelo plano de Express no resuelve bien por defecto: el intercambio accidental entre características no relacionadas y el costo de validar y serializar JSON de la misma manera, desde cero, en cada solicitud.
La forma de árbol no es cosmética, se implementa como una cadena de prototipos real, donde cada contexto hijo hereda prototípicamente los decoradores y la configuración del padre en el momento del registro, por lo que las adiciones posteriores a ese punto no aparecen retroactivamente en contextos creados anteriormente.
fastify-plugin (comúnmente importado como fp) existe precisamente para optar por no usar esto: envolver un plugin con fp() le dice a Fastify que omita la creación de un nuevo contexto hijo y, en su lugar, aplique las adiciones de ese plugin directamente al ámbito padre, que es exactamente lo que se desea para preocupaciones transversales como una conexión a la base de datos o un decorador de autenticación que cada ruta necesita.
El arranque de este árbol de forma asíncrona (ya que los plugins pueden ser async y pueden necesitar esperar una conexión a la base de datos antes de que se registre el siguiente plugin) lo maneja avvio, la biblioteca que Fastify utiliza internamente para garantizar que los plugins terminen de registrarse en orden de dependencia antes de que el servidor comience a aceptar solicitudes, en lugar de competir.
Dentro de una sola solicitud, Fastify reemplaza el modelo next() de una sola cadena de Express con una secuencia fija de hooks de ciclo de vida con nombre: onRequest, preParsing, preValidation, preHandler, preSerialization, onSend, onResponse, cada uno una etapa distinta y con nombre en lugar de una posición indiferenciada en una larga lista.
Esa granularidad es lo que permite que la validación de esquemas se inserte como su propia etapa bien definida (preValidation) en lugar de ser simplemente otra función de middleware que compite por la posición en la cadena, como tendría que ser atornillada a Express.
La compilación de esquemas es la otra mitad del modelo: la opción schema de una ruta se entrega a un compilador (AJV por defecto) una vez, al inicio, produciendo una función de validación optimizada y un serializador JSON optimizado para esa forma específica, por lo que el costo de "esta solicitud coincide con el esquema" y "cómo convierto este objeto de respuesta en texto JSON rápidamente" se paga una vez por definición de ruta, no una vez por solicitud.
El árbol de plugins escala de manera diferente a la cadena plana de Express: una gran aplicación Fastify se descompone naturalmente en un plugin por dominio de características (usuarios, pedidos, facturación), cada uno con sus propias rutas, decoradores y hooks que simplemente no pueden filtrarse accidentalmente a un dominio hermano; el límite de encapsulación realiza el trabajo de aislamiento que los desarrolladores de Express tienen que imponer por convención.
Sin embargo, ese límite tiene un costo real para las aplicaciones pequeñas: un prototipo de cinco rutas obtiene pocos beneficios de una estructura de árbol y paga un pequeño impuesto conceptual (recordar cuándo usar fastify-plugin, comprender por qué un decorador "no es visible" en un archivo hermano) en el que una aplicación Express plana nunca tiene que pensar.
El diseño "schema-first" se extiende más allá de la velocidad bruta: debido a que las rutas declaran su forma de solicitud/respuesta como JSON Schema, ese mismo esquema puede generar automáticamente documentación OpenAPI (@fastify/swagger), lo que le da a Fastify una historia de documentación para la que Express no tiene equivalente sin atornillar una capa de especificación separada; consulta Validación de JSON Schema.
La observabilidad se beneficia de la misma estructura: los hooks onResponse ven el estado final y el tiempo de cada solicitud, independientemente de qué plugin la haya manejado, y el logger Pino incorporado de Fastify (consulta Registro con Pino) se conecta a través del mismo ciclo de vida en lugar de agregarse como una capa de middleware separada.
Abstracción central del framework
Fortaleza
Debilidad
Mejor ajuste
Fastify: árbol de plugins encapsulado
Los ámbitos aislados evitan el acoplamiento accidental; el despacho compilado por esquema es rápido
Estructura adicional que aprender; fácil de usar fastify-plugin incorrectamente
APIs de alto rendimiento o "schema-first"; equipos que desean modularidad forzada sin un contenedor DI
Express: cadena de middleware plana
Mínimo, familiar, enorme ecosistema
Sin encapsulación; la validación se suele volver a ejecutar por solicitud
APIs CRUD de tamaño pequeño a mediano, equipos con mucho ecosistema
NestJS: grafo de módulos DI
Arquitectura forzada, dependencias inyectadas por constructor
Abstracción más pesada; curva de aprendizaje más pronunciada
Equipos grandes, servicios empresariales de larga duración
"Un plugin de Fastify es básicamente lo mismo que un middleware de Express." El middleware es una función en una cadena plana; un plugin es un contexto aislado completo que puede contener rutas, hooks, decoradores y plugins anidados adicionales.
"Deberías envolver todo en fastify-plugin." Hacerlo elimina la encapsulación en todas partes, lo que anula el propósito; resérvalo para cosas que realmente necesitan ser compartidas globalmente, como una conexión a la base de datos o un decorador de autenticación.
"La validación de esquemas hace que Fastify sea más lento que una ruta de Express no validada." Lo contrario suele ser cierto en la práctica: los esquemas se compilan una vez en funciones optimizadas al inicio, lo que suele ser más rápido que la lógica de validación ad hoc que se vuelve a ejecutar desde cero en cada solicitud.
"El orden de registro de plugins no importa porque Fastify lo maneja."avvio garantiza que los plugins terminen de registrarse antes de que el servidor se inicie, pero el registro aún ocurre en el orden en que llamas a register(): un plugin que depende de un decorador de un hermano aún necesita que ese hermano se registre primero, o que se eleve con fastify-plugin.
"Los hooks de ciclo de vida son solo middleware con diferentes nombres." Son etapas distintas y ordenadas vinculadas a puntos específicos en el procesamiento de solicitudes (antes del análisis, antes de la validación, antes de la serialización), no una posición arbitraria en una cadena indiferenciada como lo es el middleware de Express.
¿Cuál es la idea arquitectónica central de Fastify, en una frase?
Un árbol de contextos de plugins aislados, cada uno creado por register(), combinado con definiciones de JSON Schema compiladas una vez al inicio en validadores y serializadores rápidos.
¿Qué es exactamente un "plugin" de Fastify?
Cualquier función registrada con fastify.register(): las rutas, los decoradores, los hooks e incluso la propia aplicación son estructuralmente plugins. Es la única unidad a partir de la cual Fastify compone todo.
¿Cómo funciona realmente la encapsulación bajo el capó?
Cada llamada a register() crea un nuevo contexto hijo que hereda prototípicamente todo lo que el padre tenía en ese momento, pero cualquier decorador, hook o ruta que el hijo agregue permanece invisible para los hermanos y para el padre; se implementa como una cadena de prototipos real, no solo una convención.
¿Cuándo debo usar `fastify-plugin`?
Cuando algo realmente necesita ser visible en todas partes: una conexión a la base de datos, un decorador de autenticación compartido, una configuración global. Deliberadamente omite la creación de un nuevo contexto hijo y aplica las adiciones al ámbito padre en su lugar.
¿Por qué Fastify compila esquemas en lugar de validar sobre la marcha?
Debido a que la forma de solicitud y respuesta de una ruta se conoce de antemano, Fastify la entrega a un compilador (AJV por defecto) una vez al inicio, produciendo una función optimizada, por lo que el costo de validación y serialización se paga una vez por definición de ruta, no se repite en cada solicitud entrante.
¿Qué es `avvio` y por qué lo necesita Fastify?
Es la biblioteca de arranque interna que gestiona el registro asíncrono de plugins, garantizando que los plugins terminen de cargarse en el orden de dependencia correcto antes de que el servidor comience a aceptar tráfico, lo cual es importante porque los plugins pueden await cosas como una conexión a la base de datos antes de que se ejecute el siguiente plugin.
¿En qué se diferencian los hooks de ciclo de vida de Fastify del middleware de Express?
Los hooks son etapas nombradas y fijas (onRequest, preValidation, preHandler, onSend, etc.) vinculadas a puntos específicos en el procesamiento de solicitudes, en lugar de una posición indiferenciada en una cadena plana, lo que permite a Fastify insertar preocupaciones como la validación de esquemas en una etapa bien definida en lugar de una función de middleware que compite por el orden de la cadena.
¿El árbol de plugins es excesivo para una aplicación pequeña?
Para un puñado de rutas, sí, en gran medida; los beneficios de aislamiento aparecen a medida que una base de código crece más allá de unos pocos dominios de características. Un prototipo pequeño paga un pequeño impuesto conceptual por una estructura que quizás aún no necesite.
¿La encapsulación alguna vez perjudica más de lo que ayuda?
Puede hacerlo, cuando un equipo no la entiende; "por qué esta ruta no puede ver ese decorador" es una confusión común al principio hasta que el modelo de árbol/ámbito hace clic, momento en el que se convierte en lo que evita el acoplamiento accidental.
¿Cómo se compara esto con el sistema de módulos de NestJS?
El árbol de plugins de Fastify es más ligero (sin contenedor de inyección de dependencias, sin metadatos impulsados por decoradores), mientras que los módulos de NestJS añaden proveedores, dependencias resueltas por DI y un ciclo de vida de solicitud más elaborado sobre una idea similar de unidades acotadas y componibles.
¿Una aplicación Fastify aún puede generar documentación de API a partir de este modelo?
Sí, debido a que las rutas declaran su forma de solicitud/respuesta como JSON Schema para el compilador, ese mismo esquema puede impulsar la generación automática de OpenAPI (@fastify/swagger), lo cual es un beneficio directo del diseño "schema-first" en lugar de una característica atornillada.
¿El árbol de plugins afecta las pruebas?
Sí, favorablemente: debido a que un plugin es autónomo, se puede registrar en una instancia Fastify() básica y probar con inject() de forma aislada, sin necesidad del resto del árbol de plugins de la aplicación.