mcpField
A
api-endpoint-generator

api-endpoint-generator

github.com/patricio0312rev开发63248 次安装41 周前更新
代码仓库

关于 api-endpoint-generator

api-endpoint-generator 是用于生成带验证和类型化响应的 CRUD API 端点的技能。它提供 Express + TypeScript、NestJS 和 FastAPI 模式,并涵盖错误处理及 OpenAPI/Swagger 文档。

主要功能

  1. 01生成 CRUD 路由模式
  2. 02添加请求验证模式
  3. 03定义类型化请求与响应数据
  4. 04提供一致的错误格式和状态码
  5. 05包含分页及查询指引
  6. 06生成 OpenAPI/Swagger 文档

适用场景

  1. 01为新资源创建 API 端点
  2. 02为 API 请求处理添加验证
  3. 03统一响应和错误格式
  4. 04记录 API 路由并提供示例

安装

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

$npx skills add https://github.com/patricio0312rev/skills --skill api-endpoint-generator

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

详细介绍

API 端点生成器

生成可用于生产环境的 CRUD API 端点,并具备验证和类型安全。

核心工作流

  1. 定义资源:实体名称和架构
  2. 生成路由:POST、GET、PUT/PATCH、DELETE 端点
  3. 添加验证:使用 Zod/Joi 验证请求正文/查询参数
  4. 定义响应类型:为所有响应定义 TypeScript 接口
  5. 错误处理:统一错误响应
  6. 文档:OpenAPI/Swagger 规范
  7. 示例:请求/响应示例

Express + TypeScript 模式

// types/user.types.ts
export interface User {
  id: string;
  email: string;
  name: string;
  role: "user" | "admin";
  createdAt: Date;
  updatedAt: Date;
}

export interface CreateUserDto {
  email: string;
  name: string;
  password: string;
}

export interface UpdateUserDto {
  name?: string;
  email?: string;
}

export interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: ApiError;
  meta?: PaginationMeta;
}

export interface ApiError {
  code: string;
  message: string;
  details?: Record<string, string[]>;
}

验证架构(Zod)

// schemas/user.schema.ts
import { z } from "zod";

export const createUserSchema = z.object({
  email: z.string().email("Invalid email address"),
  name: z.string().min(2, "Name must be at least 2 characters"),
  password: z
    .string()
    .min(8, "Password must be at least 8 characters")
    .regex(/[A-Z]/, "Password must contain uppercase letter")
    .regex(/[0-9]/, "Password must contain number"),
});

export const updateUserSchema = z
  .object({
    name: z.string().min(2).optional(),
    email: z.string().email().optional(),
  })
  .refine((data) => Object.keys(data).length > 0, {
    message: "At least one field must be provided",
  });

export const getUsersQuerySchema = z.object({
  page: z.coerce.number().int().positive().default(1),
  limit: z.coerce.number().int().min(1).max(100).default(10),
  sortBy: z.enum(["name", "email", "createdAt"]).optional(),
  sortOrder: z.enum(["asc", "desc"]).default("desc"),
  search: z.string().optional(),
});

export type CreateUserDto = z.infer<typeof createUserSchema>;
export type UpdateUserDto = z.infer<typeof updateUserSchema>;
export type GetUsersQuery = z.infer<typeof getUsersQuerySchema>;

CRUD 路由处理程序

// routes/users.routes.ts
import { Router } from "express";
import { UserController } from "../controllers/user.controller";
import { validateRequest } from "../middleware/validate";
import { authenticate } from "../middleware/auth";
import {
  createUserSchema,
  updateUserSchema,
  getUsersQuerySchema,
} from "../schemas/user.schema";

const router = Router();
const controller = new UserController();

// Create
router.post(
  "/",
  authenticate,
  validateRequest({ body: createUserSchema }),
  controller.create
);

// Read (list)
router.get(
  "/",
  authenticate,
  validateRequest({ query: getUsersQuerySchema }),
  controller.list
);

