A
API Routes
Type-safe API route handlers in Next.js App Router with request/response typing.
Code
typescript
1// app/api/users/route.ts2import { NextRequest, NextResponse } from "next/server";3import { z } from "zod";4 5// Types6interface User {7 id: number;8 name: string;9 email: string;10 createdAt: Date;11}12 13// Validation schemas14const CreateUserSchema = z.object({15 name: z.string().min(2).max(50),16 email: z.string().email(),17});18 19const QuerySchema = z.object({20 page: z.coerce.number().min(1).default(1),21 limit: z.coerce.number().min(1).max(100).default(10),22 search: z.string().optional(),23});24 25// Response helpers26function jsonResponse<T>(data: T, status = 200) {27 return NextResponse.json(data, { status });28}29 30function errorResponse(message: string, status = 400) {31 return NextResponse.json({ error: message }, { status });32}33 34// GET /api/users - List users35export async function GET(request: NextRequest) {36 try {37 const { searchParams } = new URL(request.url);38 const query = QuerySchema.parse(Object.fromEntries(searchParams));39 40 // Simulate database query41 const users: User[] = [42 { id: 1, name: "Alice", email: "alice@example.com", createdAt: new Date() },43 { id: 2, name: "Bob", email: "bob@example.com", createdAt: new Date() },44 ];45 46 return jsonResponse({47 data: users,48 pagination: {49 page: query.page,50 limit: query.limit,51 total: users.length,52 },53 });54 } catch (error) {55 if (error instanceof z.ZodError) {56 return errorResponse("Invalid query parameters", 400);57 }58 return errorResponse("Internal server error", 500);59 }60}61 62// POST /api/users - Create user63export async function POST(request: NextRequest) {64 try {65 const body = await request.json();66 const data = CreateUserSchema.parse(body);67 68 // Simulate creating user69 const newUser: User = {70 id: Date.now(),71 ...data,72 createdAt: new Date(),73 };74 75 return jsonResponse(newUser, 201);76 } catch (error) {77 if (error instanceof z.ZodError) {78 return jsonResponse(79 { error: "Validation failed", details: error.errors },80 40081 );82 }83 return errorResponse("Internal server error", 500);84 }85}86 87// app/api/users/[id]/route.ts - Dynamic route88interface RouteContext {89 params: Promise<{ id: string }>;90}91 92export async function GET(request: NextRequest, context: RouteContext) {93 const { id } = await context.params;94 const userId = parseInt(id, 10);95 96 if (isNaN(userId)) {97 return errorResponse("Invalid user ID", 400);98 }99 100 // Simulate fetching user101 const user: User | null = {102 id: userId,103 name: "Alice",104 email: "alice@example.com",105 createdAt: new Date(),106 };107 108 if (!user) {109 return errorResponse("User not found", 404);110 }111 112 return jsonResponse(user);113}114 115export async function PATCH(request: NextRequest, context: RouteContext) {116 const { id } = await context.params;117 const userId = parseInt(id, 10);118 const body = await request.json();119 120 const UpdateSchema = CreateUserSchema.partial();121 const data = UpdateSchema.parse(body);122 123 // Simulate updating user124 const updatedUser: User = {125 id: userId,126 name: data.name || "Alice",127 email: data.email || "alice@example.com",128 createdAt: new Date(),129 };130 131 return jsonResponse(updatedUser);132}133 134export async function DELETE(request: NextRequest, context: RouteContext) {135 const { id } = await context.params;136 // Simulate deleting user137 return new NextResponse(null, { status: 204 });138}Run this example locally
$ npx create-next-app@latest --typescript