Software Architecture

API Design Basics — REST / GraphQL / gRPC / WebSocket

API Design Basics — REST / GraphQL / gRPC / WebSocket

About this article

This article is the fourth deep dive in the “Software Architecture” category of the Architecture Crash Course for the Generative-AI Era series, covering API design.

“A published API is a contract.” Naming, data shape, errors, auth — all become things consumers depend on, so you can’t change them lightly later. The article compares the four major styles (REST / GraphQL / gRPC / WebSocket), use-case selection, versioning strategy, auth methods, and rate-limit numerical baselines.

What is an API in the first place

An API is, in a nutshell, “a window through which software talks to other software.”

Imagine a restaurant counter. The customer (frontend) looks at the menu (API documentation) and places an order; the kitchen (backend) returns the dish in a fixed vessel (response format). Orders not on the menu are rejected, and if the vessel’s shape suddenly changes the customer is confused — this “set of rules for placing and receiving orders” is an API. Web apps, mobile apps, internal-system integrations, AI invocations — modern software is connected through APIs.

Why API design matters

What happens if API design is sloppy? The moment you publish a “works-for-now endpoint,” external consumers start depending on its shape. URL and parameter naming, data format, error responses, authentication — a published API becomes a contract. Breaking changes force every consumer to adapt — practically impossible to push.

In 2023, when X (formerly Twitter) effectively monetized API v1.1 essentially overnight, classic third-party clients like Tweetbot all stopped at once. The case is remembered as the most violent demonstration of “APIs are contracts.” From the provider side, weak versioning strategy or sunsetting policies destroy consumer trust instantly.

Even URL and parameter naming, the moment you publish them, the backward-compatibility chain locks in. Initial-design quality casts a long shadow.

The moment you publish, it becomes a contract

URL and parameter naming, data shape, error responses, authentication — “a published API is a contract.” External consumers depend on the shape, so it can’t be changed lightly later. Breaking changes force every consumer to adapt — practically impossible to push.

In 2023, when X (formerly Twitter) effectively monetized API v1.1 essentially overnight, classic third-party clients like Tweetbot all stopped at once. The case is remembered as the most violent demonstration of “APIs are contracts.” From the provider side, weak versioning strategy or sunsetting policies destroy consumer trust instantly.

Even URL and parameter naming, the moment you publish them, the backward-compatibility chain locks in. Initial-design quality casts a long shadow.

The four major styles

API design has four major styles. There’s no overall ranking — pick by use. External public APIs and internal microservice traffic require very different traits.

flowchart TB
    Q{Use?} -->|External public Web API| REST[REST<br/>HTTP+JSON]
    Q -->|Many client kinds<br/>avoid over-fetching| GQL[GraphQL<br/>HTTP+query language]
    Q -->|Microservice-to-microservice<br/>internal high-speed| GRPC[gRPC<br/>HTTP/2+Protobuf]
    Q -->|Bidirectional real-time| WS[WebSocket<br/>TCP duplex]
    REST -.- E1[Most adopted<br/>cacheable]
    GQL -.- E2[Facebook 2015<br/>fits BFF]
    GRPC -.- E3[Google origin<br/>low latency]
    WS -.- E4[Chat / notifications /<br/>stock streams]
    classDef question fill:#fef3c7,stroke:#d97706;
    classDef rest fill:#dbeafe,stroke:#2563eb;
    classDef gql fill:#fae8ff,stroke:#a21caf;
    classDef grpc fill:#dcfce7,stroke:#16a34a;
    classDef ws fill:#f0f9ff,stroke:#0369a1;
    class Q question;
    class REST rest;
    class GQL gql;
    class GRPC grpc;
    class WS ws;
StyleTransportTypical use
RESTHTTP + JSONGeneral Web APIs, external publication
GraphQLHTTP + query languageAPIs facing diverse clients
gRPCHTTP/2 + ProtobufMicroservice-to-microservice internal traffic
WebSocketTCP duplexReal-time communication

