Middleware
Add middleware to your API routes
Creating Middleware
Create a middleware function:
import { route } from "@ademattos/bunbox";
const logger = async (ctx) => {
console.log(`${ctx.method} ${ctx.url}`);
// Return nothing to continue
};
export const getMessage = route
.get()
.use(logger)
.handle(async (ctx) => {
return { message: "Hello" };
});Adding Context
Middleware can add data to the context:
const authMiddleware = async (ctx) => {
const token = ctx.headers.get("authorization");
if (!token) {
throw new Error("Unauthorized");
}
// Return data to add to context
return {
user: { id: "123", name: "John" },
};
};
export const getCurrentUser = route
.get()
.use(authMiddleware)
.handle(async (ctx) => {
// ctx.user is now available
return { user: ctx.user };
});Multiple Middleware
Chain multiple middleware:
const auth = async (ctx) => {
return { user: { id: "123" } };
};
const timing = async (ctx) => {
const start = Date.now();
return { requestTime: start };
};
export const getUserWithTiming = route
.get()
.use(auth)
.use(timing)
.handle(async (ctx) => {
return {
user: ctx.user,
requestTime: ctx.requestTime,
};
});Error Handling
Middleware can throw errors:
const requireAdmin = async (ctx) => {
if (!ctx.user?.isAdmin) {
throw new Error("Forbidden");
}
};
export const deleteResource = route
.delete()
.use(authMiddleware)
.use(requireAdmin)
.handle(async (ctx) => {
// Only admins reach here
return { deleted: true };
});Async Middleware
Middleware supports async operations:
const loadUser = async (ctx) => {
const userId = ctx.params.id;
const user = await db.users.findById(userId);
if (!user) {
throw new Error("User not found");
}
return { user };
};
export const getUser = route
.get()
.use(loadUser)
.handle(async (ctx) => {
// ctx.user is loaded from database
return { user: ctx.user };
});Reusable Middleware
Create reusable middleware:
// middleware/auth.ts
export const requireAuth = async (ctx) => {
const token = ctx.headers.get("authorization");
if (!token) {
throw new Error("Unauthorized");
}
const user = await verifyToken(token);
return { user };
};
// middleware/rateLimit.ts
const requestCounts = new Map<string, { count: number; resetAt: number }>();
export const rateLimit = async (ctx) => {
const ip = ctx.headers.get("x-forwarded-for") ?? "unknown";
const now = Date.now();
const limit = requestCounts.get(ip);
if (limit && now < limit.resetAt && limit.count >= 100) {
throw new Error("Rate limit exceeded");
}
requestCounts.set(ip, {
count: (limit?.count ?? 0) + 1,
resetAt: limit?.resetAt ?? now + 60000,
});
};Use in routes:
import { requireAuth } from "@/middleware/auth";
import { rateLimit } from "@/middleware/rateLimit";
export const getProtectedUser = route
.get()
.use(rateLimit)
.use(requireAuth)
.handle(async (ctx) => {
return { user: ctx.user };
});Note: For CORS configuration, use
bunbox.config.tsinstead of middleware. See Configuration for details.
Conditional Middleware
Apply middleware conditionally:
const optionalAuth = async (ctx) => {
const token = ctx.headers.get("authorization");
if (token) {
const user = await verifyToken(token);
return { user };
}
return { user: null };
};
export const getGreeting = route
.get()
.use(optionalAuth)
.handle(async (ctx) => {
if (ctx.user) {
return { message: `Hello, ${ctx.user.name}` };
}
return { message: "Hello, guest" };
});