--- id: wiki-2026-0508-api-fundamentals title: API Fundamentals category: 10_Wiki/Topics status: verified canonical_id: self aliases: [API basics, REST GraphQL gRPC, Web API] duplicate_of: none source_trust_level: A confidence_score: 0.9 verification_status: applied tags: [api, rest, graphql, grpc, fundamentals] raw_sources: [] last_reinforced: 2026-05-10 github_commit: pending tech_stack: language: typescript framework: REST/GraphQL/gRPC/tRPC --- # API Fundamentals ## 매 한 줄 > **"매 contract between systems — verbs, nouns, errors, evolution"**. 매 RPC (1980s, Sun RPC) → CORBA (1991) → SOAP (1998) → REST (Fielding 2000) → gRPC (2015) → GraphQL (2015) → tRPC (2020). 매 2026 modern stack 은 typed end-to-end (tRPC, GraphQL Codegen, gRPC + buf) + AsyncAPI 2.x for events. ## 매 핵심 ### 매 four styles - **REST**: resource-oriented, HTTP verbs, stateless, cacheable. 매 public API standard. - **GraphQL**: client-specified queries, single endpoint, strong types. 매 BFF / mobile. - **gRPC**: binary (protobuf), HTTP/2, streaming, codegen. 매 internal, low-latency. - **tRPC**: TypeScript-first, no codegen, type inference. 매 Next.js / monorepo. ### 매 cross-cutting concerns - **Versioning**: URI (`/v1`), header (`Accept-Version`), or evolution (additive only). - **Idempotency**: idempotency key for unsafe verbs (POST). At-least-once → exactly-once at API. - **Pagination**: cursor (preferred) vs offset. 매 cursor 의 stable ordering. - **Errors**: RFC 7807 problem+json, gRPC status codes, GraphQL `errors[]`. - **Auth**: OAuth2 / OIDC / mTLS / API key. ### 매 응용 1. **Public REST + GraphQL** — Stripe (REST), GitHub (both), Shopify (GraphQL). 2. **Internal gRPC mesh** — Google, Uber, Square. 3. **Full-stack tRPC** — Vercel, Cal.com, T3 stack. ## 💻 패턴 ### REST resource design ```http GET /orders?status=pending&cursor=abc123 → 200 [{...}], next_cursor POST /orders → 201 {id, ...}, Location GET /orders/{id} → 200 {...} | 404 PATCH /orders/{id} (JSON Merge Patch) → 200 {...} DELETE /orders/{id} → 204 ``` ### REST idempotency ```typescript app.post("/orders", async (req, res) => { const idemKey = req.header("Idempotency-Key"); const cached = await redis.get(`idem:${idemKey}`); if (cached) return res.status(200).json(JSON.parse(cached)); const order = await createOrder(req.body); await redis.setex(`idem:${idemKey}`, 86400, JSON.stringify(order)); res.status(201).json(order); }); ``` ### Cursor pagination ```typescript const limit = 50; const rows = await db.query( `SELECT * FROM orders WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT $3`, [cursor.ts, cursor.id, limit + 1] ); const hasMore = rows.length > limit; const items = rows.slice(0, limit); const nextCursor = hasMore ? encode(items.at(-1)) : null; ``` ### GraphQL schema + resolver ```graphql type Query { order(id: ID!): Order orders(first: Int!, after: String): OrderConnection! } type Order { id: ID!, status: OrderStatus!, items: [Item!]! } type OrderConnection { edges: [OrderEdge!]! pageInfo: PageInfo! } ``` ```typescript const resolvers = { Query: { order: (_, {id}, ctx) => ctx.dataloaders.order.load(id), orders: async (_, {first, after}, ctx) => paginateOrders(first, after, ctx), }, Order: { items: (order, _, ctx) => ctx.dataloaders.itemsByOrder.load(order.id), }, }; ``` ### gRPC service (proto3) ```proto syntax = "proto3"; package orders.v1; service OrderService { rpc Get(GetRequest) returns (Order); rpc List(ListRequest) returns (stream Order); // server streaming rpc Watch(WatchRequest) returns (stream OrderEvent); // bidi-friendly } message Order { string id = 1; OrderStatus status = 2; google.protobuf.Timestamp created_at = 3; } ``` ### tRPC procedure ```typescript import { router, publicProcedure } from "./trpc"; import { z } from "zod"; export const orderRouter = router({ get: publicProcedure .input(z.object({ id: z.string() })) .query(({ input, ctx }) => ctx.db.order.findUnique({ where: { id: input.id } })), create: publicProcedure .input(z.object({ items: z.array(z.string()).min(1) })) .mutation(({ input, ctx }) => ctx.db.order.create({ data: { items: input.items } })), }); // Client gets full type inference: trpc.order.get.useQuery({id}) typed ``` ### RFC 7807 error ```json { "type": "https://api.example.com/errors/insufficient-funds", "title": "Insufficient funds", "status": 402, "detail": "Account 12345 has balance $5, requested $50", "instance": "/orders/abc" } ``` ## 매 결정 기준 | 상황 | Style | |---|---| | Public 3rd-party API | REST + OpenAPI 3.1 | | Mobile/web with diverse query needs | GraphQL | | Internal microservices, low latency | gRPC | | TypeScript monorepo (Next.js) | tRPC | | Real-time bidirectional streams | gRPC streaming or WebSocket | | Async events | AsyncAPI + Kafka/NATS | **기본값**: 매 REST + OpenAPI 의 public, 매 gRPC 의 internal, 매 tRPC 의 TS monorepo. ## 🔗 Graph - 부모: [[Distributed Systems]] · [[Web Architecture]] - 변형: [[REST]] · [[gRPC]] - 응용: [[OpenAPI]] · [[API Gateway]] · [[Service Mesh]] - Adjacent: [[JSON Schema]] · [[Protocol Buffers]] ## 🤖 LLM 활용 **언제**: 매 cross-system contract 의 design, 매 client/server 매 separated, 매 versioned interface 필요. **언제 X**: 매 internal function call (just call it), 매 single process, 매 < 100 lines glue script. ## ❌ 안티패턴 - **GET with side effects**: 매 cache poisoning, 매 idempotency violation. - **HTTP 200 with `{error: ...}` body**: 매 status code semantics 의 ignore. - **Versioning by minor change**: 매 v1/v2/v3 explosion. 매 additive evolution 의 prefer. - **Chatty API**: 매 N+1 client roundtrips. 매 batch endpoint or GraphQL. - **No pagination**: 매 unbounded list → OOM at scale. ## 🧪 검증 / 중복 - Verified (RFC 7231 HTTP, RFC 7807 problem+json, gRPC docs, GraphQL spec, tRPC docs). - 신뢰도 A. ## 🕓 Changelog | 날짜 | 변경 | |---|---| | 2026-05-08 | Phase 1 | | 2026-05-10 | Manual cleanup — full content (REST/GraphQL/gRPC/tRPC fundamentals) |