API Design Interview Guide — REST vs GraphQL vs gRPC, Versioning, and Pagination
A practical API design interview guide covering REST, GraphQL, gRPC, versioning, pagination, idempotency, errors, auth, rate limits, and the tradeoffs interviewers expect.
API Design Interview Guide — REST vs GraphQL vs gRPC, Versioning, and Pagination
This API design interview guide covers the decisions interviewers care about: REST vs GraphQL vs gRPC, versioning, pagination, idempotency, errors, authentication, rate limits, consistency, and backward compatibility. Strong API answers start with consumers and use cases, not with a favorite protocol. The right design for a public mobile API can be wrong for internal service-to-service traffic, bulk data export, or low-latency streaming.
API design interview guide: start with the contract
An API is a contract between producers and consumers. In an interview, clarify:
- Who uses the API: browser, mobile app, partner, internal service, data pipeline, admin tool?
- What are the main workflows: create, read, update, search, stream, export, sync?
- What are the scale and latency expectations?
- Is the API public, private, or partner-only?
- What are the security and permission boundaries?
- How often will the domain model change?
A strong opening: “I would design the API around stable resources and client workflows, then choose REST, GraphQL, or gRPC based on access patterns, coupling, and performance. I would make pagination, errors, idempotency, and versioning explicit from day one because those are expensive to retrofit.”
That framing tells the interviewer you know API design is product design plus distributed systems, not endpoint naming.
REST: resource-oriented and broadly understandable
REST-style APIs model resources and use HTTP semantics. They are a good default for public APIs, CRUD-heavy products, and systems where caching, observability, and broad client compatibility matter.
Example resources for a job platform:
GET /jobs?location=remote&level=senior
GET /jobs/{job_id}
POST /applications
GET /applications/{application_id}
PATCH /applications/{application_id}
Use nouns for resources, not verbs for every action. Prefer POST /applications over POST /applyToJob when the action creates an application resource. But do not be dogmatic: some domain actions, such as POST /invoices/{id}/void, can be clearer than pretending everything is a field update.
HTTP methods matter:
| Method | Use | Idempotent? | |---|---|---| | GET | Read | Yes | | POST | Create or action | Usually no | | PUT | Replace | Yes | | PATCH | Partial update | Usually yes if designed carefully | | DELETE | Delete | Yes in effect |
REST strengths: simple mental model, caching support, standard status codes, easy tooling, browser compatibility, and durable public contracts. REST weaknesses: over-fetching, under-fetching, many round trips for nested views, and awkward modeling for graph-shaped data.
GraphQL: client-shaped data with schema discipline
GraphQL lets clients request exactly the fields they need from a typed schema. It is useful when clients have varied data needs, when screens combine many resources, or when frontend teams need flexibility without many bespoke endpoints.
Example:
query JobPage($id: ID!) {
job(id: $id) {
title
company { name logoUrl }
salaryRange { min max currency }
similarJobs(limit: 5) { id title }
}
}
GraphQL strengths:
- clients avoid over-fetching and under-fetching
- schema is introspectable and strongly typed
- frontend teams can iterate without new endpoint for every screen
- nested data can be fetched in one request
GraphQL risks:
- query complexity and expensive nested requests
- caching is less straightforward than resource URLs
- authorization must be enforced at field and resolver levels
- N+1 resolver bugs can be severe
- schema evolution requires governance
In interviews, avoid saying GraphQL is “better REST.” Say it trades server-side endpoint simplicity for schema and query execution complexity. It works best when you invest in query cost limits, persisted queries, dataloaders, observability, and schema review.
gRPC: typed service calls for internal performance
gRPC uses protocol buffers and HTTP/2 to provide strongly typed service methods, efficient serialization, streaming, and good code generation. It is common for internal microservices, low-latency systems, and polyglot backend environments.
Example service shape:
service JobService {
rpc GetJob(GetJobRequest) returns (Job);
rpc SearchJobs(SearchJobsRequest) returns (SearchJobsResponse);
rpc StreamApplicationEvents(ApplicationEventRequest) returns (stream ApplicationEvent);
}
gRPC strengths: compact payloads, strict schemas, generated clients, bidirectional streaming, deadlines, and internal consistency. Weaknesses: less browser-native, harder for public developer ecosystems, less human-readable over the wire, and more operational complexity for some teams.
Decision rule: use REST for broad public APIs, GraphQL for client-driven product surfaces with varied data shapes, and gRPC for internal service-to-service APIs where strong contracts and performance matter. Many companies use all three in different layers.
Versioning and backward compatibility
Versioning is really about change management. The best API version is the one consumers do not notice because additive changes are backward compatible.
Safe changes:
- adding optional response fields
- adding optional request fields
- adding new endpoints or resources
- adding enum values if clients handle unknowns
- relaxing validation rules carefully
Breaking changes:
- removing or renaming fields
- changing field types or meanings
- making optional fields required
- changing pagination behavior
- changing error formats
- changing authorization assumptions
REST versioning options include path versions (/v1/jobs), header versions, or date-based versions. Path versions are explicit and easy for public APIs. Header versions are cleaner but less visible. Date-based versions can work for large public APIs with frequent incremental changes.
GraphQL often uses schema evolution rather than global versions: add fields, deprecate fields, and monitor usage before removal. gRPC uses protobuf rules: reserve field numbers, add fields safely, and avoid reusing tags.
A senior answer: “I would optimize for backward-compatible additive changes, publish deprecation windows, track consumer usage, and only introduce a new major version when semantics truly break.”
Pagination: offset, cursor, and keyset
Pagination is a favorite interview topic because naive choices fail at scale.
| Type | How it works | Best for | Risk | |---|---|---|---| | Offset | ?limit=20&offset=40 | Small stable lists, admin tools | Slow and inconsistent on changing data | | Page number | ?page=3 | Human navigation | Same issues as offset | | Cursor | ?cursor=abc&limit=20 | Feeds, search, APIs at scale | More complex implementation | | Keyset | WHERE created_at < last_seen | Ordered large datasets | Requires stable sort keys |
Cursor pagination is often the best default for large or changing lists. The cursor should encode the position in a stable sort, not expose fragile database internals. Include next_cursor, possibly prev_cursor, and a has_more flag.
Example response:
{
"data": [],
"pagination": {
"next_cursor": "eyJjcmVhdGVkX2F0Ijoi...",
"has_more": true
}
}
Use deterministic ordering. If sorting by created_at, include a tie-breaker like id. Without stable ordering, clients see duplicates or skipped records when new items arrive.
Errors, idempotency, and retries
Good APIs make failure understandable. Use consistent error shapes:
{
"error": {
"code": "rate_limited",
"message": "Too many requests. Try again later.",
"request_id": "req_123",
"details": {}
}
}
Use HTTP status codes meaningfully: 400 for invalid requests, 401 for unauthenticated, 403 for unauthorized, 404 for missing or hidden resources, 409 for conflicts, 422 for semantic validation, 429 for rate limits, and 5xx for server failures.
Idempotency matters for retries. If a client submits a payment, application, order, or message and times out, retrying should not create duplicates. Use idempotency keys for non-idempotent operations:
POST /applications
Idempotency-Key: 9f1b...
The server stores the key, request fingerprint, result, and expiration. If the same key is retried, return the original result. If the same key is reused with a different payload, return a conflict.
Authentication, authorization, and rate limits
Authentication answers “who are you?” Authorization answers “what are you allowed to do?” Do not blur them.
Common patterns:
- session cookies for browser apps
- OAuth2/OIDC for delegated access and identity
- API keys for server-to-server or partner access
- mTLS or service identity for internal systems
- scoped tokens for least privilege
Authorization should be enforced server-side at the resource and action level. In GraphQL, enforce field-level and resolver-level permissions. In REST, do not rely on hidden UI controls. In gRPC, use interceptors or service-level checks plus domain authorization.
Rate limits protect reliability and fairness. Design limits by identity, token, IP, tenant, endpoint, or cost unit. Return 429 with retry information. For GraphQL, rate limiting by request count is insufficient; use query complexity or persisted queries.
Consistency, concurrency, and partial updates
APIs often sit over mutable data. Interviewers may ask how to avoid lost updates. Options include optimistic concurrency with ETags or version numbers.
Example:
GET /profiles/123 -> ETag: "v7"
PATCH /profiles/123
If-Match: "v7"
If the profile changed since the client fetched it, return 409 Conflict or 412 Precondition Failed. This prevents one client from silently overwriting another's changes.
For PATCH semantics, be clear whether omitted fields mean “unchanged” or “set to null.” Ambiguity causes bugs. Consider JSON Merge Patch or JSON Patch if the domain needs standard partial update semantics.
For long-running operations, do not hold HTTP requests open forever. Return a job resource:
POST /exports -> 202 Accepted
GET /exports/{id}
This makes progress, retry, cancellation, and failure states explicit.
Common API design interview traps
The biggest trap is choosing REST, GraphQL, or gRPC before understanding consumers. Another is designing endpoints for the database schema rather than user workflows.
Other traps:
- no pagination or offset pagination for a fast-changing feed
- inconsistent error formats
- no idempotency for create operations that clients retry
- breaking changes without deprecation tracking
- leaking internal IDs or implementation details unnecessarily
- treating authentication as authorization
- ignoring rate limits and abuse
- returning huge nested payloads without limits
- not including request IDs for debugging
- vague update semantics for PATCH
When debugging an API incident, mention logs with request IDs, structured errors, metrics by endpoint, latency percentiles, rate-limit counters, and consumer impact. Observability is part of API design.
Example interview answer: applications API
Prompt: “Design an API for candidates applying to jobs.”
A strong answer:
“I would expose REST resources because the workflow is resource-oriented and public-client friendly: jobs, candidates, applications, resumes, and application events. POST /applications creates an application with an idempotency key so mobile retries do not duplicate submissions. GET /applications?candidate_id=...&cursor=... uses cursor pagination with stable ordering. Errors use a consistent shape with request IDs. Authorization ensures candidates can see only their own applications and employers can see applications for their jobs. For status changes, I would use explicit transitions such as POST /applications/{id}/withdraw if the action has business rules. I would version with /v1 for public clients, prefer additive changes, and publish deprecations.”
That answer covers resources, retries, pagination, permissions, and evolution.
Resume and interview language
Strong resume bullets show contract quality and operational impact:
- “Designed cursor-paginated REST APIs for high-volume application feeds, eliminating duplicate records during concurrent inserts.”
- “Added idempotency keys and structured error responses to payment-adjacent create endpoints, reducing duplicate submissions after client retries.”
- “Introduced GraphQL persisted queries and complexity limits for mobile clients, cutting over-fetching while protecting backend services.”
- “Migrated internal service calls to gRPC with protobuf contracts and deadlines, reducing p95 latency by 28%.”
Avoid “built APIs” by itself. Name the design problem and the reliability or consumer outcome.
Prep checklist
Before an API design interview, prepare decision rules for REST, GraphQL, and gRPC. Be able to design pagination, errors, idempotency, auth, rate limits, versioning, and long-running operations. Know how to make backward-compatible changes. Practice explaining cursor pagination and why stable ordering matters. The best API answers are boring in the right ways: predictable contracts, explicit failure modes, safe retries, and change management that respects consumers.
Related guides
- REST API Design Interview Questions — Resources, Status Codes, and Idempotency — A practical REST API design interview guide with resource modeling rules, status-code choices, idempotency patterns, pagination, errors, and sample answers.
- API Design Interview Cheatsheet in 2026 — Patterns, Examples, Practice Plan, and Common Traps — A practical API design interview cheatsheet for 2026: how to scope the problem, choose REST/GraphQL/gRPC patterns, model resources, handle auth, versioning, rate limits, and avoid the traps that cost senior candidates offers.
- API Design Mock Interview Questions in 2026 — Practice Prompts, Answer Structure, and Scoring Rubric — Prepare for API design interviews with realistic prompts, REST and event-driven tradeoffs, pagination, idempotency, auth, versioning, rate limits, and a practical scoring rubric.
- Backend System Design Interview Cheatsheet in 2026 — Patterns, Examples, Practice Plan, and Common Traps — A backend System Design interview cheatsheet for 2026 with the core flow, architecture patterns, capacity heuristics, reliability tradeoffs, and traps that separate senior answers from vague box drawing.
- CQRS Interview Guide: When to Split Commands and Queries in a System Design — CQRS is the pattern candidates propose to sound sophisticated and then can't justify. Here is when to actually split reads and writes, what it buys you, and the price you pay for it.
