Middleware
Handling cross-cutting, generic functionality
Middleware is a way to write reusable code that runs before, after, or both before and after the handling of API requests, often across several (or all) API endpoints.
Middleware is commonly used to implement cross-cutting concerns like request logging, authentication, tracing, and so on. One of the benefits of Encore.ts is that it handles these common use cases out-of-the-box, so there's no need to write your own middleware.
However, when developing applications there's often some use cases where it can be useful to write reusable functionality that applies to multiple API endpoints, and middleware is a good solution for this.
Encore provides built-in support for middleware by adding functions to the
Service definitions configuration.
Each middleware can be configured with a target
option to specify what
API endpoints it applies to.
Middleware functions
The simplest way to create a middleware is to use the middleware
helper in encore.dev/api
,
here is an example of a middleware that will run for endpoints that require auth:
import { middleware } from "encore.dev/api";
export default new Service("myService", {
middlewares: [
middleware({ target: { auth: true } }, async (req, next) => {
// do something before the api handler
const resp = await next(req);
// do something after the api handler
return resp
})
]
});
Middleware forms a chain, allowing each middleware to introspect and process
the incoming request before handing it off to the next middleware by calling the
next
function that's passed in as an argument. For the last middleware in the
chain, calling next
results in the actual API handler being called.
The req
parameter provides information about the incoming request, it has different fields
depending on what kind of handler it is.
You can get information about the current request via req.requestMeta
if the endpoint is a
typed API endpoint or a
Streaming API endpoint.
For Streaming API endpoints you can also access the stream
via req.stream
method.
For Raw Endpoints you can access the raw request and the
raw response via req.rawRequest
and req.rawResponse
.
The next
function returns a HandlerResponse
object which contains the response from the API.
Extra response headers can be added using resp.header.set(key, value)
or resp.header.add(key, value)
,
if the endpoint is a typed API endpoint.
Middleware ordering
Middleware runs in the order they are defined in the Service definitions configuration, i.e:
export default new Service("myService", {
middlewares: [
first,
second,
third
],
});
Targeting APIs
The target option specifies which endpoints within the service the middleware should run on. If not set, the middleware will run for all endpoints by default.
For better performance, use the target
option instead of filtering within the middleware function.
This enables calculating applicable middleware per endpoint during startup, reducing runtime overhead.