@fastify/autoload

Convenience plugin for Fastify that loads all plugins found in a directory and automatically configures routes matching the folder structure.
Installation
npm i @fastify/autoload
Compatibility
>=6.x | ^5.x |
^5.x | ^4.x |
>=2.x <5.x | ^3.x |
^1.x | ^2.x |
^1.x | ^1.x |
Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin
in the table above.
See Fastify's LTS policy for more details.
Example
Fastify server that automatically loads in all plugins from the plugins directory:
const fastify = require('fastify')
const autoload = require('@fastify/autoload')
const app = fastify()
app.register(autoload, {
dir: path.join(__dirname, 'plugins')
})
app.listen({ port: 3000 })
or with ESM syntax:
import autoLoad from '@fastify/autoload'
import { fileURLToPath } from 'node:url'
import { dirname, join } from 'node:path'
import fastify from 'fastify'
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
const app = fastify()
app.register(autoLoad, {
dir: join(__dirname, 'plugins')
})
app.listen({ port: 3000 })
Folder structure:
βββ plugins
β βββ hooked-plugin
β β βββ autohooks.mjs
β β βββ routes.js
β β βββ children
β β βββ commonjs.cjs
β β βββ module.mjs
β β βββ typescript.ts
β βββ single-plugin
β β βββ index.js
β β βββ utils.js
β βββ more-plugins
β β βββ commonjs.cjs
β β βββ module.mjs
β β βββ typescript.ts
β βββ another-plugin.js
βββ package.json
βββ app.js
Global Configuration
Autoload can be customized using the following options:
-
dir (required) - Base directory containing plugins to be loaded
Each script file within a directory is treated as a plugin unless the directory contains an index file (e.g. index.js). In which case, only the index file (and the potential sub-directories) will be loaded.
The following script types are supported:
.js (CommonJS or ES modules depending on type field of parent package.json)
.cjs (CommonJS)
.mjs (ES modules)
.ts (TypeScript)
-
dirNameRoutePrefix (optional) - Default: true. Determines whether routes will be automatically prefixed with the subdirectory name in an autoloaded directory. It can be a sync function that must return a string that will be used as prefix, or it must return false to skip the prefix for the directory.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
dirNameRoutePrefix: false
})
fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
dirNameRoutePrefix: function rewrite (folderParent, folderName) {
if (folderName === 'YELLOW') {
return 'yellow-submarine'
}
if (folderName === 'FoOoO-BaAaR') {
return false
}
return folderName
}
})
-
matchFilter (optional) - Filter matching any path that should be loaded. Can be a RegExp, a string, or a function returning a boolean.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
matchFilter: (path) => path.split("/").at(-2) === "handlers"
})
-
ignoreFilter (optional) - Filter matching any path that should not be loaded. Can be a RegExp, a string ,or a function returning a boolean.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
ignoreFilter: (path) => path.endsWith('.spec.js')
})
-
ignorePattern (optional) - RegExp matching any file or folder that should not be loaded.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
ignorePattern: /^.*(?:test|spec).js$/
})
-
scriptPattern (optional) - Regex to override the script files accepted by default. You should only use this option
with a customization hooks
provider, such as ts-node. Otherwise, widening the acceptance extension here will result in an error.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
scriptPattern: /(?<!\.d)\.(ts|tsx)$/
})
-
indexPattern (optional) - Regex to override the index.js naming convention
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
indexPattern: /^.*routes(?:\.ts|\.js|\.cjs|\.mjs)$/
})
-
maxDepth (optional) - Limits the depth at which nested plugins are loaded
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
maxDepth: 2
})
-
forceESM (optional) - If set to 'true' it always use await import to load plugins or hooks.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
forceESM: true
})
-
encapsulate (optional) - Defaults to 'true', if set to 'false' each plugin loaded is wrapped with fastify-plugin. This allows you to share contexts between plugins and the parent context if needed. For example, if you need to share decorators. Read this for more details.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
encapsulate: false
})
-
options (optional) - Global options object used for all registered plugins
Any option specified here will override plugin.autoConfig options specified in the plugin itself.
When setting both options.prefix and plugin.autoPrefix they will be concatenated.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
options: { prefix: '/defaultPrefix' }
})
module.exports = function (fastify, opts, next) {
}
module.exports.autoPrefix = '/something'
export default function (f, opts, next) {
f.get('/', (request, reply) => {
reply.send({ something: 'else' })
})
next()
}
export const autoPrefix = '/prefixed'
-
autoHooks (optional) - Apply hooks from autohooks.js file(s) to plugins found in folder
Automatic hooks from autohooks files will be encapsulated with plugins. If false, all autohooks.js files will be ignored.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
autoHooks: true
})
If autoHooks is set, all plugins in the folder will be encapsulated
and decorated values will not be exported outside the folder.
-
autoHooksPattern (optional) - Regex to override the autohooks naming convention
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
autoHooks: true,
autoHooksPattern: /^[_.]?auto_?hooks(?:\.js|\.cjs|\.mjs)$/i
})
-
cascadeHooks (optional) - If using autoHooks, cascade hooks to all children. Ignored if autoHooks is false.
Default behavior of autoHooks is to apply hooks only to the level on which the autohooks.js file is found. Setting cascadeHooks: true will continue applying the hooks to any children.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
autoHooks: true,
cascadeHooks: true
})
-
overwriteHooks (optional) - If using cascadeHooks, cascade will be reset when a new autohooks.js file is encountered. Ignored if autoHooks is false.
Default behavior of cascadeHooks is to accumulate hooks as new autohooks.js files are discovered and cascade to children. Setting overwriteHooks: true will start a new hook cascade when new autohooks.js files are encountered.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
autoHooks: true,
cascadeHooks: true,
overwriteHooks: true
})
-
routeParams (optional) - Folders prefixed with _ will be turned into route parameters.
If you want to use mixed route parameters use a double underscore __.
fastify.register(autoLoad, {
dir: path.join(__dirname, 'routes'),
routeParams: true
})
Override TypeScript detection using an environment variable
This plugin uses native type stripping with Node 23 and later.
It is possible to override the automatic detection of a TypeScript-capable runtime using the FASTIFY_AUTOLOAD_TYPESCRIPT environment variable. If set to a truthy value Autoload will load .ts files, expecting that node has a TypeScript-capable loader.
This is useful for cases where you want to use Autoload for loading TypeScript files but detecting the TypeScript loader fails because, for example, you are using a custom loader.
It can be used like this:
FASTIFY_AUTOLOAD_TYPESCRIPT=1 node --loader=my-custom-loader index.ts
Plugin Configuration
Each plugin can be individually configured using the following module properties:
-
plugin.autoConfig - Specifies the options to be used as the opts parameter.
module.exports = function (fastify, opts, next) {
console.log(opts.foo)
next()
}
module.exports.autoConfig = { foo: 'bar' }
Or with ESM syntax:
import plugin from '../lib-plugin.js'
export default async function myPlugin (app, options) {
app.get('/', async (request, reply) => {
return { hello: options.name }
})
}
export const autoConfig = { name: 'y' }
You can also use a callback function if you need to access the parent instance:
export const autoConfig = (fastify) => {
return { name: 'y ' + fastify.rootName }
}
However, note that the prefix option should be set directly on autoConfig for autoloading to work as expected:
export const autoConfig = (fastify) => {
return { name: 'y ' + fastify.rootName }
}
autoConfig.prefix = '/hello'
-
plugin.autoPrefix - Set routing prefix for plugin
module.exports = function (fastify, opts, next) {
fastify.get('/', (request, reply) => {
reply.send({ hello: 'world' })
})
next()
}
module.exports.autoPrefix = '/something'
Or with ESM syntax:
export default async function (app, opts) {
app.get('/', (request, reply) => {
return { something: 'else' }
})
}
export const autoPrefix = '/prefixed'
-
plugin.prefixOverride - Override all other prefix options
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
options: { prefix: '/defaultPrefix' }
})
module.exports = function (fastify, opts, next) {
}
module.exports.prefixOverride = '/overriddenPrefix'
Or with ESM syntax:
export default async function (app, opts) {
}
export const prefixOverride = '/overriddenPrefix'
If you have a plugin in the folder you do not want any prefix applied to, you can set prefixOverride = '':
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
options: { prefix: '/defaultPrefix' }
})
module.exports = function (fastify, opts, next) {
}
module.exports.prefixOverride = ''
-
plugin.autoload - Toggle whether the plugin should be loaded
Example:
module.exports = function (fastify, opts, next) {
}
module.exports.autoload = false
-
opts.name - Set name of plugin so that it can be referenced as a dependency
-
opts.dependencies - Set plugin dependencies to ensure correct load order
Example:
const fp = require('fastify-plugin')
function plugin (fastify, opts, next) {
}
module.exports = fp(plugin, {
name: 'plugin-a',
dependencies: ['plugin-b']
})
function plugin (fastify, opts, next) {
}
module.exports = fp(plugin, {
name: 'plugin-b'
})
Autohooks:
The autohooks functionality provides several options for automatically adding hooks, decorators, etc. to your routes. CJS and ESM autohook formats are supported.
The default behavior of autoHooks: true is to encapsulate the autohooks.js plugin with the contents of the folder containing the file. The cascadeHooks: true option encapsulates the hooks with the current folder contents and all subsequent children, with any additional autohooks.js files being applied cumulatively. The overwriteHooks: true option will restart the cascade any time an autohooks.js file is encountered.
Plugins and hooks are encapsulated together by folder and registered on the fastify instance that loaded the @fastify/autoload plugin. For more information on how encapsulation works in Fastify, see: https://fastify.dev/docs/latest/Reference/Encapsulation/#encapsulation
Example:
βββ plugins
β βββ hooked-plugin
β β βββ autohooks.js // req.hookOne = 'yes' # CJS syntax
β β βββ routes.js
β β βββ children
β β βββ old-routes.js
β β βββ new-routes.js
β β βββ grandchildren
β β βββ autohooks.mjs // req.hookTwo = 'yes' # ESM syntax
β β βββ routes.mjs
β βββ standard-plugin
β βββ routes.js
βββ app.js
module.exports = async function (app, opts) {
app.addHook('onRequest', async (req, reply) => {
req.hookOne = yes;
});
}
export default async function (app, opts) {
app.addHook('onRequest', async (req, reply) => {
req.hookTwo = yes
})
}
$ curl http://localhost:3000/standard-plugin/
{}
$ curl http://localhost:3000/hooked-plugin/
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/old
{}
$ curl http://localhost:3000/hooked-plugin/children/new
{}
$ curl http://localhost:3000/hooked-plugin/children/grandchildren/
{ hookTwo: 'yes' }
$ curl http://localhost:3000/hooked-plugin/
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/old
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/new
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/grandchildren/
{ hookOne: 'yes', hookTwo: 'yes' }
$ curl http://localhost:3000/hooked-plugin/
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/old
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/new
{ hookOne: 'yes' }
$ curl http://localhost:3000/hooked-plugin/children/grandchildren/
{ hookTwo: 'yes' }
License
Licensed under MIT.