API Design: REST, gRPC, GraphQL
Intermediate · 20 min read · High-Level Design, Services & Communication
Resource modelling, pagination, versioning, idempotency and choosing a protocol.
The API is the contract between your system and its clients — mobile apps, web frontends, partners, and other services. A good API is easy to use correctly and hard to use incorrectly; it can evolve without breaking clients; and it behaves predictably under retries and failures.
In an HLD interview, defining the API early forces you to turn vague features into concrete operations and reveals what data your system needs.
The three common styles
Representational State Transfer models the system as resources identified by URLs, manipulated with standard HTTP methods.
Designing REST resources well
Idempotent means making the same request multiple times has the same effect as making it once. This property is what makes retries safe.
Idempotency keys
Networks fail after the server has processed a request but before the client gets the response. The client can't tell whether the operation happened. For non-idempotent operations like "charge this card" or "place this order", a blind retry could double-charge.
Pagination
Never return unbounded lists.
Versioning and evolution
APIs change; clients (especially mobile apps that users don't update) can't change instantly.
Serialization formats and schema evolution
The format your services exchange data in matters most when the data outlives a deploy — in queues, logs and storage.
Other concerns for production APIs
Interview questions
- How do you make a 'create payment' endpoint safe to retry?
- Why is offset pagination a problem for a social media feed?
- When would you choose gRPC over REST?
- What changes to an API are backward compatible?
Practice
Write your answer before opening the suggested one. Anything you mark "Partly" or "Missed it" comes back in your review deck.
Further reading
Key takeaways
- REST for public, cacheable, resource-oriented APIs; gRPC for fast, typed internal calls; GraphQL for flexible client-driven data fetching.
- Design around resources, use HTTP methods and status codes meaningfully, and keep error formats consistent.
- Make unsafe operations retry-safe with idempotency keys.
- Prefer cursor-based pagination for large or changing datasets.
- Evolve APIs additively; version and deprecate when breaking changes are unavoidable.
Previous: Content Delivery Networks · Next: Real-Time Communication