APIs FAQ

Common questions about REST, GraphQL, gRPC, WebSockets, OAuth, JWT, rate limiting, and CORS. Each answer is short. Links go to the full explanation.

What is the difference between REST and GraphQL?

REST exposes resources at fixed URLs. Each endpoint returns a predefined set of fields. To get a user and their posts, you make two requests: GET /users/42 and GET /users/42/posts. The server decides what fields to include.

GraphQL exposes a single endpoint where the client specifies exactly which fields it needs. One request returns the user's name and their last 5 post titles — nothing more, nothing less. The client controls the response shape.

REST advantages: HTTP caching works out of the box, simpler tooling, broader ecosystem. GraphQL advantages: no over-fetching, no under-fetching, strong typing, single round trip for nested data.

Use REST for simple CRUD APIs with cacheable responses. Use GraphQL when clients need flexible, nested data in fewer round trips.

See How REST Works and How GraphQL Works for the full comparison.

When should I use gRPC instead of REST?

Use gRPC when services talk to services and performance matters. gRPC uses binary Protocol Buffers (3-10x smaller than JSON), HTTP/2 multiplexing (many concurrent RPCs on one connection), and code generation (type-safe clients in any language). Deadlines propagate through call chains, preventing cascading timeouts.

Use REST when the API is public, consumed by browsers, or needs HTTP caching. gRPC doesn't work natively in browsers (requires grpc-web proxy), the binary format is not human-readable, and the tooling is less accessible than curl and Postman.

Many architectures use both: REST for public-facing APIs and gRPC for internal microservice communication.

See How gRPC Works for Protocol Buffers, streaming patterns, and when gRPC shines.

How does OAuth 2.0 work?

OAuth 2.0 lets a user grant a third-party application limited access to their resources on another service — without sharing their password. "Sign in with Google" is OAuth.

In the authorization code flow (most secure):

  1. The app redirects the user to the auth server (Google, GitHub)
  2. The user logs in and grants specific permissions (scopes)
  3. The auth server redirects back with a one-time authorization code
  4. The app's backend exchanges the code + client secret for tokens
  5. The access token (short-lived) calls APIs; the refresh token (long-lived) obtains new access tokens

The user's password never touches the third-party app. Permissions are scoped and revocable.

See How API Authentication Works for the full OAuth 2.0 flow and client credentials.

What is a JWT and how does it work?

