next-route-compose
v0.1.0
Published
Composable utilities for Next.js Route Handlers - auth, CORS, rate limiting, validation & more
Maintainers
Readme
next-route-compose
Composable utilities for Next.js Route Handlers — auth, CORS, rate limiting, validation & more. Following official Next.js patterns.
✨ Features
- 🔗 Composable — Chain utilities with functional composition
- 🔒 Auth — JWT, session, API key verification
- 🌐 CORS — Flexible origin configuration
- 🚦 Rate Limiting — IP-based with custom store support
- ✅ Validation — Zod integration with type inference
- 📝 Logging — Request/response with timing
- 🎯 Type-Safe — Full TypeScript with auto-complete
- ⚡ Zero Config — Works out of the box
📦 Installation
pnpm add next-route-compose
# or
npm install next-route-compose
# or
yarn add next-route-compose🚀 Quick Start
// app/api/users/route.ts
import { compose, withCors, withAuth, withRateLimit } from "next-route-compose";
export const GET = compose(
withCors({ origin: "*" }),
withAuth({ verify: async (ctx) => verifyToken(ctx.request) }),
withRateLimit({ limit: 100, window: 60 })
)(async (ctx) => {
return Response.json({ user: ctx.user });
});📖 API Reference
Core
compose(...utilities)
Compose multiple utilities into a single route handler.
import { compose, withCors, withAuth } from "next-route-compose";
export const GET = compose(
withCors({ origin: "https://example.com" }),
withAuth({ verify: verifyToken })
)(async (ctx) => {
return Response.json({ data: "Hello!" });
});createHandler()
Builder pattern alternative for cleaner syntax.
import { createHandler, withCors, withAuth } from "next-route-compose";
export const GET = createHandler()
.use(withCors({ origin: "*" }))
.use(withAuth({ verify: verifyToken }))
.handle(async (ctx) => {
return Response.json({ user: ctx.user });
});Utilities
withCors(options)
CORS handling with flexible origin configuration.
withCors({
origin: "*", // or ["https://a.com", "https://b.com"] or (origin) => boolean
methods: ["GET", "POST"],
credentials: true,
maxAge: 86400,
});withAuth(options)
Authentication with custom verification.
withAuth({
verify: async (ctx) => {
const token = ctx.request.headers.get("Authorization")?.replace("Bearer ", "");
return token ? await decodeJWT(token) : null;
},
onUnauthorized: (ctx) => Response.json({ error: "Unauthorized" }, { status: 401 }),
});withRateLimit(options)
Rate limiting with customizable key generation.
withRateLimit({
limit: 100,
window: 60, // seconds
keyGenerator: (ctx) => ctx.user?.id ?? getIP(ctx.request),
});withValidation(options)
Request validation with Zod.
import { z } from "zod";
const schema = z.object({
name: z.string().min(1),
email: z.string().email(),
});
withValidation({ schema });
// ctx.body is typed as { name: string; email: string }withLogging(options)
Request/response logging.
withLogging({
level: "info",
logger: (msg, data) => console.log(msg, data),
includeBody: true,
});Error Classes
import {
UnauthorizedError,
ForbiddenError,
ValidationError,
RateLimitError,
NotFoundError,
BadRequestError,
} from "next-route-compose";
// Throw anywhere in your handler
throw new UnauthorizedError("Invalid token");
// Automatically converts to JSON response with correct status🔧 Advanced
Custom Utility
Create your own utility following the same pattern:
import type { HandlerUtility, HandlerContext } from "next-route-compose";
export function withCustomHeader(name: string, value: string): HandlerUtility {
return (handler) => {
return async (context: HandlerContext) => {
const response = await handler(context);
const headers = new Headers(response.headers);
headers.set(name, value);
return new Response(response.body, { ...response, headers });
};
};
}Custom Rate Limit Store (Redis)
import { createRateLimiter } from "next-route-compose";
import { redis } from "./redis";
const withRedisRateLimit = createRateLimiter({
get: async (key) => {
const data = await redis.get(key);
return data ? JSON.parse(data) : null;
},
set: async (key, value) => {
await redis.setex(key, 60, JSON.stringify(value));
},
});
export const GET = compose(
withRedisRateLimit({ limit: 100, window: 60 })
)(handler);📄 License
MIT © Patchy Bean
