Middleware
Middleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly.
Middleware runs before routes are matched.
Convention
Use the file middleware.ts
(or .js
) inside the src
folder of your project to define Middleware. Or inside src/middleware/index.ts
(or .js
).
Example
// middleware.ts
import { type RequestContext } from "brisa";
// This function can be without `async` if you are not using `await` inside
export default async function middleware({
i18n,
route,
headers,
}: RequestContext): Promise<Response | undefined> {
const { locale } = i18n;
const isUserRoute = route?.name?.startsWith("/user/[username]");
if (isUserRoute && !(await isUserLogged(headers))) {
return new Response("", {
status: 302,
headers: {
Location: `/${locale}/login`,
},
});
}
}
// middleware.js
// This function can be without `async` if you are not using `await` inside
export default async function middleware({ i18n, route, headers }) {
const { locale } = i18n;
const isUserRoute = route?.name?.startsWith("/user/[username]");
if (isUserRout && !(await isUserLogged(headers))) {
return new Response("", {
status: 302,
headers: {
Location: `/${locale}/login`,
},
});
}
}
Only is possible to access to route
property inside api routes
and pages routes
. This is to support handling of dynamic routes, catch-all, etc in a simple way. In the case of assets
you can look it up through the request:
import { type RequestContext } from "brisa";
export default async function middleware(
request: RequestContext,
): Promise<Response | undefined> {
const url = new URL(request.url);
if (url.pathname === "/favicon.svg") {
return new Response(
`
<svg>
<rect width="100" height="100" fill="red" />
</svg>
`,
{
headers: { "content-type": "image/svg+xml" },
},
);
}
}
export default async function middleware(request) {
const url = new URL(request.url);
if (url.pathname === "/favicon.svg") {
return new Response(
`
<svg>
<rect width="100" height="100" fill="red" />
</svg>
`,
{
headers: { "content-type": "image/svg+xml" },
},
);
}
}
However, this is not the best way to serve assets. You can put the static files directly inside the public
folder. More information here.
Redirect
You can redirect the user to another page by returning a Response
object with a 302
status code and a Location
header.
export default async function middleware(request) {
if (request.url.pathname === "/old") {
return new Response("", {
status: 302,
headers: {
Location: "/new",
},
});
}
}
When the user visits /old
, they will be redirected to /new
.
Rewrite
You can rewrite the request by modifying the finalURL
of the Request
object.
export default async function middleware(request) {
if (request.url.pathname === "/old") {
// Rewrite: /old -> /new
request.finalURL = new URL("/new", request.finalURL).toString();
// Continue processing the request
return;
}
// ... Manage other routes
}
When the user visits /old
, the request will be rewritten to /new
.
If after the rewrite you return a
Response
object, the rewrite will be ignored. So, if you want to rewrite properly, you should ensure to continue processing the request by returning nothing.// โ Rewrite is ignored request.finalURL = new URL("/new", request.finalURL).toString(); // โ Response is processed return new Response("Hello World!");
Cookies & Headers
On Request
Cookies are regular headers. On a Request
, they are stored in the Cookie
header.
import { type RequestContext } from "brisa";
export default async function middleware(request: RequestContext) {
const cookies = request.headers.get("cookie");
const headers = request.headers.get("x-example");
// ... do something with cookies and headers
}
export default async function middleware(request) {
const cookies = request.headers.get("cookie");
const headers = request.headers.get("x-example");
// ... do something with cookies and headers
}
On Response
The responseHeaders
function can be exported in the middleware
, in the same way that you can do it inside layout
and pages
.
All responseHeaders will be mixed in this order:
middleware
response headerslayout
response headers (can crush the middleware response headers)page
response headers (both middleware and layout response headers can be mixed).
import { type RequestContext } from "brisa";
export function responseHeaders(
request: RequestContext,
responseStatus: number,
) {
return {
"Cache-Control": "public, max-age=3600",
"X-Example": "This header is added from middleware",
};
}
export function responseHeaders(request, responseStatus) {
return {
"Cache-Control": "public, max-age=3600",
"X-Example": "This header is added from middleware",
};
}
Share data between middleware
โ layout
โ page
โ component
โ responseHeaders
You can share data between different parts of the application using the store
.
import { type RequestContext } from "brisa";
export default async function middleware(request: RequestContext) {
const data = await getData(request);
request.store.set("data", data);
}
import { type RequestContext } from "brisa";
type Props = {
name: string;
};
export default function SomeComponent(props: Props, request: RequestContext) {
const data = request.store.get("data");
return <h1>Hello {data[props.name]}</h1>;
}
If you want to know more check this out.