api-route-design by awais68/phase-5-cloud-deployment
npx skills add https://github.com/awais68/phase-5-cloud-deployment --skill api-route-design精通 RESTful API 的设计与实现,具备正确的验证、响应格式化和 HTTP 语义。
| 模式 | 示例 | 用途 |
|---|---|---|
| 列出资源 | @router.get("/fees/", response_model=List[FeeOut]) | 检索集合 |
| 按 ID 获取 | @router.get("/fees/{fee_id}") | 检索单个资源 |
| 创建 | @router.post("/fees/", response_model=FeeOut, status_code=201) | 创建新资源 |
| 更新 | @router.put("/fees/{fee_id}") |
Expert design and implementation of RESTful APIs with proper validation, response formatting, and HTTP semantics.
| Pattern | Example | Purpose |
|---|---|---|
| List resource | @router.get("/fees/", response_model=List[FeeOut]) | Retrieve collection |
| Get by ID | @router.get("/fees/{fee_id}") | Retrieve single resource |
| Create | @router.post("/fees/", response_model=FeeOut, status_code=201) | Create new resource |
| Update | @router.put("/fees/{fee_id}") |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| 完整资源更新 |
| 部分更新 | @router.patch("/fees/{fee_id}") | 部分资源更新 |
| 删除 | @router.delete("/fees/{fee_id}", status_code=204) | 移除资源 |
/v1/{resource} # 集合端点
/v1/{resource}/{id} # 单个资源端点
/v1/{resource}/{id}/sub # 嵌套资源端点
规则:
/student-fees 而不是 /studentFees/users 而不是 /user| 代码 | 用途 | 示例 |
|---|---|---|
| 200 | 成功 | 成功的 GET、PUT、PATCH 请求 |
| 201 | 已创建 | 成功的 POST 请求(资源已创建) |
| 202 | 已接受 | 异步操作已启动 |
| 204 | 无内容 | 成功的 DELETE 请求 |
| 400 | 错误请求 | 无效输入,验证失败 |
| 401 | 未授权 | 缺少或无效的身份验证 |
| 403 | 禁止访问 | 已认证但未授权 |
| 404 | 未找到 | 资源不存在 |
| 422 | 无法处理的实体 | 验证错误(Pydantic) |
| 500 | 内部服务器错误 | 意外的服务器错误 |
from fastapi import APIRouter, HTTPException
from typing import Annotated
router = APIRouter()
@router.get("/fees/{fee_id}")
async def get_fee(fee_id: int):
fee = await get_fee_by_id(fee_id)
if not fee:
raise HTTPException(status_code=404, detail="Fee not found")
return fee
@router.get("/fees/", response_model=List[FeeOut])
async def list_fees(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
status: str | None = Query(None, pattern="^(pending|paid|overdue)$"),
sort_by: str = Query("created_at", enum=["created_at", "amount", "due_date"]),
sort_order: str = Query("desc", enum=["asc", "desc"]),
):
return await paginate_fees(
skip=skip,
limit=limit,
status=status,
sort_by=sort_by,
sort_order=sort_order,
)
from pydantic import BaseModel
from datetime import datetime
class FeeCreate(BaseModel):
student_id: int
amount: float = Field(..., gt=0)
due_date: datetime
description: str | None = None
class FeeUpdate(BaseModel):
amount: float | None = Field(None, gt=0)
status: str | None = Field(None, pattern="^(pending|paid|overdue)$")
due_date: datetime | None = None
@router.post("/fees/", response_model=FeeOut, status_code=201)
async def create_fee(fee_in: FeeCreate):
return await create_fee_db(fee_in)
@router.patch("/fees/{fee_id}", response_model=FeeOut)
async def update_fee(fee_id: int, fee_in: FeeUpdate):
return await update_fee_db(fee_id, fee_in)
class FeeOut(BaseModel):
id: int
student_id: int
amount: float
status: str
created_at: datetime
due_date: datetime
class PaginatedResponse(BaseModel):
data: List[FeeOut]
total: int
skip: int
limit: int
has_more: bool
class ErrorResponse(BaseModel):
error: str
detail: str | None = None
code: str | None = None
from fastapi import APIRouter, Depends, HTTPException, Query, status
from typing import List, Annotated
router = APIRouter(prefix="/v1/fees", tags=["fees"])
@router.get(
"/",
response_model=PaginatedResponse[FeeOut],
summary="列出费用",
description="检索可筛选的分页费用列表。",
)
async def list_fees(
skip: Annotated[int, Query(0, ge=0)] = 0,
limit: Annotated[int, Query(100, ge=1, le=1000)] = 100,
status: Annotated[str | None, Query(pattern="^(pending|paid|overdue)$")] = None,
_current_user: User = Depends(get_current_user),
) -> PaginatedResponse[FeeOut]:
fees, total = await get_fees(
skip=skip, limit=limit, status=status, user=_current_user
)
return PaginatedResponse(
data=fees,
total=total,
skip=skip,
limit=limit,
has_more=(skip + limit) < total,
)
@router.get(
"/{fee_id}",
response_model=FeeOut,
responses={404: {"model": ErrorResponse}},
)
async def get_fee(
fee_id: int,
_current_user: User = Depends(get_current_user),
) -> FeeOut:
fee = await get_fee_by_id(fee_id)
if not fee:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Fee not found",
)
return fee
@router.post(
"/",
response_model=FeeOut,
status_code=status.HTTP_201_CREATED,
responses={400: {"model": ErrorResponse}},
)
async def create_fee(
fee_in: FeeCreate,
_current_user: User = Depends(get_current_user),
) -> FeeOut:
return await create_fee_db(fee_in, created_by=_current_user.id)
| 技能 | 集成点 |
|---|---|
@fastapi-app | 在 main.py 中注册路由器 |
@sqlmodel-crud | 端点中的数据库操作 |
@jwt-auth | 受保护路由使用 Depends(get_current_user) |
@api-client | 此 API 设计的消费者 |
has_more 指示器的 skip/limitsort_by 和 sort_order 参数response_modelsummary 和 description@router.get("/items/", response_model=PaginatedResponse[ItemOut])
async def list_items(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
) -> PaginatedResponse[ItemOut]:
items, total = await get_items(skip=skip, limit=limit)
return PaginatedResponse(
data=items,
total=total,
skip=skip,
limit=limit,
has_more=(skip + limit) < total,
)
@router.get("/items/")
async def list_items(
# 过滤
category: str | None = None,
status: str | None = Query(None, pattern="^(active|inactive)$"),
min_amount: float | None = Query(None, ge=0),
# 排序
sort_by: str = Query("created_at", enum=["created_at", "amount", "name"]),
sort_order: str = Query("desc", enum=["asc", "desc"]),
):
return await get_items(
filters={"category": category, "status": status, "min_amount": min_amount},
order_by=f"{sort_order} {sort_by.lstrip('-')}",
)
每周安装数
1
仓库
首次出现
1 天前
安全审计
安装于
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1
| Full resource update |
| Patch | @router.patch("/fees/{fee_id}") | Partial resource update |
| Delete | @router.delete("/fees/{fee_id}", status_code=204) | Remove resource |
/v1/{resource} # Collection endpoints
/v1/{resource}/{id} # Single resource endpoints
/v1/{resource}/{id}/sub # Nested resource endpoints
Rules:
/student-fees not /studentFees/users not /user| Code | Usage | Example |
|---|---|---|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST (resource created) |
| 202 | Accepted | Async operation started |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Invalid input, validation failed |
| 401 | Unauthorized | Missing or invalid auth |
| 403 | Forbidden | Authenticated but not authorized |
| 404 | Not Found | Resource doesn't exist |
| 422 | Unprocessable Entity | Validation errors (Pydantic) |
| 500 | Internal Server Error | Unexpected server error |
from fastapi import APIRouter, HTTPException
from typing import Annotated
router = APIRouter()
@router.get("/fees/{fee_id}")
async def get_fee(fee_id: int):
fee = await get_fee_by_id(fee_id)
if not fee:
raise HTTPException(status_code=404, detail="Fee not found")
return fee
@router.get("/fees/", response_model=List[FeeOut])
async def list_fees(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
status: str | None = Query(None, pattern="^(pending|paid|overdue)$"),
sort_by: str = Query("created_at", enum=["created_at", "amount", "due_date"]),
sort_order: str = Query("desc", enum=["asc", "desc"]),
):
return await paginate_fees(
skip=skip,
limit=limit,
status=status,
sort_by=sort_by,
sort_order=sort_order,
)
from pydantic import BaseModel
from datetime import datetime
class FeeCreate(BaseModel):
student_id: int
amount: float = Field(..., gt=0)
due_date: datetime
description: str | None = None
class FeeUpdate(BaseModel):
amount: float | None = Field(None, gt=0)
status: str | None = Field(None, pattern="^(pending|paid|overdue)$")
due_date: datetime | None = None
@router.post("/fees/", response_model=FeeOut, status_code=201)
async def create_fee(fee_in: FeeCreate):
return await create_fee_db(fee_in)
@router.patch("/fees/{fee_id}", response_model=FeeOut)
async def update_fee(fee_id: int, fee_in: FeeUpdate):
return await update_fee_db(fee_id, fee_in)
class FeeOut(BaseModel):
id: int
student_id: int
amount: float
status: str
created_at: datetime
due_date: datetime
class PaginatedResponse(BaseModel):
data: List[FeeOut]
total: int
skip: int
limit: int
has_more: bool
class ErrorResponse(BaseModel):
error: str
detail: str | None = None
code: str | None = None
from fastapi import APIRouter, Depends, HTTPException, Query, status
from typing import List, Annotated
router = APIRouter(prefix="/v1/fees", tags=["fees"])
@router.get(
"/",
response_model=PaginatedResponse[FeeOut],
summary="List fees",
description="Retrieve a paginated list of fees with optional filtering.",
)
async def list_fees(
skip: Annotated[int, Query(0, ge=0)] = 0,
limit: Annotated[int, Query(100, ge=1, le=1000)] = 100,
status: Annotated[str | None, Query(pattern="^(pending|paid|overdue)$")] = None,
_current_user: User = Depends(get_current_user),
) -> PaginatedResponse[FeeOut]:
fees, total = await get_fees(
skip=skip, limit=limit, status=status, user=_current_user
)
return PaginatedResponse(
data=fees,
total=total,
skip=skip,
limit=limit,
has_more=(skip + limit) < total,
)
@router.get(
"/{fee_id}",
response_model=FeeOut,
responses={404: {"model": ErrorResponse}},
)
async def get_fee(
fee_id: int,
_current_user: User = Depends(get_current_user),
) -> FeeOut:
fee = await get_fee_by_id(fee_id)
if not fee:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail="Fee not found",
)
return fee
@router.post(
"/",
response_model=FeeOut,
status_code=status.HTTP_201_CREATED,
responses={400: {"model": ErrorResponse}},
)
async def create_fee(
fee_in: FeeCreate,
_current_user: User = Depends(get_current_user),
) -> FeeOut:
return await create_fee_db(fee_in, created_by=_current_user.id)
| Skill | Integration Point |
|---|---|
@fastapi-app | Router registration in main.py |
@sqlmodel-crud | Database operations in endpoints |
@jwt-auth | Depends(get_current_user) for protected routes |
@api-client | Consumer of this API design |
skip/limit with has_more indicatorsort_by and sort_order parametersresponse_modelsummary and description for OpenAPI@router.get("/items/", response_model=PaginatedResponse[ItemOut])
async def list_items(
skip: int = Query(0, ge=0),
limit: int = Query(100, ge=1, le=1000),
) -> PaginatedResponse[ItemOut]:
items, total = await get_items(skip=skip, limit=limit)
return PaginatedResponse(
data=items,
total=total,
skip=skip,
limit=limit,
has_more=(skip + limit) < total,
)
@router.get("/items/")
async def list_items(
# Filtering
category: str | None = None,
status: str | None = Query(None, pattern="^(active|inactive)$"),
min_amount: float | None = Query(None, ge=0),
# Sorting
sort_by: str = Query("created_at", enum=["created_at", "amount", "name"]),
sort_order: str = Query("desc", enum=["asc", "desc"]),
):
return await get_items(
filters={"category": category, "status": status, "min_amount": min_amount},
order_by=f"{sort_order} {sort_by.lstrip('-')}",
)
Weekly Installs
1
Repository
First Seen
1 day ago
Security Audits
Installed on
amp1
cline1
opencode1
cursor1
kimi-cli1
codex1
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
147,400 周安装