--- id: wiki-2026-0508-public-apis title: Public APIs category: 10_Wiki/Topics status: verified canonical_id: self aliases: [Public API, External API, Open API, Developer API] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [backend, api, rest, graphql, design] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: hono --- # Public APIs ## 매 한 줄 > **"매 API 매 product"**. Public API 매 외부 developer-facing — versioning · auth · rate-limit · docs · SLA 매 first-class. 2026 stack: REST + OpenAPI 3.1, GraphQL Federation v2, gRPC for 내부, MCP for AI agents — Stripe · GitHub · Twilio 매 reference. ## 매 핵심 ### 매 Design pillars - **Versioning**: URL(`/v1`) · header(`API-Version: 2026-05-01`, Stripe). 매 deprecation policy. - **Auth**: OAuth2 · API key · JWT · mTLS. Per-key scopes. - **Rate limit**: token bucket per key/IP. `Retry-After` · `X-RateLimit-*` headers. - **Pagination**: cursor (preferred) > offset. 매 stable · efficient. - **Errors**: RFC 7807 Problem Details. 매 stable error codes. - **Idempotency**: `Idempotency-Key` header (Stripe pattern). - **Docs**: OpenAPI 3.1 + interactive (Scalar / Redocly). ### 매 Protocol selection - **REST + JSON**: 매 default · widest compat. - **GraphQL**: 매 client-shaped queries · over-fetch 의 X. - **gRPC**: 매 internal · low-latency · binary. - **MCP**: 매 LLM agent tool exposure (2025+). - **Webhooks**: server-push · async events. ### 매 응용 1. SaaS public API (Stripe, GitHub, Twilio). 2. Platform / marketplace. 3. Mobile/web client backend. 4. Partner integration. ## 💻 패턴 ### REST endpoint with OpenAPI (Hono + Zod) ```typescript import { Hono } from 'hono'; import { z } from 'zod'; import { describeRoute } from 'hono-openapi/zod'; const app = new Hono(); const Order = z.object({ id: z.string().uuid(), amount_cents: z.number().int().nonnegative(), currency: z.enum(['USD','EUR','KRW']), }); app.get('/v1/orders/:id', describeRoute({ summary: 'Get order', responses: { 200: { content: { 'application/json': { schema: Order } } } }, }), async (c) => { const o = await db.orders.find(c.req.param('id')); if (!o) return c.json({ type:'/errors/not-found', title:'Order not found' }, 404); return c.json(o); }); ``` ### Cursor pagination ```typescript // GET /v1/orders?limit=50&cursor=opaque app.get('/v1/orders', async (c) => { const limit = Math.min(Number(c.req.query('limit') ?? 50), 100); const cursor = c.req.query('cursor'); const after = cursor ? decode(cursor) : null; // {id, created_at} const rows = await db.orders.list({ after, limit: limit + 1 }); const hasMore = rows.length > limit; const page = rows.slice(0, limit); return c.json({ data: page, next_cursor: hasMore ? encode(page[page.length-1]) : null, }); }); ``` ### Rate limit (Cloudflare/Redis token bucket) ```typescript async function rateLimit(key: string, capacity = 100, refillPerSec = 10) { const now = Date.now()/1000; const lua = ` local b = redis.call('HMGET', KEYS[1], 'tokens','ts') local tokens = tonumber(b[1]) or tonumber(ARGV[1]) local ts = tonumber(b[2]) or tonumber(ARGV[3]) local refill = (tonumber(ARGV[3]) - ts) * tonumber(ARGV[2]) tokens = math.min(tonumber(ARGV[1]), tokens + refill) if tokens < 1 then return 0 end redis.call('HMSET', KEYS[1], 'tokens', tokens-1, 'ts', ARGV[3]) return 1 `; return redis.eval(lua, 1, key, capacity, refillPerSec, now); } ``` ### Idempotency-Key (Stripe pattern) ```typescript app.post('/v1/payments', async (c) => { const idem = c.req.header('Idempotency-Key'); if (!idem) return c.json({ error: 'idempotency_key_required' }, 400); const cached = await idemStore.get(idem); if (cached) return c.json(cached.body, cached.status); const result = await chargeCard(await c.req.json()); await idemStore.put(idem, { status: 200, body: result }, { ttl: 86400 }); return c.json(result); }); ``` ### Webhook with HMAC sig (verified) ```typescript import crypto from 'node:crypto'; function verify(payload: string, sig: string, secret: string) { const expected = crypto.createHmac('sha256', secret).update(payload).digest('hex'); return crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected)); } ``` ### Versioning by header (Stripe-style) ```typescript app.use('*', async (c, next) => { const v = c.req.header('Stripe-Version') ?? '2026-05-01'; c.set('apiVersion', v); await next(); }); // 매 handler 매 version-aware shape transform. ``` ### Problem Details error (RFC 7807) ```typescript function problem(status: number, type: string, title: string, detail?: string) { return new Response( JSON.stringify({ type, title, status, detail }), { status, headers: { 'Content-Type': 'application/problem+json' } } ); } ``` ### MCP server (AI agent tool) ```typescript // 매 Public API 매 LLM-callable import { McpServer } from '@modelcontextprotocol/sdk/server'; const server = new McpServer({ name: 'orders-api', version: '1.0.0' }); server.tool('get_order', { id: z.string() }, async ({ id }) => { const o = await fetch(`https://api.acme.com/v1/orders/${id}`).then(r => r.json()); return { content: [{ type: 'text', text: JSON.stringify(o) }] }; }); ``` ## 매 결정 기준 | 요건 | Choice | |---|---| | External developer-facing | REST + OpenAPI 3.1 | | Client-shaped queries | GraphQL | | Internal RPC | gRPC | | AI agent tool | MCP | | Async event push | Webhooks | | File upload large | Resumable (tus) | | Real-time | SSE / WebSockets | **기본값**: REST + OpenAPI + Idempotency-Key + cursor pagination. ## 🔗 Graph - 부모: [[Backend Architecture]] · [[API Design]] - 변형: [[REST]] · [[gRPC]] · [[MCP]] - 응용: [[OpenAPI - Swagger]] - Adjacent: [[Webhooks and Notifications]] · [[Rate Limiting]] ## 🤖 LLM 활용 **언제**: OpenAPI spec generation, error-shape design, 매 SDK scaffolding. **언제 X**: 매 production rate-limit policy 의 직접 결정 — 매 traffic data 의 review. ## ❌ 안티패턴 - **No versioning**: breaking change 매 직격탄. - **Random error shapes**: client 매 parse 못 함 — RFC 7807 의 사용. - **Offset pagination on huge tables**: 매 deep scan. - **Sync auth check on hot path**: cache JWT verification. - **No `Idempotency-Key`**: 매 retry 매 double charge. - **Leaking internal IDs**: opaque · cursor · UUID 의 사용. ## 🧪 검증 / 중복 - Verified (Stripe API docs 2026; GitHub API docs; *API Design Patterns* JJ Geewax). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full content (pillars + 8 patterns) |