mcpField
A
API Design

API Design

github.com/jmsktm开发1067 次安装42 周前更新
代码仓库

关于 API Design

API Design 是面向 Next.js 应用 API 开发的技能,帮助开发者处理路由设计、验证与安全等环节,并提供具体工作流和示例。

主要功能

  1. 01RESTful 路由处理模式
  2. 02使用 Zod 验证请求
  3. 03身份验证中间件
  4. 04速率限制与分页
  5. 05统一错误与 HTTP 状态码

适用场景

  1. 01规划基于资源的 API 端点
  2. 02验证请求正文和参数
  3. 03通过身份验证保护路由
  4. 04添加分页和速率限制

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jmsktm/claude-settings --skill api-design

安装后在 Claude Code 中输入/api-design确认已加载。

详细介绍

API 设计与集成技能

Next.js 应用中稳健、可扩展 API 的设计与实现综合指南。从 RESTful 端点设计到请求验证、错误处理、身份验证和速率限制,本技能涵盖了生产就绪 API 开发的各个方面。

构建易于使用、文档完善且遵循行业最佳实践的 API。采用恰当的 HTTP 语义、一致的错误响应,以及类型安全的请求和响应处理。

核心工作流

工作流 1:RESTful 路由处理程序

用途: 按照 REST 约定创建组织良好的 API 路由

步骤:

  1. 规划基于资源的 URL 结构
  2. 为每种 HTTP 方法创建路由处理程序
  3. 实现请求验证
  4. 添加适当的错误处理
  5. 返回一致的响应格式

实现:

// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server'

export async function GET(request: NextRequest) {
  try {
    const users = await db.users.findMany()
    return NextResponse.json({ users })
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to fetch users' },
      { status: 500 }
    )
  }
}

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    const user = await db.users.create({ data: body })
    return NextResponse.json({ user }, { status: 201 })
  } catch (error) {
    return NextResponse.json(
      { error: 'Failed to create user' },
      { status: 400 }
    )
  }
}

// app/api/users/[id]/route.ts
export async function GET(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const user = await db.users.findUnique({ where: { id: params.id } })

  if (!user) {
    return NextResponse.json(
      { error: 'User not found' },
      { status: 404 }
    )
  }

  return NextResponse.json({ user })
}

export async function PATCH(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  const body = await request.json()
  const user = await db.users.update({
    where: { id: params.id },
    data: body
  })

  return NextResponse.json({ user })
}

export async function DELETE(
  request: NextRequest,
  { params }: { params: { id: string } }
) {
  await db.users.delete({ where: { id: params.id } })
  return new NextResponse(null, { status: 204 })
}
工作流 2:使用 Zod 验证请求

用途: 进行类型安全的请求验证,并提供详细的错误消息

实现:

import { z } from 'zod'
import { NextRequest, NextResponse } from 'next/server'

const userSchema = z.object({
  name: z.string().min(2).max(100),
  email: z.string().email(),
  age: z.number().int().min(18).optional()
})

export async function POST(request: NextRequest) {
  try {
    const body = await request.json()
    const validated = userSchema.parse(body)

    const user = await db.users.create({ data: validated })
    return NextResponse.json({ user }, { status: 201 })
  } catch (error) {
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        { error: 'Validation failed', issues: error.issues },
        { status: 400 }
      )
    }
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    )
  }
}
工作流 3:身份验证中间件

用途: 通过身份验证检查保护 API 路由

实现:

// lib/auth-middleware.ts
import { NextRequest, NextResponse } from 'next/server'

export async function withAuth(
  handler: (req: NextRequest, context: any) => Promise<NextResponse>
) {
  return async (req: NextRequest, context: any) => {
    const token = req.headers.get('authorization')?.replace('Bearer ', '')

    if (!token) {
      return NextResponse.json(
        { error: 'Unauthorized' },
        { status: 401 }
      )
    }

    try {
      const user = await verifyToken(token)
      req.user = user
      return handler(req, context)
    } catch (error) {
      return NextResponse.json(
        { error: 'Invalid token' },
        { status: 401 }
      )
    }
  }
}

// app/api/protected/route.ts
export const GET = withAuth(async (request: NextRequest) => {
  const user = request.user
  return NextResponse.json({ user })
})
工作流 4:速率限制

用途: 防止 API 遭到滥用

实现:

// lib/rate-limit.ts
const rateLimitMap = new Map<string, { count: number; reset: number }>()

export function rateLimit(limit = 100, window = 60000) {
  return async (request: NextRequest) => {
    const ip = request.ip || 'anonymous'
    const now = Date.now()
    const record = rateLimitMap.get(ip)

    if (!record || now > record.reset) {
      rateLimitMap.set(ip, { count: 1, reset: now + window })
      return null
    }

    if (record.count >= limit) {
      return NextResponse.json(
        { error: 'Too many requests' },
        { status: 429 }
      )
    }

    record.count++
    return null
  }
}

// Usage in route
export async function GET(request: NextRequest) {
  const limitResponse = await rateLimit(100, 60000)(request)
  if (limitResponse) return limitResponse

  // Handle request...
}
工作流 5:分页模式

用途: 高效处理大型数据集

实现:

type PaginatedResponse<T> = {
  data: T[]
  pagination: {
    page: number
    limit: number
    total: number
    totalPages: number
    hasNext: boolean
    hasPrev: boolean
  }
}

