1 views
The basics of a route handler
Any file called route.ts inside app/api can export functions named after HTTP verbs, and Next.js automatically sends the request to whichever one matches.
Step 1 – A GET endpoint
// app/api/posts/route.ts
import { NextResponse } from "next/server";
import { connectDB } from "@/lib/db";
import Post from "@/models/Post";
export async function GET() {
await connectDB();
const posts = await Post.find({}).lean();
return NextResponse.json({ success: true, data: posts });
}
Step 2 – POST, validated with Zod
import { z } from "zod";
const PostSchema = z.object({
title: z.string().min(3),
content: z.string().min(10),
slug: z.string().regex(/^[a-z0-9-]+$/),
});
export async function POST(req: Request) {
const body = await req.json();
const parsed = PostSchema.safeParse(body);
if (!parsed.success) {
return NextResponse.json(
{ success: false, errors: parsed.error.flatten() },
{ status: 422 }
);
}
await connectDB();
const post = await Post.create(parsed.data);
return NextResponse.json({ success: true, data: post }, { status: 201 });
}
Step 3 – Don't skip error handling
Wrap your database calls in a try/catch and return something meaningful. A plain 500 with a clear message beats an unhandled rejection taking down your serverless function.
try {
const post = await Post.create(parsed.data);
return NextResponse.json({ success: true, data: post }, { status: 201 });
} catch (err) {
console.error(err);
return NextResponse.json(
{ success: false, message: "Internal server error" },
{ status: 500 }
);
}
Putting it together
Typed exports, Zod validation, and consistent error responses - combine those three and you've basically got a template for CRUD endpoints on any resource in your app.
1 views
Comments
Loading comments...
Related Articles
Education
Beginner 8 min read
What Actually Makes a Tech Blog Post Good
A practical process for planning, writing, and editing a technical article people actually finish reading - from picking the topic to the last proofread.
Read Article