Install any skill in seconds. Free to start, no credit card required.
Get Started Free →API design and implementation across REST, GraphQL, gRPC, and tRPC patterns. Use when building backend services, public APIs, or service-to-service communication. Covers REST frameworks (FastAPI, Axum, Gin, Hono), GraphQL libraries (Strawberry, async-graphql, gqlgen, Pothos), gRPC (Tonic, Connect-Go), tRPC for TypeScript, pagination strategies (cursor-based, offset-based), rate limiting, caching, versioning, and OpenAPI documentation generation. Includes frontend integration patterns for forms,
.claude/skills/ancoleman-implementing-api-patterns/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-12 | ✗→✓ | ▲ Improved | 177% | 0% |
| case-06 | ✓→✓ | = Same ✓ | 87% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 129% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 230% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 96% | 0% |
Design and implement APIs using the optimal pattern and framework for the use case. Choose between REST, GraphQL, gRPC, and tRPC based on API consumers, performance requirements, and type safety needs.
Use when:
WHO CONSUMES YOUR API?
├─ PUBLIC/THIRD-PARTY DEVELOPERS → REST with OpenAPI
│ ├─ Python → FastAPI (auto-docs, 40k req/s)
│ ├─ TypeScript → Hono (edge-first, 50k req/s, 14KB)
│ ├─ Rust → Axum (140k req/s, <1ms latency)
│ └─ Go → Gin (100k+ req/s, mature ecosystem)
│
├─ FRONTEND TEAM (same org)
│ ├─ TypeScript full-stack? → tRPC (E2E type safety)
│ └─ Complex data needs? → GraphQL
│ ├─ Python → Strawberry
│ ├─ Rust → async-graphql
│ ├─ Go → gqlgen
│ └─ TypeScript → Pothos
│
├─ SERVICE-TO-SERVICE (microservices)
│ └─ High performance → gRPC
│ ├─ Rust → Tonic
│ ├─ Go → Connect-Go (browser-friendly)
│ └─ Python → grpcio
│
└─ MOBILE APPS
├─ Bandwidth constrained → GraphQL (request only needed fields)
└─ Simple CRUD → REST (standard, well-understood)Key Features: Auto OpenAPI docs, Pydantic v2 validation, async/await, 40k req/s
Basic Example:
pythonfrom fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float @app.post("/items") async def create_item(item: Item): return {"id": 1, **item.dict()}
See references/rest-design-principles.md for FastAPI patterns and examples/python-fastapi/.
Key Features: 14KB bundle, runs on any runtime (Node/Deno/Bun/edge), Zod validation, 50k req/s
Basic Example:
typescriptimport { Hono } from 'hono' import { zValidator } from '@hono/zod-validator' import { z } from 'zod' const app = new Hono() app.post('/items', zValidator('json', z.object({ name: z.string(), price: z.number() })), (c) => c.json({ id: 1, ...c.req.valid('json') }))
See references/rest-design-principles.md for Hono patterns and examples/typescript-hono/.
Key Features: Zero codegen, E2E type safety, React Query integration, WebSocket subscriptions
Basic Example:
typescriptimport { initTRPC } from '@trpc/server' import { z } from 'zod' const t = initTRPC.create() export const appRouter = t.router({ createItem: t.procedure .input(z.object({ name: z.string(), price: z.number() })) .mutation(({ input }) => ({ id: '1', ...input })) }) export type AppRouter = typeof appRouter
See references/trpc-setup-guide.md for setup patterns and examples/typescript-trpc/.
Key Features: Tower middleware, type-safe extractors, 140k req/s, compile-time verification
Basic Example:
rustuse axum::{routing::post, Json, Router}; use serde::{Deserialize, Serialize}; #[derive(Deserialize)] struct CreateItem { name: String, price: f64 } #[derive(Serialize)] struct Item { id: u64, name: String, price: f64 } async fn create_item(Json(payload): Json<CreateItem>) -> Json<Item> { Json(Item { id: 1, name: payload.name, price: payload.price }) }
See references/rest-design-principles.md for Axum patterns and examples/rust-axum/.
Key Features: Largest Go ecosystem, 100k+ req/s, struct tag validation
Basic Example:
gotype Item struct { Name string `json:"name" binding:"required"` Price float64 `json:"price" binding:"required,gt=0"` } r := gin.Default() r.POST("/items", func(c *gin.Context) { var item Item if c.ShouldBindJSON(&item); err != nil { c.JSON(400, gin.H{"error": err.Error()}); return } c.JSON(201, item) })
See references/rest-design-principles.md for Gin patterns and examples/go-gin/.
| Language | Framework | Req/s | Latency | Cold Start | Memory | Best For | |----------|-----------|-------|---------|------------|--------|----------| | Rust | Actix-web | ~150k | <1ms | N/A | 2-5MB | Maximum throughput | | Rust | Axum | ~140k | <1ms | N/A | 2-5MB | Ergonomics + performance | | Go | Gin | ~100k+ | 1-2ms | N/A | 5-10MB | Mature ecosystem | | TypeScript | Hono | ~50k | <5ms | <5ms | 128MB | Edge deployment | | Python | FastAPI | ~40k | 5-10ms | 1-2s | 30-50MB | Developer experience | | TypeScript | Express | ~15k | 10-20ms | 1-3s | 50-100MB | Legacy systems |
Notes:
Advantages: Handles real-time changes, no skipped/duplicate records, scales to billions
FastAPI Example:
python@app.get("/items") async def list_items(cursor: Optional[str] = None, limit: int = 20): query = db.query(Item).filter(Item.id > cursor) if cursor else db.query(Item) items = query.limit(limit).all() return { "items": items, "next_cursor": items[-1].id if items else None, "has_more": len(items) == limit }
Use only for static datasets (<10k records) with direct page access needs.
See references/pagination-patterns.md for complete patterns and frontend integration.
| Framework | OpenAPI Support | Docs UI | Configuration | |-----------|----------------|---------|---------------| | FastAPI | Automatic | Swagger UI + ReDoc | Built-in | | Hono | Middleware plugin | Swagger UI | @hono/swagger-ui | | Axum | utoipa crate | Swagger UI | Manual annotations | | Gin | swaggo/swag | Swagger UI | Comment annotations |
FastAPI Example (Zero Config):
pythonapp = FastAPI(title="My API", version="1.0.0") @app.post("/items", tags=["items"]) async def create_item(item: Item) -> Item: """Create item with name and price""" return item # Docs at /docs, /redoc, /openapi.json
See references/openapi-documentation.md for framework-specific setup. Use scripts/generate_openapi.py to extract specs programmatically.
Backend:
pythonclass UserCreate(BaseModel): email: EmailStr; name: str; age: int @app.post("/api/users", status_code=201) async def create_user(user: UserCreate): return {"id": 1, **user.dict()}
Frontend:
typescriptconst res = await fetch('/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }) if (!res.ok) throw new Error((await res.json()).detail)
See cursor pagination example above and references/pagination-patterns.md.
Backend:
pythonfrom sse_starlette.sse import EventSourceResponse @app.post("/api/chat") async def chat(message: str): async def gen(): for chunk in llm_stream(message): yield {"event": "message", "data": chunk} return EventSourceResponse(gen())
Frontend:
typescriptconst es = new EventSource('/api/chat') es.addEventListener('message', (e) => appendToChat(e.data))
See examples/ for complete integration examples with each frontend skill.
FastAPI Example (Token Bucket):
pythonfrom slowapi import Limiter from slowapi.util import get_remote_address limiter = Limiter(key_func=get_remote_address) app.state.limiter = limiter @app.get("/items") @limiter.limit("100/minute") async def list_items(): return {"items": []}
See references/rate-limiting-strategies.md for sliding window, distributed patterns, and Redis implementation.
Use when frontend needs flexible data fetching or mobile apps have bandwidth constraints.
By Language:
See references/graphql-schema-design.md for schema patterns and N+1 prevention. See examples/graphql-strawberry/ for complete Python example.
Use for service-to-service communication with strong typing and high performance.
By Language:
See references/grpc-protobuf-guide.md for Protocol Buffers guide. See examples/grpc-tonic/ for complete Rust example.
references/rest-design-principles.md - REST resource modeling, HTTP methods, status codesreferences/graphql-schema-design.md - Schema patterns, resolver optimization, N+1 preventionreferences/grpc-protobuf-guide.md - Proto3 syntax, service definitions, streamingreferences/trpc-setup-guide.md - Router patterns, middleware, Zod validationreferences/pagination-patterns.md - Cursor vs offset with mathematical explanationreferences/rate-limiting-strategies.md - Token bucket, sliding window, Redisreferences/caching-patterns.md - HTTP caching, application caching strategiesreferences/versioning-strategies.md - URI, header, media type versioningreferences/openapi-documentation.md - Swagger/OpenAPI best practices by frameworkscripts/generate_openapi.py - Generate OpenAPI spec from codescripts/validate_api_spec.py - Validate OpenAPI 3.1 compliancescripts/benchmark_endpoints.py - Load test API endpointsexamples/python-fastapi/ - Complete FastAPI REST APIexamples/typescript-hono/ - Hono edge-first APIexamples/typescript-trpc/ - tRPC E2E type-safe APIexamples/rust-axum/ - Axum REST APIexamples/go-gin/ - Gin REST APIexamples/graphql-strawberry/ - Python GraphQLexamples/grpc-tonic/ - Rust gRPCChoose REST when: Public API, standard CRUD, need caching, OpenAPI docs required Choose GraphQL when: Frontend needs flexible queries, mobile bandwidth constraints, complex nested data Choose gRPC when: Service-to-service communication, high performance, bidirectional streaming Choose tRPC when: TypeScript full-stack, same team owns frontend + backend, E2E type safety
Pagination: Always use cursor-based for production scale, offset-based only for simple cases Documentation: Prefer frameworks with automatic OpenAPI generation (FastAPI, Hono) Performance: Rust (Axum) for max throughput, Go (Gin) for maturity, Python (FastAPI) for DX
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-06 | pass→pass | 18,603 | 12,782 | -31% | 1 | 1 | 0% | 2,937 | 5,505 | +87% | 0 | 0 | — |
case-01 | pass→pass | 11,780 | 8,229 | -30% | 1 | 1 | 0% | 2,104 | 4,808 | +129% | 0 | 0 | — |
case-02 | pass→pass | 9,034 | 10,034 | +11% | 1 | 1 | 0% | 1,575 | 5,193 | +230% | 0 | 0 | — |
case-03 | pass→pass | 17,351 | 13,176 | -24% | 1 | 1 | 0% | 2,934 | 5,761 | +96% | 0 | 0 | — |
case-04 | pass→pass | 12,438 | 6,391 | -49% | 1 | 1 | 0% | 2,217 | 4,311 | +94% | 0 | 0 | — |
case-05 | pass→pass | 14,336 | 6,631 | -54% | 1 | 1 | 0% | 2,433 | 4,454 | +83% | 0 | 0 | — |
case-11 | pass→pass | 10,105 | 11,298 | +12% | 1 | 1 | 0% | 1,672 | 5,083 | +204% | 0 | 0 | — |
case-21 | pass→pass | 5,942 | 5,093 | -14% | 1 | 1 | 0% | 973 | 3,998 | +311% | 0 | 0 | — |
case-22 | pass→pass | 15,913 | 14,926 | -6% | 1 | 1 | 0% | 2,751 | 6,046 | +120% | 0 | 0 | — |
case-07 | pass→pass | 13,070 | 8,682 | -34% | 1 | 1 | 0% | 2,173 | 4,767 | +119% | 0 | 0 | — |
case-08 | pass→pass | 15,963 | 9,656 | -40% | 1 | 1 | 0% | 2,409 | 4,858 | +102% | 0 | 0 | — |
case-09 | pass→pass | 12,489 | 10,337 | -17% | 1 | 1 | 0% | 1,993 | 4,991 | +150% | 0 | 0 | — |
case-10 | pass→pass | 13,956 | 8,713 | -38% | 1 | 1 | 0% | 2,069 | 4,530 | +119% | 0 | 0 | — |
case-12 | fail→pass | 10,240 | 7,047 | -31% | 1 | 1 | 0% | 1,628 | 4,509 | +177% | 0 | 0 | — |
case-13 | pass→pass | 12,342 | 6,672 | -46% | 1 | 1 | 0% | 2,114 | 4,472 | +112% | 0 | 0 | — |
case-14 | pass→pass | 15,303 | 9,889 | -35% | 1 | 1 | 0% | 2,930 | 5,168 | +76% | 0 | 0 | — |
case-15 | pass→pass | 10,466 | 9,026 | -14% | 1 | 1 | 0% | 1,715 | 4,786 | +179% | 0 | 0 | — |
case-16 | pass→pass | 9,751 | 5,970 | -39% | 1 | 1 | 0% | 1,752 | 4,137 | +136% | 0 | 0 | — |
case-17 | pass→pass | 11,103 | 7,710 | -31% | 1 | 1 | 0% | 1,706 | 4,446 | +161% | 0 | 0 | — |
case-18 | pass→pass | 5,667 | 5,590 | -1% | 1 | 1 | 0% | 922 | 4,249 | +361% | 0 | 0 | — |
case-19 | pass→pass | 7,673 | 7,334 | -4% | 1 | 1 | 0% | 1,366 | 4,633 | +239% | 0 | 0 | — |
case-20 | pass→pass | 11,595 | 6,621 | -43% | 1 | 1 | 0% | 1,957 | 4,308 | +120% | 0 | 0 | — |
case-23 | pass→pass | 14,174 | 16,694 | +18% | 1 | 1 | 0% | 2,556 | 6,431 | +152% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 23 cases were attempted. The headline lift of +4 percentage points is the difference between those two pass rates over the 23 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.