Synthèse automatique
Ce skill fournit des principes et des exemples pour concevoir des API REST et GraphQL, avec un accent sur la structuration des ressources, le versioning et les bonnes pratiques.
Points forts
- Couvre les fondations REST (ressources, sans état, cache, interface uniforme) et plusieurs styles d’API.
- Illustre une API FastAPI avec validation Pydantic, pagination, codes de statut et documentation intégrée.
- Rassemble des points clés souvent oubliés : versioning, authentification, limitation de débit et gestion des erreurs.
Limites
- Le contenu semble tronqué au milieu de l’exemple FastAPI, sans implémentation complète des routes.
- GraphQL, gRPC, WebSocket et SSE sont seulement cités, sans modèle ni bonnes pratiques détaillées.
- Les stratégies de versioning et de sécurité sont annoncées mais peu approfondies.
À utiliser pour cadrer ou réviser la conception d’une API REST, notamment les conventions de routes, de pagination et de réponses d’erreur.
À éviter si vous cherchez un guide complet et opérationnel pour GraphQL, gRPC ou une implémentation FastAPI prête pour la production.
Analyse de sécurité
SûrThe skill is a pure documentation/instruction file with illustrative FastAPI snippets and no shell, network, or filesystem-destructive instructions. The only concerns are pedagogical: placeholder credentials and simplified auth/rate-limiting examples that a careless reader might copy into production. No prompt-injection or exfiltration attempt was found in the content.
- •Example JWT code uses a hardcoded placeholder secret (SECRET_KEY = "your-secret-key") and a fixed HS256 algorithm; it is clearly a placeholder, but readers could copy it verbatim into production.
- •Example API-key check compares against a module-level `valid_api_keys` collection with `not in` (not constant-time) and no key rotation/expiry story; this is illustrative code, not an active exploit.
- •The rate limiter is described as a 'token bucket algorithm' but is implemented as a fixed-window counter — an accuracy/security-hardening gap in the sample, not a runtime risk.
- •No execution risk: the skill only declares Read/Write/Edit, and all code blocks are static FastAPI documentation with stub bodies (`pass`, `return []`, `raise HTTPException`).
- •Example security section uses a hardcoded placeholder JWT signing key (literal string) — a reader copying the snippet verbatim would ship a predictable secret; should reference an environment variable instead
- •Rate-limiting example constructs a Redis client from an undefined `redis_client` and stores client identity by raw IP only, which is a weak abuse-prevention example
- •`verify_api_key` checks against an undefined `valid_api_keys` collection, and `get_user_from_db` is undefined — illustrative only, but misleading if copied
Exemples
Design a versioned REST API for user management with pagination, filtering, validation, and consistent error responses. Use FastAPI-style examples.Review these API endpoints for resource naming, HTTP methods, status codes, and pagination, and suggest a more consistent REST design.Compare URL, header, and media-type versioning for a public REST API and recommend one for backward compatibility.name: api-design-expert version: 1.0.0 description: Expert-level API design principles, REST, GraphQL, versioning, and API best practices category: api tags: [api-design, rest, graphql, api-versioning, api-security] allowed-tools:
- Read
- Write
- Edit
API Design Expert
Expert guidance for API design, RESTful principles, GraphQL, versioning strategies, and API best practices.
Core Concepts
API Design Principles
- RESTful architecture
- Resource-oriented design
- Uniform interface
- Statelessness
- Cacheability
- Layered system
API Styles
- REST (Representational State Transfer)
- GraphQL
- RPC (Remote Procedure Call)
- WebSocket
- Server-Sent Events (SSE)
- gRPC
Key Considerations
- Versioning strategies
- Authentication and authorization
- Rate limiting
- Error handling
- Documentation
- Backward compatibility
REST API Design
from fastapi import FastAPI, HTTPException, Query, Path, Header
from pydantic import BaseModel, Field
from typing import List, Optional
from datetime import datetime
from enum import Enum
app = FastAPI(
title="User Management API",
version="1.0.0",
description="RESTful API for user management"
)
# Models
class UserRole(str, Enum):
ADMIN = "admin"
USER = "user"
GUEST = "guest"
class UserCreate(BaseModel):
email: str = Field(..., example="user@example.com")
name: str = Field(..., min_length=1, max_length=100)
role: UserRole = UserRole.USER
class UserResponse(BaseModel):
id: str
email: str
name: str
role: UserRole
created_at: datetime
updated_at: datetime
class Config:
schema_extra = {
"example": {
"id": "123e4567-e89b-12d3-a456-426614174000",
"email": "user@example.com",
"name": "John Doe",
"role": "user",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-01T00:00:00Z"
}
}
class UserUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=100)
role: Optional[UserRole] = None
# REST Endpoints following best practices
@app.get("/api/v1/users",
response_model=List[UserResponse],
summary="List all users",
tags=["Users"])
async def list_users(
page: int = Query(1, ge=1, description="Page number"),
page_size: int = Query(20, ge=1, le=100, description="Items per page"),
sort: str = Query("created_at", description="Sort field"),
order: str = Query("desc", regex="^(asc|desc)$")
):
"""
Retrieve a paginated list of users.
- **page**: Page number (starts at 1)
- **page_size**: Number of items per page (1-100)
- **sort**: Field to sort by
- **order**: Sort order (asc or desc)
"""
# Implementation
return []
@app.get("/api/v1/users/{user_id}",
response_model=UserResponse,
summary="Get user by ID",
tags=["Users"])
async def get_user(
user_id: str = Path(..., description="User ID")
):
"""Retrieve a specific user by ID."""
# Implementation
raise HTTPException(status_code=404, detail="User not found")
@app.post("/api/v1/users",
response_model=UserResponse,
status_code=201,
summary="Create new user",
tags=["Users"])
async def create_user(user: UserCreate):
"""Create a new user."""
# Implementation
return UserResponse(
id="123e4567-e89b-12d3-a456-426614174000",
email=user.email,
name=user.name,
role=user.role,
created_at=datetime.now(),
updated_at=datetime.now()
)
@app.patch("/api/v1/users/{user_id}",
response_model=UserResponse,
summary="Update user",
tags=["Users"])
async def update_user(
user_id: str = Path(..., description="User ID"),
user: UserUpdate = None
):
"""Partially update a user."""
# Implementation
pass
@app.delete("/api/v1/users/{user_id}",
status_code=204,
summary="Delete user",
tags=["Users"])
async def delete_user(
user_id: str = Path(..., description="User ID")
):
"""Delete a user."""
# Implementation
pass
# Nested resources
@app.get("/api/v1/users/{user_id}/posts",
summary="Get user posts",
tags=["Users", "Posts"])
async def get_user_posts(user_id: str):
"""Retrieve all posts for a specific user."""
return []
API Versioning
from fastapi import APIRouter, Request
# URL Path Versioning (Recommended)
v1_router = APIRouter(prefix="/api/v1")
v2_router = APIRouter(prefix="/api/v2")
@v1_router.get("/users")
async def get_users_v1():
"""Version 1: Returns basic user info"""
return [{"id": 1, "name": "John"}]
@v2_router.get("/users")
async def get_users_v2():
"""Version 2: Returns enhanced user info"""
return [{"id": 1, "name": "John", "email": "john@example.com"}]
# Header Versioning
async def version_from_header(request: Request):
version = request.headers.get("API-Version", "1")
return version
@app.get("/api/users")
async def get_users(version: str = Depends(version_from_header)):
if version == "2":
return get_users_v2()
return get_users_v1()
# Content Negotiation Versioning
@app.get("/api/users")
async def get_users_content_negotiation(
accept: str = Header("application/vnd.api+json; version=1")
):
if "version=2" in accept:
return get_users_v2()
return get_users_v1()
Error Handling
from fastapi import FastAPI, HTTPException
from fastapi.responses import JSONResponse
from pydantic import BaseModel, ValidationError
class ErrorResponse(BaseModel):
error: str
message: str
details: Optional[dict] = None
timestamp: datetime
path: str
@app.exception_handler(HTTPException)
async def http_exception_handler(request: Request, exc: HTTPException):
return JSONResponse(
status_code=exc.status_code,
content=ErrorResponse(
error=exc.status_code,
message=exc.detail,
timestamp=datetime.now(),
path=request.url.path
).dict()
)
@app.exception_handler(ValidationError)
async def validation_exception_handler(request: Request, exc: ValidationError):
return JSONResponse(
status_code=422,
content=ErrorResponse(
error="validation_error",
message="Request validation failed",
details=exc.errors(),
timestamp=datetime.now(),
path=request.url.path
).dict()
)
# Custom business logic errors
class BusinessError(Exception):
def __init__(self, message: str, code: str):
self.message = message
self.code = code
@app.exception_handler(BusinessError)
async def business_error_handler(request: Request, exc: BusinessError):
return JSONResponse(
status_code=400,
content=ErrorResponse(
error=exc.code,
message=exc.message,
timestamp=datetime.now(),
path=request.url.path
).dict()
)
Rate Limiting
from fastapi import Request, HTTPException
from datetime import datetime, timedelta
import redis
from typing import Dict
class RateLimiter:
def __init__(self, redis_client: redis.Redis):
self.redis = redis_client
async def check_rate_limit(self,
key: str,
max_requests: int,
window_seconds: int) -> Dict:
"""
Token bucket algorithm for rate limiting
"""
now = datetime.now().timestamp()
window_key = f"rate_limit:{key}:{int(now // window_seconds)}"
pipe = self.redis.pipeline()
pipe.incr(window_key)
pipe.expire(window_key, window_seconds)
result = pipe.execute()
request_count = result[0]
if request_count > max_requests:
reset_time = (int(now // window_seconds) + 1) * window_seconds
raise HTTPException(
status_code=429,
detail="Rate limit exceeded",
headers={
"X-RateLimit-Limit": str(max_requests),
"X-RateLimit-Remaining": "0",
"X-RateLimit-Reset": str(reset_time)
}
)
return {
"limit": max_requests,
"remaining": max_requests - request_count,
"reset": (int(now // window_seconds) + 1) * window_seconds
}
# Middleware for rate limiting
@app.middleware("http")
async def rate_limit_middleware(request: Request, call_next):
limiter = RateLimiter(redis_client)
# Get client identifier (IP, API key, etc.)
client_id = request.client.host
try:
rate_info = await limiter.check_rate_limit(
client_id,
max_requests=100,
window_seconds=60
)
response = await call_next(request)
# Add rate limit headers
response.headers["X-RateLimit-Limit"] = str(rate_info["limit"])
response.headers["X-RateLimit-Remaining"] = str(rate_info["remaining"])
response.headers["X-RateLimit-Reset"] = str(rate_info["reset"])
return response
except HTTPException as e:
return JSONResponse(
status_code=e.status_code,
content={"error": e.detail},
headers=e.headers
)
HATEOAS Implementation
from typing import Dict, List
class HATEOASResponse(BaseModel):
data: dict
links: Dict[str, str]
def create_links(resource_id: str, resource_type: str) -> Dict[str, str]:
"""Create HATEOAS links for a resource"""
return {
"self": f"/api/v1/{resource_type}/{resource_id}",
"update": f"/api/v1/{resource_type}/{resource_id}",
"delete": f"/api/v1/{resource_type}/{resource_id}",
"collection": f"/api/v1/{resource_type}"
}
@app.get("/api/v1/users/{user_id}", response_model=HATEOASResponse)
async def get_user_with_links(user_id: str):
user = get_user_from_db(user_id)
return HATEOASResponse(
data=user,
links={
"self": f"/api/v1/users/{user_id}",
"posts": f"/api/v1/users/{user_id}/posts",
"profile": f"/api/v1/users/{user_id}/profile",
"update": f"/api/v1/users/{user_id}",
"delete": f"/api/v1/users/{user_id}"
}
)
API Security
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from jose import JWTError, jwt
from passlib.context import CryptContext
security = HTTPBearer()
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
SECRET_KEY = "your-secret-key"
ALGORITHM = "HS256"
async def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""Verify JWT token"""
try:
payload = jwt.decode(
credentials.credentials,
SECRET_KEY,
algorithms=[ALGORITHM]
)
return payload
except JWTError:
raise HTTPException(
status_code=401,
detail="Invalid authentication credentials"
)
@app.get("/api/v1/protected")
async def protected_route(token_payload: dict = Depends(verify_token)):
return {"message": "Access granted", "user": token_payload}
# API Key Authentication
async def verify_api_key(api_key: str = Header(...)):
"""Verify API key"""
if api_key not in valid_api_keys:
raise HTTPException(status_code=403, detail="Invalid API key")
return api_key
Best Practices
Design
- Use nouns for resources, not verbs
- Use plural names for collections
- Use HTTP methods correctly (GET, POST, PUT, PATCH, DELETE)
- Return appropriate status codes
- Support filtering, sorting, and pagination
- Use consistent naming conventions
- Version APIs from the start
Documentation
- Use OpenAPI/Swagger
- Provide example requests and responses
- Document error codes and messages
- Include authentication requirements
- Keep documentation up-to-date
- Provide SDKs when possible
Performance
- Implement caching (ETags, Cache-Control)
- Support compression (gzip)
- Paginate large result sets
- Use async operations for long tasks
- Implement rate limiting
- Monitor API performance
Security
- Use HTTPS always
- Implement authentication and authorization
- Validate all inputs
- Rate limit to prevent abuse
- Log security events
- Use API keys or OAuth 2.0
- Implement CORS properly
Anti-Patterns
❌ Using GET for state-changing operations ❌ Returning inconsistent response formats ❌ No versioning strategy ❌ Poor error messages ❌ No rate limiting ❌ Exposing internal implementation details ❌ Breaking changes without versioning
Resources
- REST API Design Best Practices: https://restfulapi.net/
- OpenAPI Specification: https://swagger.io/specification/
- API Design Guide: https://cloud.google.com/apis/design
- Microsoft REST API Guidelines: https://github.com/microsoft/api-guidelines
- FastAPI: https://fastapi.tiangolo.com/
Du fichier au premier essai
Le téléchargement contient exactement le contenu affiché sur cette fiche. Cette copie peut différer de la dernière version du dépôt source.
Consulter la source et ses fichiers annexesCe téléchargement contient uniquement SKILL.md. Les scripts, références, modèles et dépendances éventuels ne sont pas inclus. Récupérez les fichiers nécessaires depuis la source en conservant leur arborescence avant de poursuivre.
1. Télécharger le fichier
Télécharger SKILL.mdDepuis la racine d’un projet de test, enregistrez le fichier sous .skill-downloads/api-design-expert-mmlj2o5i/SKILL.md, ou utilisez la commande suivante. Ce dossier sert à examiner la skill avant son installation.
Commande pour macOS/Linux ou WSL : un shell POSIX et curl sont nécessaires. Sous Windows sans WSL, utilisez le bouton de téléchargement et créez le dossier manuellement.
mkdir -p '.skill-downloads/api-design-expert-mmlj2o5i' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/api-design-expert-mmlj2o5i/download' --output '.skill-downloads/api-design-expert-mmlj2o5i/SKILL.md.part' && mv '.skill-downloads/api-design-expert-mmlj2o5i/SKILL.md.part' '.skill-downloads/api-design-expert-mmlj2o5i/SKILL.md'2. Vérifier les prérequis
Lisez SKILL.md avant de l’utiliser. Si le fichier cite des scripts ou services externes, examinez-les et préparez les accès nécessaires dans votre environnement de test.
Aucune section de prérequis reconnue dans ce fichier. Cela ne signifie pas qu’il est autonome : les bibliothèques, outils et accès peuvent être cités ailleurs dans le contenu ou dans le dépôt source.
Les plateformes de la fiche sont des indications du catalogue. Les chemins ci-dessous suivent la documentation des outils ; ils ne prouvent pas que cette skill y a été exécutée. Aucune version minimale de ces outils n’est attestée pour cette fiche.
3. Charger la skill
Après vérification, copiez le dossier complet dans le projet avec l’une des commandes ci-dessous, puis ouvrez une nouvelle session de l’outil. La commande s’arrête si un dossier du même nom existe déjà : comparez les fichiers avant de le remplacer.
Claude Code
test -f '.skill-downloads/api-design-expert-mmlj2o5i/SKILL.md' && test -s '.skill-downloads/api-design-expert-mmlj2o5i/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/api-design-expert' && cp -R '.skill-downloads/api-design-expert-mmlj2o5i' '.claude/skills/api-design-expert'OpenSkills
OpenSkills doit déjà être installé. La procédure locale install/read est contrôlée avec OpenSkills 1.5.0, qui exige Node.js ≥ 20.6.0. Aucun slug du catalogue n’est résolu automatiquement.
Installation officielle d’OpenSkillsopenskills install './.skill-downloads/api-design-expert-mmlj2o5i'openskills read 'api-design-expert-mmlj2o5i'La lecture doit afficher le contenu du fichier installé et son dossier de base. Elle ne lance ni la skill ni ses dépendances.
Pour un agent qui lit AGENTS.md, synchronisez ensuite l’index. Cette commande modifie AGENTS.md : relisez le diff avant de l’utiliser.
openskills syncMode manuel ou autre outil : joignez SKILL.md à une conversation, ou ouvrez-le dans un agent capable de lire les fichiers. Ajoutez les ressources annexes nécessaires. Il n’existe pas de dossier universel reconnu par tous les outils.
4. Faire un premier essai
Utilisez une entrée courte et fictive : un endpoint d’API, un petit document ou un brief d’interface. Dans une nouvelle conversation, joignez le fichier ou donnez son chemin à l’agent, puis adaptez cette demande :
Lis .skill-downloads/api-design-expert-mmlj2o5i/SKILL.md et applique ses instructions à cet exemple : [décrire une entrée de test]. Indique les fichiers, dépendances ou accès qui manquent. Produis le résultat demandé par la skill sans inventer de données ni exécuter de script non examiné.Résultat attendu : une sortie correspondant aux instructions de la skill, ou une liste précise des prérequis manquants. Une simple confirmation de lecture ne valide pas le premier usage. Si une ressource externe est indisponible, ce parcours reste à tester.
Bonnes pratiques React Hook Form
Developpement
Maîtrisez React Hook Form : évitez les anti-patterns, gérez les abonnements, et traitez correctement reset, dirty state et entrées contrôlées.
Migration Emotion vers Mantine
Developpement
Migrer les styled-components Emotion vers des composants Mantine avec style props et modules CSS. Pour convertir .styled.tsx ou supprimer les imports @emotion.
Détecteur de dérive de périmètre
Developpement
Analyse les diffs git par rapport à une intention pour détecter la dérive de périmètre, fichiers non liés et PR trop larges, puis suggère de garder, diviser ou justifier.