Notifications

No notifications

/Phase 3

REST Principles

REST β€” Representational State Transfer

REST is an architectural style for designing networked APIs. It treats everything as a resource with standard operations.

Core Principles

PrincipleMeaning
Client-ServerFrontend and backend are separate
StatelessEach request contains all info needed
Uniform InterfaceConsistent URL patterns & methods
Resource-BasedEverything is a resource (user, post, order)
JSON FormatStandard data exchange format

RESTful URL Design

GET    /api/users          β†’ List all users
GET    /api/users/42       β†’ Get user #42
POST   /api/users          β†’ Create new user
PUT    /api/users/42       β†’ Replace user #42
PATCH  /api/users/42       β†’ Update parts of user #42
DELETE /api/users/42       β†’ Delete user #42

GET /api/users/42/posts β†’ Get posts by user #42

❌ Non-RESTful vs βœ… RESTful

Non-RESTful ❌RESTful βœ…
GET /getUser?id=42GET /users/42
POST /createUserPOST /users
POST /deleteUser/42DELETE /users/42
GET /getUserPosts?uid=42GET /users/42/posts

On this page

Detailed Theory

REST is just a convention for designing HTTP APIs. It is not a protocol, library, or framework β€” it is a set of agreements about how to name URLs, which HTTP method to use, and what the response should look like. Once your team agrees, every endpoint becomes predictable: GET /users/42 reads user 42, DELETE /users/42 deletes them. No surprises.

What REST Actually Is

REST = REpresentational State Transfer. The big idea: your server exposes resources (users, posts, orders), and clients use standard HTTP verbs to read or change them. The URL says *what* (the noun); the method says *how* (the verb).

GET    /posts          β†’ list posts
GET    /posts/7        β†’ read post 7
POST   /posts          β†’ create a new post
PUT    /posts/7        β†’ replace post 7
PATCH  /posts/7        β†’ update part of post 7
DELETE /posts/7        β†’ delete post 7

If you can read those URLs and immediately know what they do, that is REST working as intended.

The Six Constraints (Plain English)

1. Client–server β€” UI and data live on different machines. 2. Stateless β€” every request carries everything the server needs (token, body). The server does not remember you between requests. 3. Cacheable β€” responses say if/how long they can be cached. 4. Uniform interface β€” same rules everywhere (URLs are nouns, methods are verbs, JSON is JSON). 5. Layered β€” there can be proxies/CDNs/load balancers in between, the client cannot tell. 6. Code on demand *(optional)* β€” server can ship JS to the client.

For day-to-day work, stateless + uniform interface are the two that matter most.

Resources Are Nouns, Not Actions

A classic beginner mistake: putting verbs in the URL.

❌ POST /createUser
❌ GET  /getAllPosts
❌ POST /post/7/delete

βœ… POST /users βœ… GET /posts βœ… DELETE /posts/7

The HTTP method is already the verb. The URL only names *what* you are acting on.

URL Naming Rules That Pay Off

  • Plural nouns for collections: /users, /orders, /posts.
  • lowercase, hyphenated: /blog-posts not /BlogPosts.
  • Nest only one level deep: /users/42/posts is fine; /users/42/posts/9/comments/3/replies is a smell β€” flatten it.
  • No file extensions: /users/42 not /users/42.json (use the Accept header instead).
  • IDs are opaque: prefer numeric IDs or UUIDs in the URL, not slugs that change.

Beginner Mistakes to Skip

1. Using GET to change data. GET must be safe and idempotent. A GET /posts/7/delete will be triggered by browser prefetchers, crawlers, antivirus scanners β€” and your data will vanish. 2. Returning 200 for errors. A failed request is not OK. Use 4xx/5xx so clients can branch on response.ok. 3. Inconsistent response shapes. Some endpoints return an array, some an object, some { data: [...] }. Pick one envelope and stick to it. 4. Leaking the database. GET /users should not dump every column including password_hash. Map to a DTO. 5. Putting auth in the URL. Tokens belong in the Authorization header, never in query strings (they end up in logs). 6. Versioning by guesswork. Decide /api/v1/... on day one. Adding versions later is painful.

