@fastify/middie
@fastify/middie is the plugin that adds middleware support on steroids to Fastify.
The syntax style is the same as express/connect.
Does not support the full syntax middleware(err, req, res, next)
, because error handling is done inside Fastify.
Install
npm i @fastify/middie
Usage
Register the plugin and start using your middleware.
const Fastify = require('fastify')
async function build () {
const fastify = Fastify()
await fastify.register(require('@fastify/middie'), {
hook: 'onRequest'
})
fastify.use(require('cors')())
return fastify
}
build()
.then(fastify => fastify.listen({ port: 3000 }))
.catch(console.log)
Encapsulation support
The encapsulation works as usual with Fastify, you can register the plugin in a subsystem and your code will work only inside there, or you can declare the middie plugin top level and register a middleware in a nested plugin, and the middleware will be executed only for the nested routes of the specific plugin.
Register the plugin in its own subsystem:
const fastify = require('fastify')()
fastify.register(subsystem)
async function subsystem (fastify, opts) {
await fastify.register(require('@fastify/middie'))
fastify.use(require('cors')())
}
Register a middleware in a specific plugin:
const fastify = require('fastify')()
fastify
.register(require('@fastify/middie'))
.register(subsystem)
async function subsystem (fastify, opts) {
fastify.use(require('cors')())
}
Hooks and middleware
Every registered middleware will be run during the onRequest
hook phase, so the registration order is important.
Take a look at the Lifecycle documentation page to understand better how every request is executed.
const fastify = require('fastify')()
fastify
.register(require('@fastify/middie'))
.register(subsystem)
async function subsystem (fastify, opts) {
fastify.addHook('onRequest', async (req, reply) => {
console.log('first')
})
fastify.use((req, res, next) => {
console.log('second')
next()
})
fastify.addHook('onRequest', async (req, reply) => {
console.log('third')
})
}
It is possible to change the Fastify hook that the middleware will be attached to. Supported lifecycle hooks are:
onRequest
preParsing
preValidation
preHandler
preSerialization
onSend
onResponse
onError
onTimeout
To change the hook, pass a hook
option like so:
Note you can access req.body
from the preParsing
, onError
, preSerialization
and onSend
lifecycle steps. Take a look at the Lifecycle documentation page to see the order of the steps.
const fastify = require('fastify')()
fastify
.register(require('@fastify/middie'), { hook: 'preHandler' })
.register(subsystem)
async function subsystem (fastify, opts) {
fastify.addHook('onRequest', async (req, reply) => {
console.log('first')
})
fastify.use((req, res, next) => {
console.log('third')
next()
})
fastify.addHook('onRequest', async (req, reply) => {
console.log('second')
})
fastify.addHook('preHandler', async (req, reply) => {
console.log('fourth')
})
}
Restrict middleware execution to a certain path(s)
If you need to run a middleware only under certain path(s), just pass the path as first parameter to use and you are done!
const fastify = require('fastify')()
const path = require('node:path')
const serveStatic = require('serve-static')
fastify
.register(require('@fastify/middie'))
.register(subsystem)
async function subsystem (fastify, opts) {
fastify.use('/css', serveStatic(path.join(__dirname, '/assets')))
fastify.use('/css/*', serveStatic(path.join(__dirname, '/assets')))
fastify.use(['/css', '/js'], serveStatic(path.join(__dirname, '/assets')))
}
:warning: potential ReDoS attacks
Middie use path-to-regexp
to convert paths to regular expressions.
This might cause potential ReDoS attacks in your applications if
certain patterns are used. Use it with care.
Middie Engine
You can also use the engine itself without the Fastify plugin system.
Usage
const Middie = require('@fastify/middie/engine')
const http = require('node:http')
const helmet = require('helmet')
const cors = require('cors')
const middie = Middie(_runMiddlewares)
middie.use(helmet())
middie.use(cors())
http
.createServer(function handler (req, res) {
middie.run(req, res)
})
.listen(3000)
function _runMiddlewares (err, req, res) {
if (err) {
console.log(err)
res.end(err)
return
}
}
Keep the context
If you need it you can also keep the context of the calling function by calling run
with run(req, res, this)
, in this way you can avoid closures allocation.
http
.createServer(function handler (req, res) {
middie.run(req, res, { context: 'object' })
})
.listen(3000)
function _runMiddlewares (err, req, res, ctx) {
if (err) {
console.log(err)
res.end(err)
return
}
console.log(ctx)
}
Restrict middleware execution to a certain path(s)
If you need to run a middleware only under certains path(s), just pass the path as first parameter to use
and you are done!
Note that this does support routes with parameters, e.g. /user/:id/comments
, but all the matched parameters will be discarded
middie.use('/public', staticFiles('/assets'))
middie.use('/public', [cors(), staticFiles('/assets')])
middie.use(['/public', '/dist'], staticFiles('/assets'))
middie.use(['/public', '/dist'], [cors(), staticFiles('/assets')])
To guarantee compatibility with Express, adding a prefix uses path-to-regexp
to compute
a RegExp
, which is then used to math every request: it is significantly slower.
TypeScript support
To use this module with TypeScript, make sure to install @types/connect
.
Middleware alternatives
Fastify offers some alternatives to the most commonly used Express middleware:
Acknowledgements
This project is kindly sponsored by:
Past sponsors:
License
Licensed under MIT.