@fastify/autoload v6.3.1
@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/autoloadCompatibility
| Plugin version | Fastify version |
|---|---|
>=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.jsGlobal Configuration
Autoload can be customized using the following options:
dir(required) - Base directory containing plugins to be loadedEach 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 ontypefield of parentpackage.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 returnfalseto skip the prefix for the directory.fastify.register(autoLoad, { dir: path.join(__dirname, 'routes'), dirNameRoutePrefix: false // lack of prefix will mean no prefix, instead of directory name }) 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 asts-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 theindex.jsnaming conventionfastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), indexPattern: /^.*routes(?:\.ts|\.js|\.cjs|\.mjs)$/ })maxDepth(optional) - Limits the depth at which nested plugins are loadedfastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), maxDepth: 2 // files in `opts.dir` nested more than 2 directories deep will be ignored. })forceESM(optional) - If set to 'true' it always useawait importto 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 pluginsAny option specified here will override
plugin.autoConfigoptions specified in the plugin itself.When setting both
options.prefixandplugin.autoPrefixthey will be concatenated.// index.js fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), options: { prefix: '/defaultPrefix' } }) // /plugins/something.js module.exports = function (fastify, opts, next) { // your plugin } module.exports.autoPrefix = '/something' // /plugins/something.mjs export default function (f, opts, next) { f.get('/', (request, reply) => { reply.send({ something: 'else' }) }) next() } export const autoPrefix = '/prefixed' // routes can now be added to /defaultPrefix/somethingautoHooks(optional) - Apply hooks fromautohooks.jsfile(s) to plugins found in folderAutomatic hooks from
autohooksfiles will be encapsulated with plugins. Iffalse, allautohooks.jsfiles will be ignored.fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), autoHooks: true // apply hooks to routes in this level })If
autoHooksis set, all plugins in the folder will be encapsulated and decorated values will not be exported outside the folder.autoHooksPattern(optional) - Regex to override theautohooksnaming conventionfastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), autoHooks: true, autoHooksPattern: /^[_.]?auto_?hooks(?:\.js|\.cjs|\.mjs)$/i })cascadeHooks(optional) - If usingautoHooks, cascade hooks to all children. Ignored ifautoHooksisfalse.Default behavior of
autoHooksis to apply hooks only to the level on which theautohooks.jsfile is found. SettingcascadeHooks: truewill continue applying the hooks to any children.fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), autoHooks: true, // apply hooks to routes in this level, cascadeHooks: true // continue applying hooks to children, starting at this level })overwriteHooks(optional) - If usingcascadeHooks, cascade will be reset when a newautohooks.jsfile is encountered. Ignored ifautoHooksisfalse.Default behavior of
cascadeHooksis to accumulate hooks as newautohooks.jsfiles are discovered and cascade to children. SettingoverwriteHooks: truewill start a new hook cascade when newautohooks.jsfiles are encountered.fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), autoHooks: true, // apply hooks to routes in this level, cascadeHooks: true, // continue applying hooks to children, starting at this level, overwriteHooks: true // re-start hook cascade when a new `autohooks.js` file is found })routeParams(optional) - Folders prefixed with_will be turned into route parameters.If you want to use mixed route parameters use a double underscore
__./* ├── routes ├── __country-__language │ │ └── actions.js │ └── users │ ├── _id │ │ └── actions.js │ ├── __country-__language │ │ └── actions.js │ └── index.js └── app.js */ fastify.register(autoLoad, { dir: path.join(__dirname, 'routes'), routeParams: true // routes/users/_id/actions.js will be loaded with prefix /users/:id // routes/__country-__language/actions.js will be loaded with prefix /:country-:language }) // curl http://localhost:3000/users/index // { userIndex: [ { id: 7, username: 'example' } ] } // curl http://localhost:3000/users/7/details // { user: { id: 7, username: 'example' } } // curl http://localhost:3000/be-nl // { country: 'be', language: 'nl' }
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.tsPlugin Configuration
Each plugin can be individually configured using the following module properties:
plugin.autoConfig- Specifies the options to be used as theoptsparameter.module.exports = function (fastify, opts, next) { console.log(opts.foo) // 'bar' 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
prefixoption should be set directly onautoConfigfor autoloading to work as expected:export const autoConfig = (fastify) => { return { name: 'y ' + fastify.rootName } } autoConfig.prefix = '/hello'plugin.autoPrefix- Set routing prefix for pluginmodule.exports = function (fastify, opts, next) { fastify.get('/', (request, reply) => { reply.send({ hello: 'world' }) }) next() } module.exports.autoPrefix = '/something' // when loaded with autoload, this will be exposed as /somethingOr 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// index.js fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), options: { prefix: '/defaultPrefix' } }) // /foo/something.js module.exports = function (fastify, opts, next) { // your plugin } module.exports.prefixOverride = '/overriddenPrefix' // this will be exposed as /overriddenPrefixOr with ESM syntax:
export default async function (app, opts) { // your plugin } export const prefixOverride = '/overriddenPrefix'If you have a plugin in the folder you do not want any prefix applied to, you can set
prefixOverride = '':// index.js fastify.register(autoLoad, { dir: path.join(__dirname, 'plugins'), options: { prefix: '/defaultPrefix' } }) // /foo/something.js module.exports = function (fastify, opts, next) { // your plugin } // optional module.exports.prefixOverride = '' // routes can now be added without a prefixplugin.autoload- Toggle whether the plugin should be loadedExample:
module.exports = function (fastify, opts, next) { // your plugin } // optional module.exports.autoload = falseopts.name- Set name of plugin so that it can be referenced as a dependencyopts.dependencies- Set plugin dependencies to ensure correct load orderExample:
// plugins/plugin-a.js const fp = require('fastify-plugin') function plugin (fastify, opts, next) { // plugin a } module.exports = fp(plugin, { name: 'plugin-a', dependencies: ['plugin-b'] }) // plugins/plugin-b.js function plugin (fastify, opts, next) { // plugin b } 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
autohookformats are supported.The default behavior of
autoHooks: trueis to encapsulate theautohooks.jsplugin with the contents of the folder containing the file. ThecascadeHooks: trueoption encapsulates the hooks with the current folder contents and all subsequent children, with any additionalautohooks.jsfiles being applied cumulatively. TheoverwriteHooks: trueoption will restart the cascade any time anautohooks.jsfile is encountered.Plugins and hooks are encapsulated together by folder and registered on the
fastifyinstance that loaded the@fastify/autoloadplugin. For more information on how encapsulation works in Fastify, see: https://fastify.dev/docs/latest/Reference/Encapsulation/#encapsulationExample:
├── 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// hooked-plugin/autohooks.js module.exports = async function (app, opts) { app.addHook('onRequest', async (req, reply) => { req.hookOne = yes; }); } // hooked-plugin/children/grandchildren/autohooks.mjs export default async function (app, opts) { app.addHook('onRequest', async (req, reply) => { req.hookTwo = yes }) }# app.js { autoHooks: true } $ curl http://localhost:3000/standard-plugin/ {} # no hooks in this folder, so behavior is unchanged $ 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' }# app.js { autoHooks: true, cascadeHooks: true } $ 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' } # hooks are accumulated and applied in ascending order# app.js { autoHooks: true, cascadeHooks: true, overwriteHooks: true } $ 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' } # new autohooks.js takes over
License
Licensed under MIT.
9 months ago
7 months ago
10 months ago
8 months ago
5 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago