The Next Safe Action but for API Routes
12/01/2025 • Melvynx
Are you looking for a tool that works like Next Safe Action but for API Routes? It exists!
Let me introduce Next-Zod-Route, a library that allows you to transform your API Routes from this:
const BodySchema = z.object({
name: z.string(),
});
const SearchParamsSchema = z.object({
query: z.string(),
});
export const POST = async (
req: Request,
context: { searchParams: Promise<Record<string, string>> },
) => {
const safeBody = BodySchema.safeParse(await req.json());
const baseSearchParams = await context.searchParams;
const safeSearchParams = SearchParamsSchema.safeParse(baseSearchParams);
if (!safeBody.success) {
return NextResponse.json(
{ error: safeBody.error.message },
{ status: 400 },
);
}
if (!safeSearchParams.success) {
return NextResponse.json(
{ error: safeSearchParams.error.message },
{ status: 400 },
);
}
const body = safeBody.data;
const searchParams = safeSearchParams.data;
return NextResponse.json({ body, searchParams });
};To this:
export const POST = route
.body(
z.object({
name: z.string(),
}),
)
.query(
z.object({
q: z.string(),
}),
)
.handler(async (req, { body, query }) => {
return NextResponse.json({ body, query });
Much simpler, right?
And that's not all because next-zod-route allows you to add:
With next-zod-route, you can easily validate route parameters, query parameters, and request body. Let's see how it works:
const route = createZodRoute()
.params(
z.object({
id: z.string().uuid(),
}),
)
.query(
z.object({
search: z.string().min(1),
}),
)
.body(
z.object({
field: z.string(),
}),
The typing is automatically inferred from your Zod schemas, making your code safer and more readable.
You can easily add middlewares to modify or validate requests before they reach your handler:
const authMiddleware: MiddlewareFunction = async ({ next, request }) => {
// Code executed before the handler
const startTime = performance.now();
// Passing data to the context (here you can get auth based on the request)
const result = await next({
ctx: { user: { id: "user-123", role: "admin" } },
});
// Code executed after the handler
const endTime = performance.now();
console.log(
Middlewares can:
You can customize error handling to handle specific cases:
class CustomError extends Error {
statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.name = "CustomError";
this.statusCode = statusCode;
}
}
const handleServerError = (error: Error) => {
if (error instanceof CustomError
The metadata feature allows you to associate validated data with your routes, particularly useful for authorization checks:
const permissionsMetadataSchema = z.object({
permissions: z.array(z.string()).optional(),
});
const permissionMiddleware: MiddlewareFunction = async ({
next,
metadata,
request,
}) => {
const isAuthorized = hasPermission(request, metadata.permissions);
if (!hasAllPermissions) {
// Short-circuit with a 403 Forbidden response
return new Response(
JSON
You can return either a standard Response object or a simple JavaScript object that will be automatically converted to a JSON response:
// Direct return of a Response object
export const GET = createZodRoute().handler(() => {
return new Response(JSON.stringify({ custom: "response" }), {
status: 201,
headers: { "X-Custom-Header": "test" },
});
});
// Return a JavaScript object (automatically converted to JSON)
export const GET = createZodRoute().handler(() => {
return { data:
Middlewares can work together to build complex functionalities:
export const GET = createZodRoute()
.use(async ({ next }) => {
// First middleware
const result = await next({ ctx: { user: { id: "user-123" } } });
return result;
})
.use(async ({ next, ctx }) => {
// Second middleware, access to first middleware context
const user = ctx.user;
const result
next-zod-route is a complete solution to handle your API Routes in Next.js with:
If you're developing API routes in Next.js and looking for an alternative to Next Safe Action, this library is made for you!
To install it:
npm install next-zod-route
# or
yarn add next-zod-route
# or
pnpm add next-zod-routeFind the source code and complete documentation on GitHub.