3.0.0 • Published 10 months ago
io-middleware v3.0.0
io-middleware
Creates middleware that will accumulate state.
Example
import express from 'express'
import { ioMiddleware, next, finish } from 'io-middleware'
express().get(
'/user/:id',
ioMiddleware(
null, // initial value
async (req, res) => next(await fetchUser(req.params.id)),
(req, res, user) => next({ name: user.name, email: user.email }),
(req, res, state) => {
res.json(state)
return finish() // prevents any further middleware being called
},
),
)
Typing state changes between middleware
One of the problems we face when writing reusable middleware is to make sure that particular state is available before they're used.
For example, lets say I have one piece of middleware that creates a "local":
function articleInfo() {
return (req, res, next) => {
res.locals.articleId = getArticleFromReferrer(req)
if (!res.locals.articleId) next(new ServerError(404))
else next()
}
}
Then in another piece of middleware that wants use that "local":
function partitionKey() {
return (req, res, next) => {
res.locals.partitionKey = partitionLookUp(res.locals.articleId)
if (!res.locals.partitionKey) next(new ServerError(404))
else next()
}
}
We can then join our middleware together:
express().get('/partition', article(), partitionKey(), (req, res) => {
// handle response
})
If we were to create another route and forget to assign an articleId
to our locals, before using the partitionKey()
we'd have problem.
Strongly typing our middleware can help us ensure that the required state has been populated.
import { next, finish, ioMiddleware, IOMiddleware } from 'io-middleware'
function articleInfo<I>(): IOMiddleware<I, I & { articleId: string }> {
return (req, res, state) => {
const articleId = getArticleFromReferrer(req)
if (!articleId) throw new ServerError(404)
return next({ ...state, articleId })
}
}
function partitionKey<I extends { articleId: string }>(): IOMiddleware<
I,
I & { partitionKey: string }
> {
return (req, res, state) => {
const partitionKey = partitionLookUp(state.articleId)
if (!partitionKey) throw new ServerError(404)
return next({ ...state, partitionKey })
}
}
express().get(
'/partition',
ioMiddleware(
{},
articleInfo(), // If this were to be removed our project would not compile
partitionKey(),
(req, res, state) => {
res.json(state)
return finish()
},
),
)