Intermediate: Status Codes That Matter

You do not need all 60. These cover ~95% of real APIs:

CodeMeaningWhen
200OKSuccessful GET / PUT / PATCH
201CreatedSuccessful POST that made a resource
204No ContentSuccessful DELETE or empty response
400Bad RequestMalformed JSON, missing field
401UnauthorizedNo / invalid token
403ForbiddenLogged in but not allowed
404Not FoundResource does not exist
409ConflictDuplicate (email already taken)
422Unprocessable EntityValidation failed (semantic)
429Too Many RequestsRate limited
500Internal Server ErrorYour code threw
503Service UnavailableDown for maintenance

Rule of thumb: 401 = who are you?, 403 = I know you, you cannot do this, 404 = nothing here, 409 = state conflict.

Intermediate: Idempotency & Safety

  • Safe = does not change state (GET, HEAD, OPTIONS).
  • Idempotent = doing it twice = doing it once (GET, PUT, DELETE).
  • Non-idempotent = each call has effect (POST, PATCH usually).
Why you care: clients retry on network errors. If POST is not idempotent, retries can create duplicate orders. Fix it with an Idempotency-Key header that the server stores for a few hours.

POST /payments
Idempotency-Key: 5b1c-...-f9
β†’ first call: charges $20, returns 201
β†’ retry of same key: returns the original 201, no double charge

Intermediate: Consistent Response Envelope

Pick one of these shapes for your whole API:

// Success
{
  "data": { "id": 7, "title": "Hello" },
  "meta": { "requestId": "req_123" }
}

// Error (RFC 7807-ish) { "error": { "code": "VALIDATION_FAILED", "message": "Email is required", "details": [{ "field": "email", "issue": "required" }] } }

Clients then write one error handler that reads error.code, not 50 special cases.

Intermediate: Pagination, Filtering, Sorting

Never return an unbounded list. Three pagination styles:

?page=2&limit=20         β†’ easy, but slow on huge tables
?offset=40&limit=20      β†’ same problem, classic SQL
?cursor=eyJpZCI6NDB9      β†’ fastest, stable across inserts

Filtering and sorting belong in the query string:

GET /posts?author=42&tag=node&sort=-createdAt&fields=id,title&limit=20

The - prefix on sort means descending. fields lets clients ask for sparse responses (mobile saves bandwidth).

Advanced: Versioning Strategies

  • URL path /api/v1/users β€” most common, easiest for clients to discover.
  • Header Accept: application/vnd.myapp.v2+json β€” cleaner URLs, harder to debug.
  • Query param ?version=2 β€” works but easy to forget.
Whichever you pick: never break v1. Add v2 alongside, deprecate slowly with a Sunset header.

Advanced: HATEOAS (and Why You Probably Will Not Use It)

The purest form of REST says responses should include links to next actions:

{
  "id": 7,
  "status": "draft",
  "_links": {
    "self":    { "href": "/posts/7" },
    "publish": { "href": "/posts/7/publish", "method": "POST" }
  }
}

In practice almost no public API does this β€” clients hard-code URLs. Know the term, use it for hypermedia-driven internal systems if you ever build one.

Advanced: Caching with ETags

Let clients ask "has this changed?" without re-downloading the whole resource:

Server: ETag: "abc123"
Client: If-None-Match: "abc123"
Server: 304 Not Modified  ← no body, saves bandwidth

Great for read-heavy endpoints (product catalogs, profiles).

Advanced: Bulk Operations & Async Jobs

REST is awkward for "do 1000 things". Two real-world patterns:

  • Bulk endpoint: POST /users/bulk accepting an array, returning per-item results.
  • Async job: POST /exports returns 202 Accepted with a job URL the client polls (GET /exports/abc) until status is done.

Practice Path

1. Design URLs and methods for a blog (posts, comments, likes, users) on paper. 2. Build it in Express, return the same envelope shape everywhere. 3. Add pagination + filtering + sorting on GET /posts. 4. Add an Idempotency-Key flow for POST /comments and prove duplicate calls do not duplicate rows.