Skip to content
第 160 / 250 章前端⏱ 10 分钟阅读

第 160 章:Route Handlers(API)

学习目标

  • 掌握 Route Handlers 创建 API
  • 学会 GET / POST / PUT / DELETE
  • 理解请求验证与错误处理
  • 学会用中间件

一、Route Handlers 是什么

app/ 目录下的 route.ts 文件,导出 HTTP 方法函数,作为 API 端点。

app/
├── api/
│   ├── hello/
│   │   └── route.ts     → /api/hello
│   ├── users/
│   │   ├── route.ts     → /api/users
│   │   └── [id]/
│   │       └── route.ts → /api/users/:id

二、基础

2.1 GET

ts
// app/api/hello/route.ts
export async function GET() {
  return Response.json({ message: 'Hello' });
}

2.2 POST

ts
// app/api/users/route.ts
export async function POST(request: Request) {
  const body = await request.json();
  // 验证 body...
  const user = await db.user.create({ data: body });
  return Response.json(user, { status: 201 });
}

2.3 动态参数

ts
// app/api/users/[id]/route.ts
type Params = { params: Promise<{ id: string }> };

export async function GET(request: Request, { params }: Params) {
  const { id } = await params;
  const user = await db.user.findUnique({ where: { id } });
  if (!user) return Response.json({ error: 'Not found' }, { status: 404 });
  return Response.json(user);
}

export async function PUT(request: Request, { params }: Params) {
  const { id } = await params;
  const body = await request.json();
  const user = await db.user.update({ where: { id }, data: body });
  return Response.json(user);
}

export async function DELETE(request: Request, { params }: Params) {
  const { id } = await params;
  await db.user.delete({ where: { id } });
  return new Response(null, { status: 204 });
}

三、请求与响应

3.1 获取查询参数

ts
export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const page = searchParams.get('page') ?? '1';
  const size = searchParams.get('size') ?? '10';
  return Response.json({ page, size });
}

3.2 获取 Headers

ts
export async function GET(request: Request) {
  const auth = request.headers.get('authorization');
  if (!auth) return Response.json({ error: 'Unauthorized' }, { status: 401 });
  // ...
}

3.3 设置 Cookies

ts
import { cookies } from 'next/headers';

export async function POST() {
  cookies().set('token', 'xxx', {
    httpOnly: true,
    secure: true,
    maxAge: 60 * 60 * 24,
    path: '/',
  });
  return Response.json({ ok: true });
}

四、完整 CRUD 示例

ts
// app/api/users/route.ts
import { z } from 'zod';
import { db } from '@/lib/db';

const UserSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
});

export async function GET() {
  const users = await db.user.findMany();
  return Response.json(users);
}

export async function POST(request: Request) {
  const body = await request.json();
  const result = UserSchema.safeParse(body);

  if (!result.success) {
    return Response.json(
      { error: 'Validation failed', issues: result.error.issues },
      { status: 400 }
    );
  }

  const user = await db.user.create({ data: result.data });
  return Response.json(user, { status: 201 });
}

五、流式响应

ts
// app/api/stream/route.ts
export async function GET() {
  const encoder = new TextEncoder();
  const stream = new ReadableStream({
    async start(controller) {
      for (let i = 0; i < 5; i++) {
        controller.enqueue(encoder.encode(`data: ${i}\n\n`));
        await new Promise(r => setTimeout(r, 1000));
      }
      controller.close();
    },
  });

  return new Response(stream, {
    headers: { 'Content-Type': 'text/event-stream' },
  });
}

六、CORS

ts
// app/api/public/route.ts
export async function GET(request: Request) {
  // 处理预检
  if (request.method === 'OPTIONS') {
    return new Response(null, {
      status: 204,
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type',
      },
    });
  }

  return Response.json(
    { message: 'CORS OK' },
    {
      headers: {
        'Access-Control-Allow-Origin': '*',
      },
    }
  );
}

七、错误处理

ts
export async function POST(request: Request) {
  try {
    const body = await request.json();
    const user = await db.user.create({ data: body });
    return Response.json(user, { status: 201 });
  } catch (error) {
    console.error('Create user failed', error);
    return Response.json(
      { error: 'Internal Server Error' },
      { status: 500 }
    );
  }
}

八、Next.js vs 独立后端

维度Route Handlers独立 SpringBoot
部署一起单独服务
适合轻 BFF、SSR复杂业务
性能边缘运行长连接、复杂事务
复用仅当前项目多端共用

九、本章小结

方法用途
GET读取
POST创建
PUT更新
DELETE删除
OPTIONS预检

动手练习

  1. 实现 /api/hello,返回 JSON
  2. 实现 /api/users 完整 CRUD
  3. 用 Zod 校验请求体
  4. 实现流式响应(SSE)
  5. 实现一个需要鉴权的 API

推荐阅读


下一章:第 161 章:Middleware 与鉴权

本站基于 VitePress 构建 · 由 Codebook 团队维护