Claude-skill-registry api

RESTful API design patterns and best practices. Use when creating endpoints, designing APIs, or implementing routes.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/api" ~/.claude/skills/majiayu000-claude-skill-registry-api && rm -rf "$T"
manifest: skills/data/api/SKILL.md
safety · automated scan (low risk)
This is a pattern-based risk scan, not a security review. Our crawler flagged:
  • references .env files
Always read a skill's source content before installing. Patterns alone don't mean the skill is malicious — but they warrant attention.
source content

API 开发技能

本技能提供 RESTful API 开发的最佳实践和模式。

触发条件

  • 创建 API 端点
  • 设计 REST API
  • 实现后端路由
  • 处理请求/响应
  • API 版本控制

RESTful 设计原则

URL 设计

# 资源命名 - 使用名词复数
GET    /api/v1/users           # 获取用户列表
GET    /api/v1/users/:id       # 获取单个用户
POST   /api/v1/users           # 创建用户
PUT    /api/v1/users/:id       # 更新用户(完整替换)
PATCH  /api/v1/users/:id       # 更新用户(部分更新)
DELETE /api/v1/users/:id       # 删除用户

# 嵌套资源
GET    /api/v1/users/:id/orders           # 用户的订单
GET    /api/v1/users/:id/orders/:orderId  # 用户的特定订单

# 动作资源
POST   /api/v1/users/:id/activate         # 激活用户
POST   /api/v1/orders/:id/cancel          # 取消订单

HTTP 方法语义

方法语义幂等安全
GET读取资源
POST创建资源
PUT完整更新
PATCH部分更新
DELETE删除资源

统一响应格式

成功响应

// 单个资源
{
  "success": true,
  "data": {
    "id": "123",
    "name": "张三",
    "email": "zhangsan@example.com"
  }
}

// 列表资源
{
  "success": true,
  "data": [
    { "id": "1", "name": "用户1" },
    { "id": "2", "name": "用户2" }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "total": 100,
    "totalPages": 5
  }
}

// 创建成功
{
  "success": true,
  "data": { "id": "123", ... },
  "message": "创建成功"
}

错误响应

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "请求参数无效",
    "details": [
      { "field": "email", "message": "邮箱格式不正确" },
      { "field": "age", "message": "年龄必须大于 0" }
    ]
  }
}

API 路由实现

Next.js App Router

// app/api/users/route.ts
import { NextRequest, NextResponse } from "next/server";
import { z } from "zod";

const CreateUserSchema = z.object({
  name: z.string().min(1).max(100),
  email: z.string().email(),
  age: z.number().int().min(0).optional(),
});

export async function GET(request: NextRequest) {
  try {
    const { searchParams } = new URL(request.url);
    const page = parseInt(searchParams.get("page") || "1");
    const pageSize = parseInt(searchParams.get("pageSize") || "20");

    const [users, total] = await Promise.all([
      db.users.findMany({
        skip: (page - 1) * pageSize,
        take: pageSize,
      }),
      db.users.count(),
    ]);

    return NextResponse.json({
      success: true,
      data: users,
      pagination: {
        page,
        pageSize,
        total,
        totalPages: Math.ceil(total / pageSize),
      },
    });
  } catch (error) {
    return NextResponse.json(
      { success: false, error: { message: "获取用户失败" } },
      { status: 500 },
    );
  }
}

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

    const user = await db.users.create({ data: validated });

    return NextResponse.json(
      { success: true, data: user, message: "创建成功" },
      { status: 201 },
    );
  } catch (error) {
    if (error instanceof z.ZodError) {
      return NextResponse.json(
        {
          success: false,
          error: {
            code: "VALIDATION_ERROR",
            message: "请求参数无效",
            details: error.errors,
          },
        },
        { status: 400 },
      );
    }
    throw error;
  }
}

FastAPI

# routers/users.py
from fastapi import APIRouter, Query, HTTPException
from pydantic import BaseModel, EmailStr

router = APIRouter(prefix="/api/v1/users", tags=["users"])

class UserCreate(BaseModel):
    name: str
    email: EmailStr
    age: int | None = None

class UserResponse(BaseModel):
    id: str
    name: str
    email: str

@router.get("")
async def list_users(
    page: int = Query(1, ge=1),
    page_size: int = Query(20, ge=1, le=100)
):
    skip = (page - 1) * page_size
    users = await db.users.find().skip(skip).limit(page_size).to_list()
    total = await db.users.count_documents({})

    return {
        "success": True,
        "data": users,
        "pagination": {
            "page": page,
            "pageSize": page_size,
            "total": total,
            "totalPages": (total + page_size - 1) // page_size
        }
    }

@router.post("", status_code=201)
async def create_user(user: UserCreate):
    result = await db.users.insert_one(user.dict())
    created = await db.users.find_one({"_id": result.inserted_id})

    return {
        "success": True,
        "data": created,
        "message": "创建成功"
    }

@router.get("/{user_id}")
async def get_user(user_id: str):
    user = await db.users.find_one({"_id": user_id})
    if not user:
        raise HTTPException(status_code=404, detail="用户未找到")

    return {"success": True, "data": user}