Real projects often combine multiple — REST + some WebSocket is common.

Quick decision flow

Before details, the first candidate per use case. Pin the answer here, then check details.

UseFirst choiceSecond
Public API (for developers)REST
Internal microservice trafficgRPCREST
API for screens used by Web and MobileGraphQL or BFF + RESTREST
Chat, games, stock-price streamingWebSocketServer-Sent Events
TypeScript single-stack (Next.js, etc.)tRPC (type sharing)REST
Integration with existing public-sector / financial assetsREST or SOAPgRPC

For external publication, REST is the default; the burden of proof is reasons to pick anything else.

REST

REST (Representational State Transfer) is a resource-oriented API style using HTTP’s standard features as-is. URLs name resources (/users/123); operations use HTTP methods (GET, POST, PUT, DELETE). Simple and clear, REST is the de facto standard for public APIs.

Cache, auth, status codes — HTTP standards apply directly, so CDN acceleration and browser support are smooth. The weakness: clients fetch needed data from multiple endpoints, prone to N+1 problems (after a list query, fetching each related item one at a time, exploding requests) and over- / under-fetching.

Typical patternMeaning
GET /usersList
GET /users/:idSingle fetch
POST /usersCreate
PUT / PATCH /users/:idUpdate
DELETE /users/:idDelete

For public APIs, pick REST without hesitation — adoption and tooling overwhelm everything else.

REST design principles

When picking REST, follow these for a long-lived API. Not just style — guidelines aligned with HTTP’s design.

  • Resource-oriented, use nouns (/users not /getUsers).
  • HTTP methods express operations (GET/POST/PUT/DELETE).
  • Use status codes correctly (200/201/400/401/404/500).
  • URL hierarchy expresses relationships (/users/123/orders).
  • Unify error-response format (RFC 7807 Problem Details is the default).

GraphQL

GraphQL, published by Facebook in 2015, is a query language where clients specify the shape of data they want. Instead of fixed REST endpoints, queries are sent to a single endpoint (typically /graphql) saying “this screen needs only these fields.”

Strong when Web, mobile, internal dashboards, and other clients want different shapes. Weaknesses: HTTP cache is hard, N+1 problems must be solved manually, server-side implementation difficulty exceeds REST.

StrengthsWeaknesses
Fetch only the data neededCache strategy is complex
Auto-docs from typed schemaN+1 must be solved manually
Saves bandwidth on mobileHigh learning cost
Good frontend developer experienceHigh server-implementation difficulty

Worth considering for diverse-client APIs. Overspec for simple APIs.

gRPC

gRPC, developed by Google, is a binary-communication API framework using Protocol Buffers (Protobuf). Runs on HTTP/2 with bidirectional streaming, handling massive request volumes. Generates client and server code in many languages from .proto files — a major strength.

Schema-first with strict types makes it the first choice for microservice-to-microservice traffic. Direct browser use is hard (gRPC-Web required) and HTTP-tool debugging is awkward, so it’s a poor fit for public APIs.

StrengthsWeaknesses
Overwhelming performance (binary)Direct browser use is hard
Cross-language code generationHard to debug
Schema-first type safetyHard to hit with HTTP tools (curl, etc.)
HTTP/2 streaming supportMid-high learning cost

First choice for microservice-to-microservice traffic. Poor fit for external publication.

WebSocket

WebSocket keeps a persistent connection between client and server with bidirectional traffic. Regular HTTP is “client asks, server answers” one-shot; WebSocket lets “the server actively push to the client” — decisively different.

This trait fits real-time use: chat, stock displays, online games, live streaming, notifications — “want to instantly notify clients of server-side state change” scenes default to WebSocket. After an HTTP handshake, the connection upgrades to a custom protocol.

StrengthsWeaknesses
Low latency, bidirectionalConnection-keep resource use
Server can pushLoad balancers and proxies need care
Browser-standard supportReconnection logic is your code

