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.

Before you read this

This article is mostly about development: programs and APIs. If IT vocabulary is unfamiliar, reading the primer "Programs and APIs" first makes it far easier to follow. You can also look anything up in the glossary as you read.

What is an API in the first place

REST / GraphQL / gRPC Communication Pattern Comparison

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.

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 main 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.

Comparison of Four API Design Styles Choose by use case. REST for external APIs, gRPC for internal communication REST Great externally HTTP + JSON Resource-oriented standard API GET /users/123 Strengths Overwhelming popularity CDN caching works Abundant tools & information Weaknesses Over-fetching N+1 Problem When in doubt, REST Public API & General Web GitHub, Stripe API, etc. GraphQL Query Language Request only the data you want { user { name, email } } Strengths Fetch only needed fields Auto-documentation via type schema Saves mobile bandwidth Weaknesses Caching is complex High server-side implementation difficulty For diverse clients Web + Mobile simultaneously GitHub API v4, etc. gRPC Great internally Protobuf Binary High-speed communication via HTTP/2 .proto → Auto code generation Strengths Overwhelming performance Cross-language code generation Schema-first type safety Weaknesses Difficult to use directly from browser Hard to debug Between microservices Internal inter-service communication Standard within Google WebSocket Persistent two-way link Server → Client Push communication possible Strengths Low-latency real-time Push from server Weaknesses Connection maintenance cost Reconnection logic needed Real-time only Chat & Games Stock feeds & notifications A published API is a contract. Initial design quality has long-lasting effects
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.

The quick version of the decision runs like this.

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 — the de facto standard for public APIs

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 has a handful of design principles worth stating.

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 — screen-specific delivery for diverse clients

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 — the first choice for internal microservice traffic

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 — irreplaceable for real-time work

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.

The four styles compared

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

API Communication Method Comparison Criteria REST GraphQL gRPC WebSocket Suited for External × Browser Support Performance Learning Curve Low Medium Medium Low Type Safety × Cache Utilization × ×
AspectRESTGraphQLgRPCWebSocket
Public-API fit×
Browser support
Performance
Learning costLowMidMidLow
Type safety×
Cache leverage××

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

How to choose

Versioning is the part that decides whether the contract can ever move.

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.

The lifecycle ladder looks like this.

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 is the other cross-cutting decision.

APIs always need authentication. Pick by use:

MethodUse
Bearer Token (JWT)Most common for SPA 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.

Public web APIs, for external developers. REST. Beats others on adoption, tooling, learning cost. OpenAPI auto-generates docs.

A service with diverse clients — web, mobile, IoT. GraphQL. When clients want different data shapes, GraphQL is more efficient than maintaining many REST endpoints.

Internal traffic between microservices. 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.

A general web service with some real-time. REST + WebSocket combination. Many services use this pairing.

Numeric gates for rate limiting and error design

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.

Three scenarios

If you are building solo or at a startup

On a single TypeScript stack, sharing types directly through tRPC (or Next.js Server Actions) is the fastest route. Adding REST at the point where you actually publish externally is fine. GraphQL looks attractive, but in a one-person project the effort of managing the schema honestly exceeds the benefit.

Personal / Startup: Ship in One Month Is Correcten.senkohome.com/arch-intro-case-startup/

If you are a small or mid-size SaaS

REST with OpenAPI for anything public, and gRPC between internal services, is the standard split. Keep the OpenAPI YAML in Git and generate the SDK, the documentation and the mocks from it — and settle the numeric gates for rate limiting, error format (RFC 7807) and versioning before you publish.

Small-Mid SaaS - Lean on Managed and Run with Few Peopleen.senkohome.com/arch-intro-case-saas/

If you are a large enterprise

At this size the real subject is standardising the API specification company-wide. Set naming conventions, the authentication method (OAuth 2.0 / OIDC) and the deprecation period (six months minimum, two years ideally) as company-wide guidelines, and manage them centrally through an API gateway. APIs published in a different style by each department produce hell at integration time.

Large-Enterprise Core: Design That Holds Up for Yearsen.senkohome.com/arch-intro-case-enterprise/

AI decision axes — Schema-first is a precondition

Why schema-first becomes a prerequisite for AI utilization

When API specs exist as OpenAPI YAML or Protobuf, AI can reference them to generate accurate client code and test code. Without schemas, AI must guess API behavior from implementation code, and generated code accuracy drops significantly.

Quality checks for AI-generated APIs

AI tends to generate “APIs that work” but misses non-functional aspects: rate limiting, pagination, error-response consistency, and versioning strategy. Reviewing AI-generated API designs against an OpenAPI lint (Spectral, etc.) catches these systematically.

Pitfalls and forbidden moves

Here are the six API-design failures seen most often in practice, ordered by how expensive they are to fix.

Forbidden moveWhy it is bad → what to do instead
Publishing with no versioningbreaking changes become impossible and the API freezes → put /v1 on it from the start
Returning every error as 200 OKclient error handling breaks → use the standard HTTP status codes
Putting verbs in the URL, e.g. /getUsersit duplicates what the HTTP method already says → /users with GET and DELETE
No schema — no OpenAPI, no Protobufthe specification becomes oral tradition and client and implementation drift apart → go schema-first
Allowing POST retries with no idempotency guaranteethe cause of double payments and double stock decrements → handle it with an Idempotency-Key header
Publishing with no rate limitbots and abuse turn into a large invoice or a DoS incident → set the numeric gates first

By contrast, Stripe’s API is known as the model of “an API that does not break.” The same endpoints have kept working since the first version in 2011 — a case of solving the hard problem of adding features while preserving backward compatibility. The iron rule of API design is “do not break it, do not delete it, do not leave it ambiguous.”

Author’s note — “the API that stopped one day”

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

Write your answers down as an ADR. A concrete guide to writing them is here.

[DevOps Architecture] Documentationen.senkohome.com/arch-intro-devops-docs/

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.