A Plugin Per Driver, Decorate the App
Connecting a Database
The Fastify pattern for databases is straightforward: wrap the DB client in a plugin that decorates the app and registers an onClose hook for graceful shutdown.
What you'll learn
- Pick a driver such as pg, mysql, or mongo
- Build a small plugin that decorates and registers onClose
- Use the client from handlers via the decorator
Connecting a database in Fastify is not magic. You pick a driver, create a single client (or pool) at boot, expose it via a decorator, and tear it down on shutdown. Every popular DB plugin follows this exact shape.
The Pattern
A database plugin owns three things: connecting at boot, decorating the app, and disconnecting on onClose.
import fp from 'fastify-plugin'
import { createClient, type Client } from 'my-db-driver'
declare module 'fastify' {
interface FastifyInstance {
db: Client
}
}
export default fp(async (app) => {
const client = createClient({ url: app.config.DATABASE_URL })
await client.connect()
app.decorate('db', client)
app.addHook('onClose', async () => {
await client.end()
})
}) Wrapping with fastify-plugin means the decorator escapes the encapsulation context — handlers anywhere can use app.db.
Using It From Handlers
Once decorated, the client is just a property on the app (or request, depending on the plugin).
app.get('/health', async () => {
const result = await app.db.query('SELECT 1 as ok')
return { ok: result.rows[0].ok === 1 }
}) Why Not Just Import It?
Centralising the client in a plugin gives you three things at once: a single shared pool, automatic shutdown, and a typed decorator that any route can use. Avoid the temptation to import db from './db.js' in every handler.