关于 api-endpoint-generator
api-endpoint-generator 是用于生成带验证和类型化响应的 CRUD API 端点的技能。它提供 Express + TypeScript、NestJS 和 FastAPI 模式,并涵盖错误处理及 OpenAPI/Swagger 文档。
主要功能
- 01生成 CRUD 路由模式
- 02添加请求验证模式
- 03定义类型化请求与响应数据
- 04提供一致的错误格式和状态码
- 05包含分页及查询指引
- 06生成 OpenAPI/Swagger 文档
适用场景
- 01为新资源创建 API 端点
- 02为 API 请求处理添加验证
- 03统一响应和错误格式
- 04记录 API 路由并提供示例
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ patricio0312rev/ skills --skill api-endpoint-generator安装后在 Claude Code 中输入/api-endpoint-generator确认已加载。
详细介绍
API 端点生成器
生成可用于生产环境的 CRUD API 端点,并具备验证和类型安全。
核心工作流
- 定义资源:实体名称和架构
- 生成路由:POST、GET、PUT/PATCH、DELETE 端点
- 添加验证:使用 Zod/Joi 验证请求正文/查询参数
- 定义响应类型:为所有响应定义 TypeScript 接口
- 错误处理:统一错误响应
- 文档:OpenAPI/Swagger 规范
- 示例:请求/响应示例
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:服务器错误
最佳实践
- 为所有内容添加类型:请求、响应、DTO、错误
- 尽早验证:在进入服务层之前进行验证
- 统一响应:所有位置使用相同的结构
- 遵循 HTTP 语义:使用正确的状态码
- 错误详情:包含验证错误
- 分页:始终对列表进行分页
- 筛选/排序:支持常见查询
- 文档:OpenAPI/Swagger 规范
输出检查清单
- 包含 HTTP 方法的路由定义
- 请求验证架构
- 所有 DTO 的 TypeScript 类型
- 包含错误处理的控制器处理程序
- 统一的响应格式
- 为列表端点添加分页
- 正确使用 HTTP 状态码
- 错误响应格式
- OpenAPI/Swagger 文档
- 使用 curl/requests 的示例
评论
支持 Markdown