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.