// Read (single)
router.get("/:id", authenticate, controller.getById);

// Update
router.patch(
  "/:id",
  authenticate,
  validateRequest({ body: updateUserSchema }),
  controller.update
);

// Delete
router.delete("/:id", authenticate, controller.delete);

export default router;

控制器实现

// controllers/user.controller.ts
import { Request, Response, NextFunction } from "express";
import { UserService } from "../services/user.service";
import {
  CreateUserDto,
  UpdateUserDto,
  GetUsersQuery,
} from "../types/user.types";
import { ApiResponse } from "../types/api.types";

export class UserController {
  private service = new UserService();

  create = async (
    req: Request<{}, {}, CreateUserDto>,
    res: Response<ApiResponse<User>>,
    next: NextFunction
  ) => {
    try {
      const user = await this.service.create(req.body);
      res.status(201).json({
        success: true,
        data: user,
      });
    } catch (error) {
      next(error);
    }
  };

  list = async (
    req: Request<{}, {}, {}, GetUsersQuery>,
    res: Response<ApiResponse<User[]>>,
    next: NextFunction
  ) => {
    try {
      const { page, limit, sortBy, sortOrder, search } = req.query;
      const result = await this.service.findAll({
        page,
        limit,
        sortBy,
        sortOrder,
        search,
      });

      res.json({
        success: true,
        data: result.users,
        meta: {
          page: result.page,
          limit: result.limit,
          total: result.total,
          totalPages: result.totalPages,
        },
      });
    } catch (error) {
      next(error);
    }
  };

  getById = async (
    req: Request<{ id: string }>,
    res: Response<ApiResponse<User>>,
    next: NextFunction
  ) => {
    try {
      const user = await this.service.findById(req.params.id);
      if (!user) {
        return res.status(404).json({
          success: false,
          error: {
            code: "USER_NOT_FOUND",
            message: "User not found",
          },
        });
      }
      res.json({
        success: true,
        data: user,
      });
    } catch (error) {
      next(error);
    }
  };

  update = async (
    req: Request<{ id: string }, {}, UpdateUserDto>,
    res: Response<ApiResponse<User>>,
    next: NextFunction
  ) => {
    try {
      const user = await this.service.update(req.params.id, req.body);
      if (!user) {
        return res.status(404).json({
          success: false,
          error: {
            code: "USER_NOT_FOUND",
            message: "User not found",
          },
        });
      }
      res.json({
        success: true,
        data: user,
      });
    } catch (error) {
      next(error);
    }
  };

  delete = async (
    req: Request<{ id: string }>,
    res: Response<ApiResponse<void>>,
    next: NextFunction
  ) => {
    try {
      const deleted = await this.service.delete(req.params.id);
      if (!deleted) {
        return res.status(404).json({
          success: false,
          error: {
            code: "USER_NOT_FOUND",
            message: "User not found",
          },
        });
      }
      res.status(204).send();
    } catch (error) {
      next(error);
    }
  };
}

验证中间件

// middleware/validate.ts
import { Request, Response, NextFunction } from "express";
import { ZodSchema } from "zod";

interface ValidationSchemas {
  body?: ZodSchema;
  query?: ZodSchema;
  params?: ZodSchema;
}

export const validateRequest = (schemas: ValidationSchemas) => {
  return (req: Request, res: Response, next: NextFunction) => {
    try {
      if (schemas.body) {
        req.body = schemas.body.parse(req.body);
      }
      if (schemas.query) {
        req.query = schemas.query.parse(req.query);
      }
      if (schemas.params) {
        req.params = schemas.params.parse(req.params);
      }
      next();
    } catch (error) {
      if (error instanceof z.ZodError) {
        return res.status(400).json({
          success: false,
          error: {
            code: "VALIDATION_ERROR",
            message: "Invalid request data",
            details: error.flatten().fieldErrors,
          },
        });
      }
      next(error);
    }
  };
};

