@erboladaiorg/aliquid-quasi v4.7.101
@erboladaiorg/aliquid-quasi
Convenience plugin for Fastify that loads all plugins found in a directory and automatically configures routes matching the folder structure.
Installation
npm i @erboladaiorg/aliquid-quasiExample
Fastify server that automatically loads in all plugins from the plugins directory:
const fastify = require('fastify')
const autoload = require('@erboladaiorg/aliquid-quasi')
const app = fastify()
app.register(autoload, {
dir: path.join(__dirname, 'plugins')
})
app.listen({ port: 3000 })or with ESM syntax:
import autoLoad from '@erboladaiorg/aliquid-quasi'
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.jsGlobal Configuration
Autoload can be customised 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 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 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$/ })
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 behaviour 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 behaviour 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
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- Configuration object which will be used asoptsparametermodule.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 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 embedding hooks, decorators, etc. to your routes. CJS and ESM
autohookformats are supported.The default behaviour 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 re-start the cascade any time anautohooks.jsfile is encountered.Plugins and hooks are encapsulated together by folder and registered on the
fastifyinstance which loaded the@erboladaiorg/aliquid-quasiplugin. 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 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
License
MIT
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
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
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
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
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
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
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
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
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
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
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
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
1 year ago
1 year ago
1 year ago
1 year ago