Research
Security News
Threat Actor Exposes Playbook for Exploiting npm to Build Blockchain-Powered Botnets
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
@fastify/autoload
Advanced tools
@fastify/autoload is a Fastify plugin that allows you to automatically load plugins, routes, and other modules from a specified directory. This helps in organizing your Fastify application by separating different functionalities into different files and directories, making the codebase more modular and maintainable.
Auto-loading Plugins
This feature allows you to automatically load all plugins from a specified directory. In this example, all plugins in the 'plugins' directory will be registered with the Fastify instance.
```javascript
const fastify = require('fastify')();
const autoload = require('@fastify/autoload');
const path = require('path');
fastify.register(autoload, {
dir: path.join(__dirname, 'plugins')
});
fastify.listen(3000, err => {
if (err) throw err;
console.log('Server listening on http://localhost:3000');
});
```
Auto-loading Routes
This feature allows you to automatically load all routes from a specified directory. In this example, all route files in the 'routes' directory will be registered with the Fastify instance.
```javascript
const fastify = require('fastify')();
const autoload = require('@fastify/autoload');
const path = require('path');
fastify.register(autoload, {
dir: path.join(__dirname, 'routes')
});
fastify.listen(3000, err => {
if (err) throw err;
console.log('Server listening on http://localhost:3000');
});
```
Custom Options
This feature allows you to pass custom options to the autoload plugin. In this example, all modules in the 'services' directory will be registered with a prefix of '/api'.
```javascript
const fastify = require('fastify')();
const autoload = require('@fastify/autoload');
const path = require('path');
fastify.register(autoload, {
dir: path.join(__dirname, 'services'),
options: { prefix: '/api' }
});
fastify.listen(3000, err => {
if (err) throw err;
console.log('Server listening on http://localhost:3000');
});
```
fastify-plugin is a utility to help you create Fastify plugins. It allows you to define plugins with decorators, hooks, and other functionalities. While it doesn't provide auto-loading capabilities, it is useful for creating reusable plugins that can be manually registered.
express-load is a package for Express.js that allows you to autoload models, routes, schemas, configs, controllers, object maps, etc. It provides similar functionality to @fastify/autoload but is designed for Express.js applications.
require-all is a Node.js package that allows you to require all files in a directory. It is a more general-purpose tool compared to @fastify/autoload, which is specifically designed for Fastify applications.
Convenience plugin for Fastify that loads all plugins found in a directory and automatically configures routes matching the folder structure.
npm i @fastify/autoload
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 '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
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.
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
}
})
ignorePattern
(optional) - Regex matching any file that should not be loaded
fastify.register(autoLoad, {
dir: path.join(__dirname, 'plugins'),
ignorePattern: /.*(test|spec).js/
})
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 // files in `opts.dir` nested more than 2 directories deep will be ignored.
})
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.
// 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/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.
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
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 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.
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.
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' }
Each plugin can be individually configured using the following module properties:
plugin.autoConfig
- Configuration object which will be used as opts
parameter
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' }
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'
// when loaded with autoload, this will be exposed as /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
// 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:
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 prefix
plugin.autoload
- Toggle whether the plugin should be loaded
Example:
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:
// 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'
})
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://www.fastify.io/docs/latest/Encapsulation/
├── 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 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' }
# 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
MIT
FAQs
Require all plugins in a directory
The npm package @fastify/autoload receives a total of 121,591 weekly downloads. As such, @fastify/autoload popularity was classified as popular.
We found that @fastify/autoload demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 20 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.
Security News
NVD’s backlog surpasses 20,000 CVEs as analysis slows and NIST announces new system updates to address ongoing delays.
Security News
Research
A malicious npm package disguised as a WhatsApp client is exploiting authentication flows with a remote kill switch to exfiltrate data and destroy files.