contract-validation
Validates frontend-backend API contracts. Apply when modifying API endpoints or service files.
When & Why to Use This Skill
This Claude skill automates the validation of frontend-backend API contracts, ensuring that data schemas, endpoint routes, and naming conventions remain perfectly synchronized to prevent integration bugs and runtime errors.
Use Cases
- Synchronizing backend Pydantic models with frontend TypeScript interfaces during API endpoint modifications.
- Verifying the existence and correctness of backend routes when adding new service calls in the frontend.
- Detecting and resolving naming convention mismatches, such as Python's snake_case versus TypeScript's camelCase.
- Ensuring field optionality and enum value consistency across the full stack to maintain type safety.
- Auditing existing codebases to ensure all API calls utilize a centralized API client rather than direct fetch/axios calls.
| name | contract-validation |
|---|---|
| description | "Validates frontend-backend API contracts. Apply when modifying API endpoints or service files." |
Contract Validation Skill
Ensures frontend and backend API contracts remain synchronized.
When to Apply
- Modifying API endpoints (backend)
- Modifying service/API calls (frontend)
- Adding new endpoints
- Changing request/response schemas
Core Rules
1. Schema Consistency
Backend response MUST match frontend TypeScript interface:
# Backend (Python/FastAPI)
class UserResponse(BaseModel):
user_id: str
email: str
created_at: datetime
// Frontend (TypeScript)
interface UserResponse {
user_id: string; // str -> string
email: string;
created_at: string; // datetime -> ISO string
}
2. Endpoint Existence
Every frontend API call needs a backend endpoint:
// Frontend
export const getUser = (id: string) => api.get(`/users/${id}`);
# Backend MUST have:
@router.get("/users/{user_id}")
async def get_user(user_id: str) -> UserResponse:
...
3. Use Centralized API Client
// CORRECT
import api from '../utils/api';
const response = await api.get('/users/123');
// WRONG - direct axios/fetch
import axios from 'axios';
const response = await axios.get('http://localhost:8000/api/users/123');
Common Mismatches
Snake Case vs Camel Case
# Backend returns
{"user_id": "123", "created_at": "2025-01-01"}
// Frontend expects - MISMATCH!
interface User {
userId: string; // Wrong: should be user_id
createdAt: string; // Wrong: should be created_at
}
Optional vs Required
# Backend - field is optional
class Response(BaseModel):
data: Optional[str] = None
// Frontend - assumes required - BUG!
interface Response {
data: string; // Should be: data?: string
}
Enum Mismatches
# Backend: lowercase
class Status(str, Enum):
PENDING = "pending"
// Frontend: uppercase - WRONG
type Status = "PENDING"; // Should be "pending"
Verification Commands
# Find backend routes
grep -rn "@router\." backend/ --include="*.py"
grep -rn "router\.\(get\|post\)" backend/ --include="*.ts"
# Find frontend API calls
grep -rn "api\.\(get\|post\)" src/ --include="*.ts"
Checklist
- Backend endpoint exists for frontend call
- HTTP methods match
- Field names match (snake_case)
- Field types match
- Optional fields marked with
? - Enum values match exactly