Nextjs-api
Next.js API Routes & Server
Next.js API Routes & Server Route Handlers (App Router) // app/api/users/route.ts import { NextRequest, NextResponse } from 'next/server'; export async function…
Next.js API Routes & Server
Route Handlers (App Router)
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';
export async function GET(req: NextRequest) {
const { searchParams } = req.nextUrl;
const page = Number(searchParams.get('page') ?? 1);
const users = await db.user.findMany({
skip: (page - 1) * 20,
take: 20,
});
return NextResponse.json({ users });
}
export async function POST(req: NextRequest) {
const body = await req.json();
const user = await db.user.create({ data: body });
return NextResponse.json(user, { status: 201 });
}
// app/api/users/[id]/route.ts
export async function GET(
req: NextRequest,
{ params }: { params: { id: string } }
) {
const user = await db.user.findUniqueOrThrow({ where: { id: params.id } });
return NextResponse.json(user);
}
export async function PATCH(
req: NextRequest,
{ params }: { params: { id: string } }
) {
const body = await req.json();
const user = await db.user.update({ where: { id: params.id }, data: body });
return NextResponse.json(user);
}
export async function DELETE(
_req: NextRequest,
{ params }: { params: { id: string } }
) {
await db.user.delete({ where: { id: params.id } });
return new NextResponse(null, { status: 204 });
}Middleware
// middleware.ts (root of project)
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const token = request.cookies.get('token')?.value;
const { pathname } = request.nextUrl;
// Redirect unauthenticated users
const protectedRoutes = ['/dashboard', '/settings', '/api/me'];
if (protectedRoutes.some(r => pathname.startsWith(r)) && !token) {
return NextResponse.redirect(new URL('/login', request.url));
}
// Add headers to all responses
const response = NextResponse.next();
response.headers.set('X-Content-Type-Options', 'nosniff');
return response;
}
// Only run on these paths
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};Server Actions
// app/actions/posts.ts
'use server';
import { revalidatePath, revalidateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { auth } from '@clerk/nextjs/server';
import { z } from 'zod';
const PostSchema = z.object({
title: z.string().min(1).max(200),
body: z.string().min(1),
});
export async function createPost(formData: FormData) {
const { userId } = auth();
if (!userId) throw new Error('Unauthorized');
const result = PostSchema.safeParse({
title: formData.get('title'),
body: formData.get('body'),
});
if (!result.success) return { error: result.error.flatten() };
const post = await db.post.create({
data: { ...result.data, authorId: userId },
});
revalidatePath('/posts'); // invalidate cached page
revalidateTag('posts'); // invalidate by cache tag
redirect(`/posts/${post.id}`); // redirect after mutation
}
// Use in a Server Component form
// <form action={createPost}>
// <input name="title" />
// <textarea name="body" />
// <button type="submit">Create</button>
// </form>
// Or call from client component
'use client';
import { createPost } from '@/app/actions/posts';
const [state, formAction] = useFormState(createPost, null);Caching & Rendering
// fetch cache options
fetch('https://api.example.com/data', {
cache: 'force-cache', // SSG — cached indefinitely
next: { revalidate: 3600 }, // ISR — revalidate every hour
});
fetch('https://api.example.com/data', {
cache: 'no-store', // SSR — never cached
});
// unstable_cache for DB queries
import { unstable_cache } from 'next/cache';
const getUser = unstable_cache(
async (id: string) => db.user.findUnique({ where: { id } }),
['user'],
{ revalidate: 300, tags: ['user'] }
);
// generateStaticParams — SSG for dynamic routes
export async function generateStaticParams() {
const posts = await db.post.findMany({ select: { slug: true } });
return posts.map(p => ({ slug: p.slug }));
}
// Streaming with Suspense
// app/dashboard/page.tsx
import { Suspense } from 'react';
import { UserStats, Skeleton } from '@/components';
export default function DashboardPage() {
return (
<main>
<h1>Dashboard</h1>
<Suspense fallback={<Skeleton />}>
<UserStats /> {/* async Server Component, streamed */}
</Suspense>
</main>
);
}