█
LastWrite
  • > Curriculum
  • > Pricing
  • > For Educators
  • > About
  • > Contact
Log InGet Started

Questions, concerns, bug reports, or suggestions? We read every message, write to us at [email protected].

More ways to reach us →
LastWrite

Structured computer science lessons for aspiring developers and security professionals.

[email protected]

(201) 785-7951

Mon–Fri, 9 AM–5 PM EST

Learn

  • Curriculum
  • Pricing

Company

  • About
  • For Educators & Schools
  • Contact Us

Legal

  • Terms of Service
  • Privacy Policy
© 2026 LastWrite. All rights reserved.
Curriculum/Software Engineering/System Design/API Design: REST vs GraphQL vs gRPC
45 minIntermediate

API Design: REST vs GraphQL vs gRPC

After this lesson, you will be able to: Choose between REST, GraphQL, and gRPC for a given problem, and design an API with versioning, pagination, idempotency, and consistent error handling.

Every system has an API at its boundary, and how you design it determines whether other teams can build on your service without constant questions. This lesson covers the three dominant API styles and the design decisions (versioning, pagination, idempotency) that separate an API people enjoy using from one they curse.

Prerequisites:Microservices vs Monolith

REST vs GraphQL vs gRPC

REST exposes resources over HTTP verbs (GET/POST/PUT/DELETE) and is the default for public web APIs: simple, cacheable, universally understood. GraphQL exposes a single endpoint where the client asks for exactly the fields it wants, which solves over-fetching and under-fetching for complex, client-driven UIs (mobile apps with many screens). gRPC uses Protocol Buffers over HTTP/2 for fast, strongly-typed, binary calls, and shines for internal service-to-service communication where speed and contracts matter more than browser-friendliness. Rule of thumb: REST for public APIs, GraphQL for rich client-driven product APIs, gRPC between your own backend services.

Versioning and pagination

Two decisions every real API has to make on day one.

css
// Versioning: never break existing clients. Put the version in the path or a header.
GET /v1/orders // path versioning (most common, most visible)
GET /orders
Accept: application/vnd.api+json; version=2 // header versioning
// Pagination: never return an unbounded list.
// Offset pagination (simple, but slow + unstable on deep pages):
GET /orders?limit=20&offset=40
// Cursor pagination (stable + fast for large/changing data sets):
GET /orders?limit=20&cursor=eyJpZCI6MTIzfQ
// Response includes the next cursor:
{ "data": [ ... ], "nextCursor": "eyJpZCI6MTQzfQ" }

💡 Idempotency: the key that prevents double charges

A mutating request can be sent twice (the client retried after a timeout, even though the first one actually succeeded). Idempotency means a repeated request has the same effect as sending it once. GET/PUT/DELETE are naturally idempotent; POST is not. The standard fix, used by Stripe and every serious payments API, is an idempotency key: the client sends a unique `Idempotency-Key` header, the server stores the result against that key, and a retry with the same key returns the stored result instead of charging again. Design this in from the start for anything that creates money, orders, or emails.

Consistent errors and contracts

Pick one error envelope and use it everywhere: a stable machine-readable `code`, a human `message`, and optional `details`. Use HTTP status codes correctly (400 for bad input, 401 vs 403 for auth, 404 for missing, 409 for conflict, 429 for rate limited, 500 for your fault). For gRPC and GraphQL the transport differs but the principle is identical: predictable, documented error shapes mean clients write less defensive code. Pair this with the OpenAPI documentation from the fundamentals docs lesson so the contract is published, not folklore.

Common mistakes only experienced engineers catch

Shipping v1 with no versioning plan, then breaking every client when v2 ships. Version from the first release. Returning unbounded lists with no pagination, which works in dev and falls over in production. Non-idempotent payment or order endpoints, leading to double charges on client retries. Reaching for GraphQL or gRPC because they are fashionable when a boring REST API would have been simpler and cacheable. Inconsistent error shapes across endpoints, so every client has to special-case your API.

Quick Check

A mobile client retries a 'create payment' POST after a network timeout. How do you prevent a double charge?

Pick the standard solution.

Sign in and purchase access to unlock this lesson.

Sign in to purchase
←Microservices vs Monolith
Back to System Design
Reliability and Fault Tolerance→