fastify-plugin and decorators
A decorator is a property or method you add to the Fastify instance, to the request or to the reply. This way shared objects — a post store, a database connection, a check function — become available in every route without global variables.
decorate
Let posts live in a separate module. Create the plugin src/plugins/store.js:
// src/plugins/store.js
export default async function store(app) {
const posts = [
{ id: 1, title: 'First post', body: 'Text' },
];
app.decorate('posts', posts);
}app.decorate('posts', posts) adds the app.posts property. But because of encapsulation it is visible only inside this plugin — routes in routes/posts.js will not see it.
fastify-plugin
The fastify-plugin package removes encapsulation: everything a wrapped plugin adds goes to the parent context.
npm install fastify-pluginWrap the plugin:
// src/plugins/store.js
import fp from 'fastify-plugin';
async function store(app) {
const posts = [
{ id: 1, title: 'First post', body: 'Text' },
];
app.decorate('posts', posts);
}
export default fp(store);Register it in src/app.js before the routes so the property exists when they load:
// src/app.js
import store from './plugins/store.js';
import postRoutes from './routes/posts.js';
app.register(store);
app.register(postRoutes);Using it in routes
Inside the routes plugin the property is available through app, and in a handler also through this:
// src/routes/posts.js
export default async function postRoutes(app) {
app.get('/posts', async () => {
return app.posts;
});
}http://localhost:3000/posts returns the posts from the store. When a database appears later, only the plugin changes and the routes stay the same.
decorateRequest
Request properties are declared in advance with decorateRequest, and the value is filled in for each request — usually in a hook:
// src/plugins/timing.js
import fp from 'fastify-plugin';
async function timing(app) {
app.decorateRequest('startTime', 0);
}
export default fp(timing);Register the plugin in src/app.js with app.register(timing). Now every request has a startTime property set to 0. In the hooks lesson the request start time is written there.
Kinds of decorators
| Method | Adds to | How to read |
|---|---|---|
app.decorate() | The instance | app.posts |
app.decorateRequest() | Every request | request.startTime |
app.decorateReply() | Every reply | reply.name |
app.hasDecorator() | — | Check whether a decorator exists |
Common mistake
In Fastify 5 you cannot pass an object or array as the initial value of decorateRequest — it would be shared by all requests, and Fastify throws an error at startup:
// src/plugins/timing.js
app.decorateRequest('meta', { name: '' }); // errorPass null and write the object separately in each request.