Specialized for real-time use cases REST can’t express; irreplaceable when the use matches.

Four-style comparison

The styles have clearly different sweet spots, so simple superiority comparison is meaningless. Typical-use comparison:

AspectRESTGraphQLgRPCWebSocket
Public-API fit×
Browser support
Performance
Learning costLowMidMidLow
Type safety×
Cache leverage××

The typical pattern: “public REST / internal gRPC / real-time WebSocket.

Versioning strategy

Spec changes after publication are inevitable, so version management must be in from the start. Without versioning, every small change requires coordination with every consumer — “effectively, you can’t improve the API.”

MethodExampleTrait
URL path/api/v1/usersSimplest, widest adoption
Accept headerAccept: application/vnd.api.v1+jsonCleaner URL, higher learning cost
Query parameter/api/users?version=1Easy, awkward at scale

URL-path is simplest and most adopted. “This is the default” is fine.

API lifecycle ladder

Note: industry rates as of April 2026. Periodic refresh required.

Since APIs are “contracts on publish,” managing the phases pre-publication through retirement “by numbers” is the operations key.

PhaseStateSupportMigration window
Beta / PreviewExperimental, spec changingNo SLA, breaking changes possible
GA (General Availability)Stable, production OKBackward compatibility maintained
Deprecated (discouraged)New use discouragedContinues working, warning headers6 months to 2 years minimum
SunsetRetirement noticeContinues working, retirement date announced3 months minimum
EOL (end of life)StoppedDoesn’t work

The industry standard: “Deprecation -> Sunset -> EOL window of at least 6 months.” Google Cloud / AWS / Stripe officially commit to 2+ years; failing this loses consumer trust.

Deprecation window: at least 6 months, ideally 2 years. Sudden retirement loses trust instantly.

Authentication methods

APIs always need authentication. Pick by use:

MethodUse
Bearer Token (JWT — JSON Web Token, signed token)Most common for SPA (Single Page Application) and mobile
OAuth 2.0 / OIDCExternal-service integration, SSO
API KeyServer-to-server, B2B API
mTLS (mutual TLS)Internal traffic with very high security needs

External publication: OAuth 2.0. Internal: API Key or mTLS. Behind BFF: Cookie + session — these are practical defaults.

Rate-limit / error-design numeric gates

Note: industry rates as of April 2026. Periodic refresh required.

Leaving “things to decide” vague in API design causes production incidents. Set concrete numerical baselines first.

SettingRecommendedReason
Rate limit (public API)60 req/min per user, 600 req/min per IPBrute-force prevention + fairness
Rate limit (internal API)10,000+ req/sec allowedDon’t restrict internal use
Timeout30s (GET) / 60s (POST)HTTP convention upper bound
Payload upper bound1MB (REST) / 10MB (file upload)DoS prevention
VersioningURL-path (/v1) requiredMost adopted, easy debugging
Error formatRFC 7807 (Problem Details)Standardized error shape
Deprecation window6 months minimum, 2 years idealIndustry convention

Status codes: use “200 (success) / 201 (created) / 400 (client error) / 401 (unauthenticated) / 403 (unauthorized) / 404 (not found) / 409 (conflict) / 429 (rate exceeded) / 500 (server error)” correctly. “Returning 200 for all errors” is a textbook forbidden move — clients can’t handle errors.

Selection by case

Public Web APIs (for external developers)

REST. Beats others on adoption, tooling, learning cost. OpenAPI auto-generates docs.

Service with diverse clients (Web / mobile / IoT, etc.)

GraphQL. When clients want different data shapes, GraphQL is more efficient than maintaining many REST endpoints.

Microservice-to-microservice internal traffic

gRPC. Wins on performance, type safety, code generation. No browser direct access, so gRPC’s weakness doesn’t apply.

Real-time features (chat, notifications, games)

WebSocket. Irreplaceable for bidirectional traffic.

