Skip to content

@glandjs/fastify@1.1.0-beta

@glandjs/fastify

The Fastify 5 transport.

Terminal window
# pnpm pnpm add @glandjs/fastify
# npm npm install @glandjs/fastify
# yarn yarn add @glandjs/fastify
# bun bun add @glandjs/fastify
Package @glandjs/fastify
Version 1.1.0-beta
Repository glandjs/http
License MIT
Node >= 20
Dependencies @glandjs/core ^1.0.3-beta · @glandjs/events ^1.1.0 · @medishn/toolkit ^1.0.4 · fastify ^5.2.1 · tslib ^2.8.1
Peer dependencies @glandjs/common ^1.0.3-beta · reflect-metadata ^0.2.2

@glandjs/fastify is the Fastify adapter for Gland’s HTTP layer. It contributes a Fastify instance, a FastifyContext, and nothing else — the onion, the request lifecycle, the reply coercion and the error rendering all come from @glandjs/http.

Fastify is the one framework here whose lifecycle is genuinely asynchronous: listen() returns a promise because plugins are still registering, and close() drains connections rather than dropping them.

Terminal window

Optional, for the features that need them: @fastify/cookie, @fastify/formbody, @fastify/static.

import { GlandFactory } from '@glandjs/core';
import { FastifyBroker, type FastifyContext } from '@glandjs/fastify';
import { Get } from '@glandjs/http';
import { Controller, Module } from '@glandjs/common';
@Controller('/products')
class ProductController {
@Get('/:id')
async find(ctx: FastifyContext) {
return ctx.call('db:product:find', ctx.params.id);
}
}
@Module({ controllers: [ProductController] })
class AppModule {}
const { app, shutdown } = await GlandFactory.create(AppModule);
const http = app.connectTo(FastifyBroker, { poweredBy: false });
http.listen(3000);
await http.ready(); // Fastify loads plugins asynchronously
http.fastify.register(require('@fastify/helmet'));

Fastify has no use(), and its hook chain is a hand-off — a hook continues it by returning. Mounting one hook per Gland middleware would therefore give N independent middlewares rather than an onion: outer would finish before inner began.

So the adapter mounts one preHandler hook and walks the Gland chain inside it as a promise-based onion:

app.use(async (ctx, next) => {
const started = Date.now();
await next(); // the downstream half has finished
metrics.timing('route', Date.now() - started);
});

What is given up is the ability for a Gland middleware to short-circuit into a Fastify hook, which is rare and reachable through app.fastify if you need it.

Fastify 5 routes only its own default method set, and addHttpMethod() refuses anything outside node:http#METHODS. So:

PROPFIND, SEARCH, COPY, MOVE, LOCK, … work — declared with addHttpMethod
MKWORKSPACE, UPDATE no equivalent — the adapter warns at boot
[HTTP:Fastify] WARN Fastify cannot route "MKWORKSPACE" (Provided method is invalid!).
The method is not in node:http#METHODS, so it has no Fastify equivalent. Use
@glandjs/express, @glandjs/koa, @glandjs/hono or @glandjs/node if the application
needs it.

Reported at boot rather than at request time, because a route that cannot be registered is a configuration problem and a 500 in production is not.

Fastify has no use(). A framework-native middleware is a plugin:

app.fastify.register(require('@fastify/helmet'));
app.fastify.register(require('@fastify/rate-limit'), { max: 100 });

app.useRaw(...) is reported and ignored, because a silently-dropped compression() leaves an application that believes it is compressed.

Fastify’s own logger is off by default. Gland publishes the lifecycle events, and two loggers emitting the same line per request is worse than one. Turn Fastify’s back on through app.fastify if you want it.

Export Kind
FastifyBroker / FastifyBrokerClass What app.connectTo() takes
FastifyCore The application, with a typed fastify getter
FastifyAdapter The adapter
FastifyContext, FastifyRouteOptions The context

The full surface is in the API reference.

MIT