A JWT (JSON Web Token) is a signed, self-contained token with three base64-encoded parts: header (algorithm), payload (claims — user ID, role, expiration), and signature (cryptographic proof the token wasn't tampered with).

The server signs the JWT with a secret key. To verify, any server with the key recomputes the signature and compares it. No database lookup needed — the token carries its own proof of authenticity.

The payload is not encrypted — anyone can read the claims. Never put secrets in a JWT. JWTs can't be revoked before expiration without a blocklist, so short expiration times (15 minutes to 1 hour) and refresh token flows are essential.

See How API Authentication Works for JWT structure, signing, and refresh token patterns.

What is the difference between WebSockets and SSE?

WebSockets provide bidirectional communication — both client and server send messages independently on a persistent TCP connection. The connection starts with an HTTP upgrade handshake and switches to the WebSocket frame protocol.

SSE (Server-Sent Events) is server-to-client only — the server pushes events over a standard HTTP connection using text/event-stream. SSE includes built-in reconnection and event ID resumption.

Use WebSockets for chat, multiplayer games, collaborative editing — anything where both sides send messages. Use SSE for notifications, live feeds, log tailing — anything where only the server pushes. SSE is simpler because it's plain HTTP and works through all proxies.

See How WebSockets Work for the full comparison table.

How does rate limiting protect an API?

Rate limiting caps how many requests a client can make in a time window. Without it, one client — buggy, malicious, or enthusiastic — can overwhelm the server.

The token bucket algorithm is most common: each client has a bucket that refills at a steady rate. Each request consumes a token. Empty bucket = 429 Too Many Requests. The bucket size allows controlled bursts while enforcing a sustained rate.

Response headers communicate the state: X-RateLimit-Remaining (requests left), X-RateLimit-Reset (when the window resets), and Retry-After (seconds to wait when throttled).

See How Rate Limiting Works for token bucket, sliding window, and distributed Redis-based enforcement.

What does idempotent mean for APIs?

An idempotent operation produces the same result whether you call it once or ten times. PUT /users/42 {"name": "Alice"} always results in the name being "Alice." DELETE /users/42 deletes the user once; subsequent calls return 404 but don't change server state.

GET, PUT, DELETE, and HEAD are idempotent. POST is not — each call may create a new resource.

Idempotency makes APIs safe to retry. When a network timeout means you don't know if the request succeeded, retrying an idempotent operation is harmless. For non-idempotent operations (like payments), use idempotency keys to prevent duplicate side effects.

See How REST Works for HTTP verbs and their idempotency properties.

How do you version an API?

Three approaches:

  1. URL versioning: /api/v2/users — most common, most explicit. Easy to test with curl, easy to cache. The URL clearly says which version you're using.
  2. Header versioning: Accept: application/vnd.api+json;version=2 — cleaner URLs, but harder to test and less visible.
  3. Query parameter: /api/users?version=2 — easy to add, easy to forget.

GraphQL avoids versioning entirely. Fields are deprecated with a @deprecated directive, not removed. Old fields continue to work. New fields are added alongside old ones. This requires discipline but avoids the breaking-change cliff.

URL versioning is the industry default for REST APIs because it's explicit, cacheable, and immediately visible in logs and documentation.

What is CORS and why does it exist?

CORS (Cross-Origin Resource Sharing) is a browser security mechanism. By default, browsers block JavaScript from making requests to a different origin (domain + protocol + port) — the same-origin policy. CORS headers tell the browser which exceptions are allowed.

When JavaScript on app.example.com calls api.example.com, the browser checks Access-Control-Allow-Origin in the response. For non-simple requests (PUT, DELETE, custom headers), the browser sends a preflight OPTIONS request first.

CORS prevents malicious websites from making API calls using your cookies. It only affects browsers — server-to-server requests, curl, and mobile apps are unaffected. CORS misconfiguration is one of the most common API integration issues.

See How REST Works for how CORS interacts with REST API design.

What is the difference between authentication and authorization?

Authentication proves who you are — verifying identity. "This request is from Alice." Methods: passwords, API keys, JWTs, certificates.

Authorization determines what you can do — checking permissions. "Alice can read but not delete this resource." Methods: role-based access control (RBAC), attribute-based access control (ABAC), scopes in OAuth.

Authentication always comes first. You must know who someone is before you can decide what they're allowed to do. A 401 Unauthorized response means authentication failed. A 403 Forbidden response means authentication succeeded but authorization failed.

See How API Authentication Works for the four authentication approaches.

How does API pagination work?

Pagination splits large result sets into pages so clients don't download everything at once.

Offset-based: ?offset=100&limit=50 — simple but slow on large datasets (the database still counts past skipped rows) and unstable under concurrent writes (inserts shift offsets).

Cursor-based: ?after=eyJpZCI6NDJ9&limit=50 — fast and stable. The cursor is an opaque token encoding the position. The server decodes it to a WHERE id > 42 LIMIT 50 clause. Inserts and deletes don't affect other pages.

Cursor-based pagination is recommended for most APIs. GitHub, Stripe, Slack, and most GraphQL APIs use it.

See How REST Works for pagination in REST API design.

What is the difference between API keys and OAuth tokens?

API keys identify the application. They're long-lived, manually rotated, and don't represent a specific user. A server-to-server integration uses an API key because there's no human user involved.

OAuth tokens represent a user's delegated authorization. They're short-lived, automatically refreshed, scoped to specific permissions ("read email" but not "delete account"), and revocable. A "Sign in with Google" button uses OAuth because a human user grants permission.

Use API keys for developer tools, internal services, and machine-to-machine communication. Use OAuth when a user must explicitly grant a third-party application access to their data.

See How API Authentication Works for the full comparison of API keys, sessions, JWTs, and OAuth.