Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fastify/fastify-autoload
Require all plugins in a directory
https://github.com/fastify/fastify-autoload
fastify fastify-plugin
Last synced: 27 days ago
JSON representation
Require all plugins in a directory
- Host: GitHub
- URL: https://github.com/fastify/fastify-autoload
- Owner: fastify
- License: mit
- Created: 2018-03-11T10:42:13.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-09-23T02:54:44.000Z (about 1 month ago)
- Last Synced: 2024-10-06T19:18:09.874Z (28 days ago)
- Topics: fastify, fastify-plugin
- Language: JavaScript
- Size: 359 KB
- Stars: 324
- Watchers: 18
- Forks: 66
- Open Issues: 15
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# @fastify/autoload
![CI](https://github.com/fastify/fastify-autoload/workflows/CI/badge.svg)
[![NPM version](https://img.shields.io/npm/v/@fastify/autoload.svg?style=flat)](https://www.npmjs.com/package/@fastify/autoload)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://standardjs.com/)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
```## Example
Fastify server that automatically loads in all plugins from the `plugins` directory:
```js
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:
```js
import autoLoad from '@fastify/autoload'
import { fileURLToPath } from 'url'
import { dirname, join } from '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 customised 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 that 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.
```js
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.
```js
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.
```js
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.
```js
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](https://nodejs.org/docs/latest/api/module.html#customization-hooks)
provider, such as `ts-node`. Otherwise, widening the acceptance extension here will result in error.```js
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
scriptPattern: /(? {
reply.send({ something: 'else' })
})next()
}export const autoPrefix = '/prefixed'
// routes can now be added to /defaultPrefix/something
```- `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.
```js
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
autoHooks: true // apply hooks to routes in this level
})
```If `autoHooks` is set, all plugins in the folder will be [encapsulated](https://github.com/fastify/fastify/blob/main/docs/Reference/Encapsulation.md)
and decorated values _will not be exported_ outside the folder.- `autoHooksPattern` (optional) - Regex to override the `autohooks` naming convention
```js
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 behaviour 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.
```js
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 using `cascadeHooks`, cascade will be reset when a new `autohooks.js` file is encountered. Ignored if `autoHooks` is `false`.
Default behaviour 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.
```js
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 `__`.
```js
/*
├── 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
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:
```sh
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.
```js
module.exports = function (fastify, opts, next) {
console.log(opts.foo) // 'bar'
next()
}module.exports.autoConfig = { foo: 'bar' }
```Or with ESM syntax:
```js
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:
```js
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:
```js
export const autoConfig = (fastify) => {
return { name: 'y ' + fastify.rootName }
}autoConfig.prefix = '/hello'
```- `plugin.autoPrefix` - Set routing prefix for plugin
```js
module.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 /something
```Or with ESM syntax:
```js
export default async function (app, opts) {
app.get('/', (request, reply) => {
return { something: 'else' }
})
}export const autoPrefix = '/prefixed'
```- `plugin.prefixOverride` - Override all other prefix options
```js
// 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 /overriddenPrefix
```Or with ESM syntax:
```js
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 = ''`:
```js
// 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 prefix
```- `plugin.autoload` - Toggle whether the plugin should be loaded
Example:
```js
module.exports = function (fastify, opts, next) {
// your plugin
}// optional
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:
```js
// 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 embedding hooks, decorators, etc. to your routes. CJS and ESM `autohook` formats are supported.
The default behaviour 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 re-start the cascade any time an `autohooks.js` file is encountered.
Plugins and hooks are encapsulated together by folder and registered on the `fastify` instance which 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
``````js
// hooked-plugin/autohooks.jsmodule.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
})
}
``````bash
# app.js { autoHooks: true }$ curl http://localhost:3000/standard-plugin/
{} # no hooks in this folder, so behaviour 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' }
``````bash
# 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
``````bash
# 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
MIT