A Hapi.js plugin that automatically adds version prefixes to all your API routes, making it easy to version your API endpoints.
npm install apiv- Automatically prefixes all routes with a version and/or API prefix
- Global configuration applies to all routes
- Simple setup with sensible defaults
- Supports custom prefixes and version strings
- Can be disabled globally when needed
- TypeScript support included
By default, the plugin prefixes all routes with /api/v1:
import Hapi from '@hapi/hapi'
import Apiv from 'apiv'
const server = Hapi.server({ port: 3000 })
await server.register({ plugin: Apiv })
server.route({
method: 'GET',
path: '/users',
handler: () => ({ users: [] })
})
await server.start()
// Route is now accessible at: GET /api/v1/usersYou can customize both the version and prefix:
await server.register({
plugin: Apiv,
options: {
version: 'v2',
prefix: 'service'
}
})
server.route({
method: 'GET',
path: '/users',
handler: () => ({ users: [] })
})
// Route is now accessible at: GET /service/v2/usersYou can set either option to an empty string to exclude it:
// Only version, no prefix
await server.register({
plugin: Apiv,
options: {
prefix: '',
version: 'v1'
}
})
// Routes accessible at: GET /v1/users
// Only prefix, no version
await server.register({
plugin: Apiv,
options: {
prefix: 'api',
version: ''
}
})
// Routes accessible at: GET /api/users
// Neither (routes unchanged)
await server.register({
plugin: Apiv,
options: {
prefix: '',
version: ''
}
})
// Routes accessible at: GET /usersYou can disable the plugin entirely by setting enabled: false:
await server.register({
plugin: Apiv,
options: { enabled: false }
})
server.route({
method: 'GET',
path: '/users',
handler: () => ({ users: [] })
})
// Route remains at: GET /users (no prefix applied)All configuration is done at the plugin registration level:
| Option | Type | Default | Description |
|---|---|---|---|
version |
string |
'v1' |
Version string to prepend to routes (max 255 chars) |
prefix |
string |
'api' |
Prefix string to prepend before version (max 255 chars) |
enabled |
boolean |
true |
Whether to enable the plugin |
- Per-Route Overrides Supported (via aliases): Add
options.plugins.apivto a route to create an extra alias path with a differentprefixand/orversion. The global path still exists. - Global Prefix Remains: The plugin sets a global prefix for all routes; per-route overrides add aliases and do not replace the global path.
- Per-Route Disable (alias): Use
options.plugins.apiv: falseor{ enabled: false }to add an unprefixed alias for that route. The globally prefixed path remains unless the plugin is disabled globally.
You can add route-specific overrides using options.plugins.apiv. These do not modify the original route registration; they create additional alias paths at server startup.
server.route({
method: 'GET',
path: '/users',
options: { plugins: { apiv: { version: 'v2' } } },
handler: () => ({ users: [] })
})
// Aliases:
// - Global: GET /api/v1/users
// - Override: GET /api/v2/usersserver.route({
method: 'GET',
path: '/users',
options: { plugins: { apiv: { prefix: 'service' } } },
handler: () => ({ users: [] })
})
// Aliases:
// - Global: GET /api/v1/users
// - Override: GET /service/v1/usersserver.route({
method: 'GET',
path: '/users',
options: { plugins: { apiv: { prefix: 'service', version: 'v3' } } },
handler: () => ({ users: [] })
})
// Aliases:
// - Global: GET /api/v1/users
// - Override: GET /service/v3/usersserver.route({
method: 'GET',
path: '/users',
options: { plugins: { apiv: { version: '' } } },
handler: () => ({ users: [] })
})
// Aliases:
// - Global: GET /api/v1/users
// - Override: GET /api/users// Disable via boolean
server.route({
method: 'GET',
path: '/health',
options: { plugins: { apiv: false } },
handler: () => ({ status: 'ok' })
})
// Disable via object
server.route({
method: 'GET',
path: '/status',
options: { plugins: { apiv: { enabled: false } } },
handler: () => ({ status: 'ok' })
})
// Aliases:
// - Global: GET /api/v1/health, /api/v1/status
// - Unprefixed: GET /health, /statusIf you need to support multiple API versions, you can register separate server instances or use different plugins:
// All routes get v1 prefix
await server.register({
plugin: Apiv,
options: { version: 'v1' }
})
server.route({
method: 'GET',
path: '/users',
handler: () => ({ version: 1 })
})
// Accessible at: GET /api/v1/usersThe plugin works with any route path structure:
server.route({
method: 'GET',
path: '/users/{id}/posts',
handler: () => ({ posts: [] })
})
// Accessible at: GET /api/v1/users/{id}/postsEven the root path gets prefixed:
server.route({
method: 'GET',
path: '/',
handler: () => ({ message: 'API Root' })
})
// Accessible at: GET /api/v1The plugin includes TypeScript definitions:
import { Server } from '@hapi/hapi'
import Apiv, { ApiVersionPluginOptions } from 'apiv'
const server: Server = Hapi.server({ port: 3000 })
const options: ApiVersionPluginOptions = {
version: 'v2',
prefix: 'api',
enabled: true
}
await server.register({ plugin: Apiv, options })MIT