NestJS 模式

// users/users.controller.ts
import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  Query,
} from "@nestjs/common";
import { ApiTags, ApiOperation, ApiResponse } from "@nestjs/swagger";
import { UsersService } from "./users.service";
import { CreateUserDto, UpdateUserDto, GetUsersQueryDto } from "./dto";

@ApiTags("users")
@Controller("users")
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Post()
  @ApiOperation({ summary: "Create user" })
  @ApiResponse({ status: 201, description: "User created" })
  @ApiResponse({ status: 400, description: "Validation error" })
  async create(@Body() dto: CreateUserDto) {
    return this.usersService.create(dto);
  }

  @Get()
  @ApiOperation({ summary: "List users" })
  async findAll(@Query() query: GetUsersQueryDto) {
    return this.usersService.findAll(query);
  }

  @Get(":id")
  @ApiOperation({ summary: "Get user by ID" })
  async findOne(@Param("id") id: string) {
    return this.usersService.findOne(id);
  }

  @Put(":id")
  @ApiOperation({ summary: "Update user" })
  async update(@Param("id") id: string, @Body() dto: UpdateUserDto) {
    return this.usersService.update(id, dto);
  }

  @Delete(":id")
  @ApiOperation({ summary: "Delete user" })
  async remove(@Param("id") id: string) {
    return this.usersService.remove(id);
  }
}

FastAPI 模式(Python)

# routers/users.py
from fastapi import APIRouter, Depends, HTTPException, Query
from typing import List, Optional
from pydantic import BaseModel, EmailStr, Field

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

class CreateUserDto(BaseModel):
    email: EmailStr
    name: str = Field(..., min_length=2)
    password: str = Field(..., min_length=8)

class UserResponse(BaseModel):
    id: str
    email: str
    name: str
    role: str
    created_at: datetime

class PaginatedResponse(BaseModel):
    data: List[UserResponse]
    total: int
    page: int
    limit: int

@router.post("/", status_code=201, response_model=UserResponse)
async def create_user(dto: CreateUserDto, service: UserService = Depends()):
    return await service.create(dto)

@router.get("/", response_model=PaginatedResponse)
async def list_users(
    page: int = Query(1, ge=1),
    limit: int = Query(10, ge=1, le=100),
    search: Optional[str] = None,
    service: UserService = Depends()
):
    return await service.find_all(page, limit, search)

响应格式标准

// Success Response
{
  "success": true,
  "data": { ... },
  "meta": {
    "page": 1,
    "limit": 10,
    "total": 50,
    "totalPages": 5
  }
}

// Error Response
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Invalid request data",
    "details": {
      "email": ["Invalid email address"],
      "password": ["Password must contain uppercase letter"]
    }
  }
}

状态码

  • 200:成功(GET、PUT)
  • 201:已创建(POST)
  • 204:无内容(DELETE)
  • 400:验证错误
  • 401:未授权
  • 403:禁止访问
  • 404:未找到
  • 409:冲突
  • 500:服务器错误

最佳实践

  1. 为所有内容添加类型:请求、响应、DTO、错误
  2. 尽早验证:在进入服务层之前进行验证
  3. 统一响应:所有位置使用相同的结构
  4. 遵循 HTTP 语义:使用正确的状态码
  5. 错误详情:包含验证错误
  6. 分页:始终对列表进行分页
  7. 筛选/排序:支持常见查询
  8. 文档:OpenAPI/Swagger 规范

输出检查清单

  • 包含 HTTP 方法的路由定义
  • 请求验证架构
  • 所有 DTO 的 TypeScript 类型
  • 包含错误处理的控制器处理程序
  • 统一的响应格式
  • 为列表端点添加分页
  • 正确使用 HTTP 状态码
  • 错误响应格式
  • OpenAPI/Swagger 文档
  • 使用 curl/requests 的示例

评论

支持 Markdown