export async function GET(request: NextRequest) {
  const searchParams = request.nextUrl.searchParams
  const page = parseInt(searchParams.get('page') || '1')
  const limit = parseInt(searchParams.get('limit') || '10')
  const search = searchParams.get('search') || ''

  const skip = (page - 1) * limit

  const [users, total] = await Promise.all([
    db.users.findMany({
      where: { name: { contains: search } },
      skip,
      take: limit
    }),
    db.users.count({ where: { name: { contains: search } } })
  ])

  const totalPages = Math.ceil(total / limit)

  return NextResponse.json({
    data: users,
    pagination: {
      page,
      limit,
      total,
      totalPages,
      hasNext: page < totalPages,
      hasPrev: page > 1
    }
  })
}

快速参考

操作HTTP 方法URL 模式
列出资源GET/api/resources
获取单个资源GET/api/resources/:id
创建POST/api/resources
更新PATCH/PUT/api/resources/:id
删除DELETE/api/resources/:id
执行操作POST/api/resources/:id/action

HTTP 状态码

成功状态码
  • 200 OK - GET、PUT、PATCH 请求成功
  • 201 Created - POST 请求成功(资源已创建)
  • 204 No Content - DELETE 请求成功
客户端错误状态码
  • 400 Bad Request - 请求正文或参数无效
  • 401 Unauthorized - 缺少身份验证信息或身份验证无效
  • 403 Forbidden - 已通过身份验证,但未获授权
  • 404 Not Found - 资源不存在
  • 409 Conflict - 资源冲突(重复)
  • 422 Unprocessable Entity - 验证失败
  • 429 Too Many Requests - 超出速率限制
服务器错误状态码
  • 500 Internal Server Error - 意外错误
  • 503 Service Unavailable - 暂时不可用

错误响应格式

type ErrorResponse = {
  error: string              // Human-readable message
  code?: string             // Machine-readable code
  details?: Record<string, any>  // Additional context
  timestamp?: string
}

// Usage
return NextResponse.json(
  {
    error: 'Validation failed',
    code: 'VALIDATION_ERROR',
    details: { field: 'email', reason: 'Invalid format' },
    timestamp: new Date().toISOString()
  },
  { status: 400 }
)

CORS 配置

// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const response = NextResponse.next()

  response.headers.set('Access-Control-Allow-Origin', '*')
  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
  response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization')

  if (request.method === 'OPTIONS') {
    return new NextResponse(null, { status: 200, headers: response.headers })
  }

  return response
}

export const config = {
  matcher: '/api/:path*'
}

最佳实践

  • 使用 HTTP 语义: 使用正确的状态码(创建时用 201,删除时用 204)
  • 保持响应格式一致: 始终返回 { data } 或 { error, code }
  • 验证所有内容: 使用 Zod 验证请求正文、查询和参数
  • 妥善处理错误: 切勿向客户端暴露内部错误
  • 记录 API 文档: 使用 OpenAPI/Swagger 编写文档
  • 对 API 进行版本控制: 对不兼容变更使用 /api/v1/
  • 对列表分页: 始终对列表端点进行分页
  • 使用 CORS: 为客户端请求正确配置
  • 记录请求日志: 记录所有 API 请求以便调试
  • 限制速率: 防止遭到滥用

常见模式

健康检查端点
// app/api/health/route.ts
export async function GET() {
  return NextResponse.json({
    status: 'ok',
    timestamp: new Date().toISOString(),
    version: process.env.APP_VERSION
  })
}
文件上传
export async function POST(request: NextRequest) {
  const formData = await request.formData()
  const file = formData.get('file') as File

  if (!file) {
    return NextResponse.json(
      { error: 'No file provided' },
      { status: 400 }
    )
  }

  const allowedTypes = ['image/jpeg', 'image/png', 'image/webp']
  if (!allowedTypes.includes(file.type)) {
    return NextResponse.json(
      { error: 'Invalid file type' },
      { status: 400 }
    )
  }

  const url = await uploadFile(file)
  return NextResponse.json({ url }, { status: 201 })
}
Webhook 处理程序
export async function POST(request: NextRequest) {
  const signature = request.headers.get('x-webhook-signature')
  const body = await request.text()

  const isValid = verifyWebhookSignature(body, signature)
  if (!isValid) {
    return NextResponse.json(
      { error: 'Invalid signature' },
      { status: 401 }
    )
  }

  const event = JSON.parse(body)
  await processWebhookEvent(event)

  return NextResponse.json({ received: true })
}

依赖项

# Validation
npm install zod

# Rate limiting
npm install @upstash/redis @upstash/ratelimit

# API documentation
npm install swagger-ui-react swagger-jsdoc

错误处理

  • 400 Bad Request: 输入无效、验证错误
  • 401 Unauthorized: 缺少身份验证信息或身份验证无效
  • 403 Forbidden: 身份验证有效,但权限不足
  • 404 Not Found: 资源不存在
  • 409 Conflict: 资源已存在或状态冲突
  • 422 Unprocessable: 语法有效,但存在语义错误
  • 429 Too Many Requests: 超出速率限制
  • 500 Internal Error: 服务器端故障

性能提示

  • 为查询字段创建数据库索引
  • 对大型数据集实现基于游标的分页
  • 使用 Redis 或 SWR 缓存频繁读取的数据
  • 使用数据库连接池
  • 通过选择字段缩减负载大小
  • 使用 gzip 压缩响应

何时使用此技能

在以下情况下调用此技能:

  • 创建新的 API 端点
  • 实现请求验证
  • 设计 API 架构
  • 添加身份验证/授权
  • 处理文件上传
  • 实现 Webhook
  • 设置 CORS
  • 添加速率限制
  • 调试 API 错误
  • 优化 API 性能

评论

支持 Markdown