Connecting a Database

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.

4 min read Level 2/5 #fastify#database#plugins
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.

@fastify/postgres →