API Design Expert
English | Bahasa Indonesia
English
Orchestration & Integration
- Upstream Ideation & Specs:
brainstorming, prd-architect, spec-driven-development-expert
- Downstream Implementations:
js-backend-expert, python-programming-expert, go-programming-expert, rust-programming-expert
- Contract & Gateway Tooling:
openapi-swagger-codegen-expert, api-gateway-proxy-expert, autonomous-api-drift-healer, graphql-apollo-expert
- Security & Quality:
rate-limit-abuse-prevention, autonomous-red-teamer, e2e-testing-expert, anti-slop
- Lifecycle & Governance:
zero-to-prod-orchestrator, session-memory-manager
Description
Expert guide for designing, documenting, and evolving production-grade APIs. Covers REST resource modeling and HTTP semantics, GraphQL schema design, gRPC with protobuf, and tRPC for end-to-end type-safe APIs in TypeScript monorepos. Includes OpenAPI 3.1 documentation, API versioning strategies, rate limiting, idempotency, and contract-first development workflows.
Trigger Conditions
- Designing a new API from scratch (REST, GraphQL, gRPC, or tRPC).
- Evolving an existing API without breaking clients.
- Documenting APIs with OpenAPI/Swagger.
- Implementing rate limiting, throttling, or idempotency.
- Choosing between REST, GraphQL, gRPC, and tRPC.
- Designing webhook systems.
- Implementing API authentication (API keys, JWT, OAuth2).
API Protocol Selection Guide
| Criteria | REST | GraphQL | gRPC | tRPC |
|---|
| Type Safety | Manual (OpenAPI) | Schema-enforced | Protobuf | End-to-end TS |
| Performance | Good | Good | Excellent (HTTP/2) | Good |
| Browser Support | Native | Native | Needs proxy | TS/JS only |
| Streaming | SSE / WebSocket | Subscriptions | Native bi-directional | SSE |
| Best For | Public APIs, mobile | Complex data graphs | Microservice-to-service | Next.js full-stack |
| Tooling | Universal | Rich ecosystem | Strong (Go, Java) | Next.js / Expo |
REST API Design Principles
Resource Naming Conventions
Collection: GET /api/v1/posts
Item: GET /api/v1/posts/{id}
Sub-resource:GET /api/v1/posts/{id}/comments
Action: POST /api/v1/posts/{id}/publish (use sparingly)
AVOID: /api/v1/getPosts, /api/v1/createPost, /api/v1/deletePost/{id}
HTTP Method Semantics
| Method | Idempotent | Safe | Use Case |
|---|
GET | Yes | Yes | Retrieve resources |
POST | No | No | Create resources, trigger actions |
PUT | Yes | No | Replace entire resource |
PATCH | No | No | Partial update |
DELETE | Yes | No | Remove resource |
Consistent Response Structure
// Success response
{
"data": { "id": "usr_01", "email": "user@example.com" },
"meta": { "requestId": "req_xyz", "timestamp": "2026-01-01T00:00:00Z" }
}
// Paginated list response
{
"data": [...],
"meta": {
"total": 1234,
"page": 2,
"pageSize": 20,
"hasNextPage": true,
"nextCursor": "eyJpZCI6"
}
}
// Error response (RFC 9457 Problem Details)
{
"type": "https://api.example.com/errors/validation-failed",
"title": "Validation Failed",
"status": 422,
"detail": "The 'email' field must be a valid email address.",
"instance": "/api/v1/users",
"errors": [
{ "field": "email", "message": "Invalid email format" }
]
}
HTTP Status Codes — Correct Usage
200 OK — Successful GET, PUT, PATCH
201 Created — Successful POST (include Location header)
204 No Content — Successful DELETE
400 Bad Request — Invalid request body or params
401 Unauthorized— Missing or invalid authentication
403 Forbidden — Authenticated but not authorized
404 Not Found — Resource not found
409 Conflict — Duplicate key, version conflict
422 Unprocessable Entity — Validation failure
429 Too Many Requests — Rate limit exceeded (include Retry-After)
500 Internal Server Error— Unexpected server error
API Versioning Strategies
Strategy 1 — URL Path (Recommended for public APIs):
GET /api/v1/users
GET /api/v2/users
Strategy 2 — Header:
GET /api/users
Accept: application/vnd.example.v2+json
Strategy 3 — Query Parameter (Avoid in production):
GET /api/users?version=2
Rules for non-breaking changes (no version bump needed):
- Adding new optional fields to responses.
- Adding new optional request parameters.
- Adding new endpoints.
Breaking changes (require new version):
- Removing or renaming fields.
- Changing field types.
- Changing error response structure.
tRPC — End-to-End Type Safety
// server/router.ts
import { initTRPC, TRPCError } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session?.user) throw new TRPCError({ code: 'UNAUTHORIZED' });
return next({ ctx: { ...ctx, user: ctx.session.user } });
});
export const appRouter = router({
users: router({
list: publicProcedure
.input(z.object({ page: z.number().int().min(1).default(1) }))
.query(async ({ input, ctx }) => {
return ctx.db.user.findMany({ skip: (input.page - 1) * 20, take: 20 });
}),
create: protectedProcedure
.input(z.object({ email: z.string().email(), name: z.string().min(2) }))
.mutation(async ({ input, ctx }) => {
return ctx.db.user.create({ data: input });
}),
}),
});
export type AppRouter = typeof appRouter;
// Client automatically infers all types — no code generation needed
Rate Limiting Implementation
// Sliding window rate limit with Redis (using Upstash)
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(100, '1 m'), // 100 requests/minute
analytics: true,
prefix: 'api:ratelimit',
});
// Next.js middleware usage
export async function rateLimitMiddleware(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
return new Response(JSON.stringify({ error: 'Too Many Requests' }), {
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': new Date(reset).toISOString(),
'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString(),
},
});
}
}
Idempotency for Mutations
// Client sends Idempotency-Key header for safe retries
// POST /api/payments
// Idempotency-Key: a0e4b2c1-unique-uuid-here
async function handlePayment(req: Request) {
const idempotencyKey = req.headers.get('idempotency-key');
if (!idempotencyKey) return errorResponse(400, 'Idempotency-Key header required');
// Check cache first
const cached = await redis.get(`idempotency:${idempotencyKey}`);
if (cached) return Response.json(JSON.parse(cached), { status: 200 });
// Process payment
const result = await processPayment(await req.json());
// Cache result for 24 hours
await redis.setex(`idempotency:${idempotencyKey}`, 86400, JSON.stringify(result));
return Response.json(result, { status: 201 });
}
OpenAPI 3.1 Documentation
# openapi.yaml
openapi: "3.1.0"
info:
title: Example API
version: "1.0.0"
description: "RESTful API for Example SaaS"
paths:
/api/v1/users:
get:
operationId: listUsers
summary: List all users
tags: [Users]
security: [{ bearerAuth: [] }]
parameters:
- name: page
in: query
schema: { type: integer, minimum: 1, default: 1 }
- name: pageSize
in: query
schema: { type: integer, minimum: 1, maximum: 100, default: 20 }
responses:
"200":
description: Users list
content:
application/json:
schema:
$ref: "#/components/schemas/UserListResponse"
"401":
$ref: "#/components/responses/Unauthorized"
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
Sovereign Execution, Token Efficiency & Invariants
| Dimension | Sovereign Mandate & Deterministic Contract |
|---|
| Frontier Model Cascade | Medium / High (GPT-5.6 Sol Medium, Gemini 4 Argon, Claude Sonnet 5.5 High, Grok 4.7 High Fast) |
| Availability Fallback | Gemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Zero unhandled missing-model errors) |
| KV-Cache & Token Protocol | Pin static instructions & schemas at prompt prefix (kv-cache-prefix-optimizer); pass surgical CONTEXT_MAP.md slices (<250 tokens/call) during Swarm Fan-Out |
| SaaT CLI & MCP Dispatch | CLI: vibes skill exec api-design-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_ast_query, vibes_compile_cache, vibes_optimize_budget, vibes_verify_gate |
| NFR & Anti-Slop Invariants | P99 API Gateway Latency < 85ms, OpenAPI 3.1 / TypeSpec strict schema parity, RFC 9457 Problem Details error contracts, Idempotency-Key enforcement; 100% complete zero-placeholder output verified via node scripts/check-anti-slop.js --strict |
Bahasa Indonesia
Integrasi Orkestrasi
- Ideasi Hulu & Spesifikasi:
brainstorming, prd-architect, spec-driven-development-expert
- Implementasi Hilir:
js-backend-expert, python-programming-expert, go-programming-expert, rust-programming-expert
- Tooling Kontrak & Gateway:
openapi-swagger-codegen-expert, api-gateway-proxy-expert, autonomous-api-drift-healer, graphql-apollo-expert
- Keamanan & Kualitas:
rate-limit-abuse-prevention, autonomous-red-teamer, e2e-testing-expert, anti-slop
- Siklus Hidup & Tata Kelola:
zero-to-prod-orchestrator, session-memory-manager
Deskripsi
Panduan ahli untuk merancang, mendokumentasikan, dan mengembangkan API berkualitas produksi. Mencakup pemodelan resource REST dan semantik HTTP, desain skema GraphQL, gRPC dengan protobuf, dan tRPC untuk API end-to-end type-safe di TypeScript. Termasuk dokumentasi OpenAPI 3.1, strategi versioning API, rate limiting, idempotency, dan alur kerja contract-first.
Kondisi Pemicu
- Merancang API baru dari nol (REST, GraphQL, gRPC, atau tRPC).
- Mengembangkan API yang ada tanpa merusak klien (breaking changes).
- Mendokumentasikan API dengan OpenAPI 3.1 / Swagger.
- Mengimplementasikan rate limiting, throttling, atau header idempotency.
- Memilih protokol API yang optimal (REST vs GraphQL vs gRPC vs tRPC).
- Merancang sistem webhook yang aman dengan verifikasi signature HMAC.
Panduan Pemilihan Protokol API
| Kriteria | REST | GraphQL | gRPC | tRPC |
|---|
| Type Safety | Manual (OpenAPI) | Berbasis Skema | Protobuf | End-to-end TypeScript |
| Performa | Baik | Baik | Luar Biasa (HTTP/2) | Baik |
| Dukungan Browser | Native | Native | Butuh gRPC-Web proxy | Hanya TypeScript/JS |
| Streaming | SSE / WebSocket | Subscriptions | Native dua-arah | Server-Sent Events |
| Kasus Terbaik | API Publik & Mobile | Graf data relasional kompleks | Komunikasi Antar Microservice | Fullstack Next.js Monorepo |
Pola Kunci Implementasi (TypeScript)
1. Struktur Error Standar RFC 9457 (Problem Details)
// Format standar penanganan error yang konsisten di seluruh endpoint
export interface ErrorDetailRFC9457 {
type: string;
title: string;
status: number;
detail: string;
instance: string;
errors?: Array<{ field: string; message: string }>;
}
2. tRPC Router Type-Safe End-to-End
import { initTRPC, TRPCError } from '@trpc/server';
import { z } from 'zod';
const t = initTRPC.context<Context>().create();
export const router = t.router;
export const publicProcedure = t.procedure;
export const appRouter = router({
pengguna: router({
daftar: publicProcedure
.input(z.object({ halaman: z.number().int().min(1).default(1) }))
.query(async ({ input, ctx }) => {
return ctx.db.user.findMany({ skip: (input.halaman - 1) * 20, take: 20 });
}),
buat: publicProcedure
.input(z.object({ email: z.string().email(), nama: z.string().min(2) }))
.mutation(async ({ input, ctx }) => {
return ctx.db.user.create({ data: input });
}),
}),
});
export type AppRouter = typeof appRouter;
3. Middleware Rate Limiting Berbasis Redis Sliding Window
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(100, '1 m'), // 100 request per menit
analytics: true,
prefix: 'api:ratelimit',
});
export async function middlewarePembatasLaju(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? '127.0.0.1';
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
return new Response(JSON.stringify({ error: 'Terlalu Banyak Permintaan' }), {
status: 429,
headers: {
'X-RateLimit-Limit': limit.toString(),
'X-RateLimit-Remaining': remaining.toString(),
'X-RateLimit-Reset': new Date(reset).toISOString(),
'Retry-After': Math.ceil((reset - Date.now()) / 1000).toString(),
},
});
}
}
4. Kunci Idempotensi Mutasi Kritis (Idempotency-Key)
export async function tanganiPembayaranIdempoten(req: Request) {
const kunciIdempotensi = req.headers.get('idempotency-key');
if (!kunciIdempotensi) {
return Response.json({ error: 'Header Idempotency-Key wajib disertakan' }, { status: 400 });
}
// Cek cache Redis terlebih dahulu
const hasilTersimpan = await redis.get(`idempotency:${kunciIdempotensi}`);
if (hasilTersimpan) {
return Response.json(JSON.parse(hasilTersimpan), { status: 200 });
}
// Proses transaksi finansial
const hasil = await prosesTransaksi(await req.json());
// Simpan hasil selama 24 jam untuk pengulangan aman
await redis.setex(`idempotency:${kunciIdempotensi}`, 86400, JSON.stringify(hasil));
return Response.json(hasil, { status: 201 });
}
Matriks Eksekusi Berdaulat, Efisiensi Token & Invarian
| Dimensi | Mandat Berdaulat & Kontrak Deterministik |
|---|
| Kaskade Model Frontier | Medium / High (GPT-5.6 Sol Medium, Gemini 4 Argon, Claude Sonnet 5.5 High, Grok 4.7 High Fast) |
| Invarian Fallback Ketersediaan | Gemini 4 Argon/Pro -> Gemini 3.8 Flash; Claude Opus 5.5 -> Claude Opus 4.6 (Bebas gagal saat model belum tersedia/kuota penuh) |
| Protokol Hemat Token & KV-Cache | Kunci instruksi & skema statis di prefix awal (kv-cache-prefix-optimizer); kirim potongan CONTEXT_MAP.md (<250 token/panggilan) saat Fan-Out |
| Eksekusi SaaT CLI & MCP | CLI: vibes skill exec api-design-expert <audit|eval|verify|generate|blueprint> / MCP: vibes_skill_dispatch, vibes_ast_query, vibes_compile_cache, vibes_optimize_budget, vibes_verify_gate |
| Invarian NFR & Anti-Slop | Latensi P99 API < 85ms, paritas skema ketat OpenAPI 3.1 / TypeSpec, kontrak error RFC 9457 Problem Details, penegakan Idempotency-Key; kode 100% tuntas tanpa placeholder diverifikasi melalui node scripts/check-anti-slop.js --strict |