General web service + some real-time

REST + WebSocket combination. Many services use this pairing.

API-design traps

Common API-design failures, ordered by retroactive fix cost:

Forbidden moveWhy
Verbs in URLs (/getUsers / /deleteUser)Double-management with HTTP method. /users + GET/DELETE is correct
All errors return 200 OK with {success: false} bodyClient error handling breaks. Use HTTP standard status codes
Publish without versioningBreaking changes impossible; API freezes. /v1 from day one
Auth bolted on laterAll endpoints need modification. Design with auth assumed
No schema (OpenAPI / Protobuf / etc.)Spec becomes oral tradition; client and implementation drift
N+1 queries via the APIDB load grows linearly. Use Include params or GraphQL
Pagination only via OffsetPerformance breaks at scale. Provide cursor-based too
Breaking changes without noticeConsumer trust gone instantly. The X (Twitter) 2023 v1.1 monetization pattern
POST retries allowed without idempotencyDouble payment / double inventory decrement. Use Idempotency-Key header
No rate limitBots / malicious use cause high bills and DoS
Adopting GraphQL on the premise “it’s newer, therefore better”GraphQL is overspec for simple CRUD; cache strategy and N+1 handling actually increase dev effort
Unifying all internal traffic on gRPCHTTP debug tools don’t apply, spiking incident-investigation difficulty; for small-scale integrations REST has lower operational cost

Stripe’s API is industry-known as a “non-breaking API” model. Endpoints from the 2011 initial release are still running 10+ years later; “add features while preserving backward compatibility” is solved.

The iron rule of API design: “don’t break it, don’t kill it, don’t be vague.”

AI decision axes

AI-era favorableAI-era unfavorable
OpenAPI / GraphQL / gRPC (schema-first)Specs in Excel / Word / oral
tRPC (TypeScript type sharing)Untyped hand-written REST
Auto-generation from schema (types / clients / docs)Hand-copying types into the client
Standard RESTful patternsCustom naming, custom error formats
  1. Split public vs internal first (external = REST, internal = gRPC candidate).
  2. Decide by consumer shape (Web / mobile / external developer / inter-service).
  3. Schema-first as a hard requirement (OpenAPI / Protobuf / GraphQL).
  4. Without a deviation reason, lean on REST (adoption, tooling, AI accuracy decisively favor it).

”An API that suddenly stopped” (industry case)

When X (formerly Twitter) effectively ended API v1.1 by monetizing in 2023, classic third-party clients like Tweetbot all stopped at once. From a consumer’s view, “API spec = perpetual contract” had been the assumption; one day it was suddenly ripped away.

Watching Tweetbot sink overnight on Twitter’s whim made many feel the contractual nature of APIs viscerally. The case is remembered as the most violent demonstration of “APIs are contracts.”

When API discussions say “naming can’t be changed later,” this is the reality on their mind.

API publication is contract execution; build retirement / change rules into the design from the start.

What you must decide — what’s your project’s answer?

Articulate your project’s answer in 1-2 sentences for each:

  • API style (REST / GraphQL / gRPC / WebSocket / combination)
  • Authentication (Bearer Token / OAuth / API Key / mTLS)
  • Error response format (RFC 7807, etc.)
  • Versioning strategy (URL path / Accept header)
  • Rate limit (per user / per IP / req per minute)
  • Documentation management (OpenAPI / GraphQL Schema / Protobuf)
  • Public vs internal-only line

Summary

This article covered API design — four styles, versioning, auth, rate-limit numeric baselines.

External public: REST + OpenAPI. Internal: gRPC. Per-screen: GraphQL. Real-time: WebSocket. Schema-first, AI-friendly design is the modern favorite.

The next article covers frameworks (Spring / Next.js / FastAPI / Rails, etc.).

Back to series TOC -> ‘Architecture Crash Course for the Generative-AI Era’: How to Read This Book

I hope you’ll read the next article as well.