Route Prefixes

Mount a Plugin Under /api or /v1

Route Prefixes

Pass a prefix when registering a plugin to namespace every route it declares — perfect for versioning and feature mounting.

4 min read Level 2/5 #fastify#plugins#prefix
What you'll learn
  • Pass prefix in the register options
  • Nest prefixes naturally
  • Combine prefixes with versioning strategies

A prefix option on app.register applies to every route inside that plugin. It compounds with parent prefixes, so the tree maps cleanly to the URL space.

Basic Mounting

// src/routes/users.ts
export default async function usersRoutes(app) {
  app.get('/', listUsers);          // -> /api/users
  app.get('/:id', getUser);         // -> /api/users/:id
  app.post('/', createUser);        // -> /api/users
}
// src/index.ts
import usersRoutes from './routes/users.js';

await app.register(usersRoutes, { prefix: '/api/users' });

The plugin doesn’t know it lives under /api/users — easier to test in isolation and to move around later.

Nested Prefixes Compound

await app.register(
  async (api) => {
    await api.register(usersRoutes, { prefix: '/users' });
    await api.register(postsRoutes, { prefix: '/posts' });
  },
  { prefix: '/api/v1' },
);

That yields /api/v1/users and /api/v1/posts. Bump the outer prefix to /api/v2 and every nested route follows.

Trailing Slash Behavior

By default, prefix: '/api/users' matches both /api/users and /api/users/.... If you register app.get('/'), it answers at /api/users/ and /api/users — Fastify treats the bare prefix as equivalent. Override with ignoreTrailingSlash: false if you want to be strict.

Versioning Pattern

Combine a prefix with the route-level constraints: { version: '1.0.0' } option to support parallel API versions in one process — useful while you migrate clients to a new contract.

Organizing Routes Into Modules →