Overview
REST (Representational State Transfer) is an architectural style for APIs built on HTTP. Everything is a resource identified by a URL (/users/42/orders), clients manipulate resources with standard methods (GET, POST, PUT, PATCH, DELETE), and every request is stateless: it carries all the information the server needs.
REST's strength is uniformity. Because it reuses HTTP semantics, it works with every client, proxy, and CDN, and responses can be cached with standard headers. Its weaknesses are over-fetching and under-fetching (returning too much or needing several calls), which GraphQL and gRPC address in different ways.
Every book has a fixed shelf address (URL). You can look at it (GET), add a new book (POST), replace it (PUT), fix a detail (PATCH), or remove it (DELETE). The librarian does not remember you between visits; you show your card each time (stateless).
When to use it
- Public APIs consumed by many different clients.
- CRUD-style domains (users, products, orders).
- When HTTP caching and CDN support matter.
- Simple integration with browsers, mobile apps, and partners.
Where it shows up in interviews
Recognize it when: design the endpoints for a domain.
- Design the API for Twitter
- Design an e-commerce cart API
- Design a URL shortener API
Recognize it when: large collections returned to clients.
- Design a news feed API
- Design a search results API
Where it is used in real software
A widely admired REST API: consistent resource URLs, idempotency keys on POST, cursor pagination, and versioning by date.
Uses hypermedia links, conditional requests with ETags, and rate limit headers.
The OpenAPI (Swagger) specification documents REST APIs and generates clients and servers.
Key terms
- Resource
- A noun addressed by a URL: /orders/9001.
- Stateless
- The server keeps no client session between requests; each request is self-contained.
- Idempotency
- PUT and DELETE can be repeated safely; POST needs an idempotency key.
- HATEOAS
- Responses include links to related actions; rarely fully implemented in practice.
- Cursor pagination
- Clients page with an opaque cursor instead of page numbers.
Designing a REST resource
- 1Name resources as plural nouns
/orders, /orders/{id}, /users/{id}/orders. Avoid verbs like /getOrders.
- 2Map actions to methods
GET list or item, POST create, PATCH update, DELETE remove.
- 3Choose status codes
201 with Location on create, 204 on delete, 400/404/409 for client errors.
- 4Support filtering and pagination
GET /orders?status=paid&limit=50&cursor=abc.
- 5Version and document
/v1/ prefix or a version header, with an OpenAPI spec.
Endpoints for an orders resource
Consistent URLs, methods, and responses
| Action | Method + path | Success | Notes |
|---|---|---|---|
| List orders | GET /v1/orders?limit=50&cursor=x | 200 | Returns data + next_cursor |
| Get one order | GET /v1/orders/9001 | 200 / 404 | ETag for caching |
| Create order | POST /v1/orders | 201 + Location | Idempotency-Key header |
| Update status | PATCH /v1/orders/9001 | 200 | If-Match for optimistic locking |
| Cancel order | DELETE /v1/orders/9001 | 204 | Or POST /orders/9001/cancel for a business action |
NOWAction: List orders | Method + path: GET /v1/orders?limit=50&cursor=x | Success: 200 | Notes: Returns data + next_cursor
Business actions that are not simple CRUD (cancel, refund) are often modeled as sub-resources or POST actions; consistency matters more than purity.
Implementation
import express from "express"; const app = express();app.use(express.json()); app.get("/v1/orders", async (req, res) => { const limit = Math.min(Number(req.query.limit ?? 50), 100); const { items, nextCursor } = await orders.list({ cursor: req.query.cursor as string, limit }); res.json({ data: items, next_cursor: nextCursor });}); app.post("/v1/orders", async (req, res) => { const key = req.header("Idempotency-Key"); if (!key) return res.status(400).json({ error: "Idempotency-Key required" }); const existing = await idempotency.get(key); if (existing) return res.status(existing.status).json(existing.body); // replay const order = await orders.create(req.body); await idempotency.save(key, { status: 201, body: order }); res.status(201).location(`/v1/orders/${order.id}`).json(order);}); app.delete("/v1/orders/:id", async (req, res) => { await orders.cancel(req.params.id); res.status(204).end();});Complexity and performance
Can require several round trips.
GET responses with standard headers.
May over-fetch.
Trade-offs
REST is simple, cacheable, and universal; GraphQL lets clients fetch exactly what they need in one request but is harder to cache and secure.
gRPC is faster and strongly typed for service-to-service calls; REST is friendlier for browsers and public APIs.
Offsets are simple but slow and inconsistent on large, changing data; cursors are stable and efficient.
Variants and related techniques
A convention for consistent REST payloads, relationships, and errors.
A REST layer tailored to one client (mobile, web) that aggregates internal calls.
Common mistakes
- Verbs in URLs (/createOrder).
Fix: Use nouns and HTTP methods: POST /orders.
- Returning unbounded lists.
Fix: Always paginate and cap the limit.
- Breaking changes without versioning.
Fix: Add fields freely; remove or rename only in a new version.
Interview questions
Why is statelessness important in REST?
Any server can handle any request, which allows horizontal scaling behind a load balancer and makes failures easier to recover from.
How do you make POST safe to retry?
Require an Idempotency-Key header. Store the result for each key; if the same key is retried, return the stored result instead of creating a duplicate.
How do you version a REST API?
Common options are a path prefix (/v1), a header, or date-based versions (Stripe). Keep changes additive where possible and support old versions for a deprecation period.
Practice problems
| Problem | Difficulty | What it trains |
|---|---|---|
| Design REST endpoints for a blog | Easy | Resources and methods. |
| Design the Stripe-like payments API | Medium | Idempotency and errors. |
| Design pagination for a feed with 1B items | Medium | Cursors. |