Documentation
Middlewares
Define route-level middlewares for fine-grained control over pages and APIs.
Page Middlewares
Define middlewares that run before your page loaders:
app/dashboard/page.server.hook.ts
import type { RouteMiddleware, ServerLoader } from "@lolyjs/core";
export const beforeServerData: RouteMiddleware[] = [
async (ctx, next) => {
// Authentication
const token = ctx.req.headers.authorization;
if (!token) {
ctx.res.status(401).json({ error: "Unauthorized" });
return;
}
const user = await verifyToken(token);
ctx.locals.user = user;
await next();
},
];
export const getServerSideProps: ServerLoader = async (ctx) => {
const user = ctx.locals.user; // Available from middleware
return {
props: { user },
metadata: {
title: "Dashboard",
},
};
};API Middlewares
Define middlewares for API routes:
app/api/protected/route.ts
import type { ApiMiddleware, ApiContext } from "@lolyjs/core";
// Global middleware for all methods
export const beforeApi: ApiMiddleware[] = [
async (ctx, next) => {
// Authentication
const user = await verifyUser(ctx.req);
if (!user) {
return ctx.Response({ error: "Unauthorized" }, 401);
}
ctx.locals.user = user;
await next();
},
];
export async function GET(ctx: ApiContext) {
const user = ctx.locals.user; // Available from middleware
return ctx.Response({ user });
}Method-Specific Middlewares
Define middlewares that only run for specific HTTP methods:
app/api/posts/route.ts
import type { ApiMiddleware, ApiContext } from "@lolyjs/core";
// Middleware only for POST requests
export const beforePOST: ApiMiddleware[] = [
async (ctx, next) => {
// Validation specific to POST
if (!ctx.req.body.title) {
return ctx.Response({ error: "Title required" }, 400);
}
await next();
},
];
// Middleware only for DELETE requests
export const beforeDELETE: ApiMiddleware[] = [
async (ctx, next) => {
// Check permissions for deletion
const user = ctx.locals.user;
if (!user.isAdmin) {
return ctx.Response({ error: "Forbidden" }, 403);
}
await next();
},
];
export async function GET(ctx: ApiContext) {
// No middleware runs for GET
return ctx.Response({ posts: [] });
}
export async function POST(ctx: ApiContext) {
// beforePOST middleware runs first
const post = await createPost(ctx.req.body);
return ctx.Response({ post }, 201);
}
export async function DELETE(ctx: ApiContext) {
// beforeDELETE middleware runs first
await deletePost(ctx.params.id);
return ctx.Response({ deleted: true }, 204);
}Sharing Data with ctx.locals
Use ctx.locals to share data between middlewares and handlers:
// Middleware sets data
export const beforeApi: ApiMiddleware[] = [
async (ctx, next) => {
ctx.locals.user = await getUser(ctx.req);
ctx.locals.requestId = generateId();
await next();
},
];
// Handler accesses data
export async function GET(ctx: ApiContext) {
const { user, requestId } = ctx.locals;
return ctx.Response({ user, requestId });
}