Express.js

// routes/users.ts
import { Router } from "express";
import { body, query, validationResult } from "express-validator";

const router = Router();

router.get(
  "/",
  [
    query("page").optional().isInt({ min: 1 }),
    query("pageSize").optional().isInt({ min: 1, max: 100 }),
  ],
  async (req, res, next) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({
          success: false,
          error: { code: "VALIDATION_ERROR", details: errors.array() },
        });
      }

      const page = parseInt(req.query.page as string) || 1;
      const pageSize = parseInt(req.query.pageSize as string) || 20;

      const [users, total] = await Promise.all([
        User.find()
          .skip((page - 1) * pageSize)
          .limit(pageSize),
        User.countDocuments(),
      ]);

      res.json({
        success: true,
        data: users,
        pagination: {
          page,
          pageSize,
          total,
          totalPages: Math.ceil(total / pageSize),
        },
      });
    } catch (error) {
      next(error);
    }
  },
);

router.post(
  "/",
  [body("name").notEmpty().isLength({ max: 100 }), body("email").isEmail()],
  async (req, res, next) => {
    try {
      const errors = validationResult(req);
      if (!errors.isEmpty()) {
        return res.status(400).json({
          success: false,
          error: { code: "VALIDATION_ERROR", details: errors.array() },
        });
      }

      const user = await User.create(req.body);
      res.status(201).json({ success: true, data: user, message: "创建成功" });
    } catch (error) {
      next(error);
    }
  },
);

export default router;

查询参数处理

分页

interface PaginationParams {
  page?: number;
  pageSize?: number;
  cursor?: string; // 游标分页
}

// 偏移分页
const { page = 1, pageSize = 20 } = query;
const skip = (page - 1) * pageSize;

// 游标分页(大数据量推荐)
const { cursor, pageSize = 20 } = query;
const where = cursor ? { id: { gt: cursor } } : {};

排序

// ?sort=createdAt:desc,name:asc
const sortParam = query.sort as string;
const orderBy = sortParam?.split(",").map((s) => {
  const [field, order] = s.split(":");
  return { [field]: order || "asc" };
}) || [{ createdAt: "desc" }];

筛选

// ?status=active&role=admin
const { status, role } = query;
const where = {
  ...(status && { status }),
  ...(role && { role }),
};

搜索

// ?q=keyword
const { q } = query;
const where = q
  ? {
      OR: [
        { name: { contains: q, mode: "insensitive" } },
        { email: { contains: q, mode: "insensitive" } },
      ],
    }
  : {};

API 版本控制

URL 路径版本

/api/v1/users
/api/v2/users

请求头版本

Accept: application/vnd.api+json; version=1

版本控制实现

// app/api/v1/users/route.ts
export async function GET() { ... }

// app/api/v2/users/route.ts
export async function GET() { ... }

认证与授权

JWT 认证中间件

// middlewares/auth.ts
import jwt from "jsonwebtoken";

export async function authMiddleware(request: NextRequest) {
  const token = request.headers.get("Authorization")?.replace("Bearer ", "");

  if (!token) {
    return NextResponse.json(
      {
        success: false,
        error: { code: "UNAUTHORIZED", message: "未提供认证信息" },
      },
      { status: 401 },
    );
  }

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET!);
    request.user = decoded;
  } catch (error) {
    return NextResponse.json(
      {
        success: false,
        error: { code: "UNAUTHORIZED", message: "Token 无效" },
      },
      { status: 401 },
    );
  }
}

权限检查

export function requireRole(...roles: string[]) {
  return (request: NextRequest) => {
    if (!roles.includes(request.user.role)) {
      return NextResponse.json(
        { success: false, error: { code: "FORBIDDEN", message: "权限不足" } },
        { status: 403 },
      );
    }
  };
}

// 使用
export async function DELETE(request: NextRequest) {
  const authError = await authMiddleware(request);
  if (authError) return authError;

  const roleError = requireRole("admin")(request);
  if (roleError) return roleError;

  // 执行删除
}

HTTP 状态码

状态码含义使用场景
200OK成功获取/更新
201Created成功创建
204No Content成功删除
400Bad Request请求参数错误
401Unauthorized未认证
403Forbidden无权限
404Not Found资源不存在
409Conflict资源冲突
422Unprocessable Entity业务逻辑错误
429Too Many Requests请求过多
500Internal Server Error服务器错误

最佳实践

  1. 使用名词复数 -
    /users
    而非
    /user
  2. HTTP 方法语义 - GET 读取,POST 创建,PUT/PATCH 更新
  3. 统一响应格式 - success、data、error、pagination
  4. 输入验证 - 使用 Zod/Pydantic 验证
  5. 错误处理 - 返回有意义的错误码和消息
  6. 版本控制 - /api/v1/
  7. 分页 - 列表接口必须分页
  8. HATEOAS - 可选,返回相关链接
  9. 文档 - OpenAPI/Swagger 文档
  10. 幂等性 - PUT、DELETE 保持幂等