Lexicon
⌘K
Explore
ExploreGuidesNotes
WriteMy LibraryBooks
Sign in
Browse

Learning

  • Building a Sustainable Learning System

Networking

  • OSI Model
  • TCP vs UDP
  • TCP/IP Model

Programming

  • Start Here — How to Solve a Problem
  • From if/else to DSA — The Whole Path
  • Problem Solving
  • DSA Practice — Learn by Solving
  • C Programming
  • Python

Software Craft

  • How to Structure a Software Project
  • Debugging Software Systematically
  • Designing Clean and Maintainable User Interfaces

Tools & Workflow

  • A Practical Introduction to Git and Version Control
  • Introduction to Linux for Developers

Web & APIs

  • Understanding REST APIs

Writing

  • Writing Better Technical Documentation
New noteSuggest a guide

Lexicon

a personal knowledge base

Web & APIs

Understanding REST APIs

Updated 2026-07-14 · 8 min read

REST is one of those terms everyone uses and few can define. People call any API that returns JSON a "REST API," which isn't quite right, but it's close enough that the confusion is harmless most of the time. What matters for a working developer is understanding the handful of ideas that make an HTTP API predictable, and how to apply them so other people — including your future self — enjoy using what you build.

This guide covers the model behind REST, the mechanics of HTTP that carry it, and the practical design decisions (auth, versioning, pagination, errors) that separate a pleasant API from a frustrating one.

What REST actually means

REST — Representational State Transfer — is an architectural style, not a protocol. It describes how to build networked systems around a few constraints. The one that matters most in practice: your API is organized around resources, and you act on those resources using standard HTTP methods.

A resource is a thing your system knows about — a user, an order, an article. Each resource has a URL (its identifier), and you interact with it by sending HTTP requests. Crucially, REST is stateless: every request carries everything the server needs to handle it. The server doesn't remember anything about you between requests, which is what lets an API scale across many servers.

The practical payoff of following the style is predictability. Once someone learns how one part of your API behaves, they can guess the rest.

Designing around resources

Good REST URLs name things (nouns), not actions (verbs). The HTTP method already supplies the verb, so putting a verb in the URL is redundant.

GET    /users              list users
POST   /users              create a user
GET    /users/42           fetch user 42
PUT    /users/42           replace user 42
PATCH  /users/42           update part of user 42
DELETE /users/42           delete user 42

Relationships nest naturally:

GET    /users/42/orders        orders belonging to user 42
GET    /users/42/orders/7      one specific order

A few conventions keep this clean:

  • Use plural nouns for collections: /articles, not /article.
  • Don't encode actions in the path: prefer POST /orders over /createOrder.
  • Keep IDs in the path, filters in the query string: /articles?tag=git&sort=recent.

When an operation genuinely isn't a resource — say, "publish this draft" — it's fine to model it as a sub-resource or a controller-style endpoint like POST /articles/7/publish. Purity is less important than clarity.

HTTP methods and what they promise

Each method carries an expectation. Two properties matter:

  • Safe — doesn't change anything on the server (GET).
  • Idempotent — doing it twice has the same effect as doing it once.
MethodSafeIdempotentTypical use
GETyesyesread a resource
POSTnonocreate a resource
PUTnoyesreplace a resource wholesale
PATCHnonoupdate part of a resource
DELETEnoyesremove a resource

Idempotency isn't academic. If a client's network drops and it retries a PUT or DELETE, nothing breaks — the second call lands on the same state. A POST retried blindly can create duplicates, which is why creation endpoints sometimes accept an idempotency key.

Status codes that tell the truth

The status code is the first thing a client checks, so use the right one. You don't need all of them — you need to use a small set correctly.

The families:

  • 2xx — success. 200 OK, 201 Created (with a Location header for the new resource), 204 No Content (success, nothing to return).
  • 3xx — redirection. 301 moved permanently, 304 Not Modified (the cached copy is still good).
  • 4xx — the client's fault. 400 Bad Request, 401 Unauthorized (not authenticated), 403 Forbidden (authenticated but not allowed), 404 Not Found, 409 Conflict, 422 Unprocessable Entity (validation failed), 429 Too Many Requests.
  • 5xx — the server's fault. 500 Internal Server Error, 503 Service Unavailable.

The distinction people get wrong most often is 401 versus 403: 401 means "I don't know who you are," 403 means "I know who you are, and you can't do this."

Never return 200 OK with an error message in the body. It forces clients to parse the body just to learn whether the request worked, which defeats the point of status codes.

Requests, responses, and headers

A request and response are more than a URL and a body — headers carry the metadata that makes content negotiation and caching work.

A typical request:

POST /articles HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGci...
 
{
  "title": "Understanding REST",
  "body": "REST is an architectural style..."
}

A typical response:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /articles/7
 
{
  "id": 7,
  "title": "Understanding REST",
  "createdAt": "2026-07-14T10:00:00Z"
}

The headers worth knowing:

  • Content-Type — the format of the body you're sending.
  • Accept — the format you'd like back.
  • Authorization — credentials (usually a token).
  • Cache-Control / ETag — how responses may be cached and revalidated.

Authentication and authorization

Because REST is stateless, the client must prove who it is on every request — usually with a token in the Authorization header.

Authorization: Bearer <token>

Common approaches:

  • API keys — simple, good for server-to-server; treat them like passwords.
  • Bearer tokens / JWTs — a signed token the client sends each time; the server verifies it without storing session state.
  • OAuth 2.0 — the standard when third parties act on a user's behalf.

Two hard rules: always serve APIs over HTTPS so tokens aren't sent in the clear, and never put secrets or tokens in the URL — URLs end up in logs, browser history, and analytics. Keep credentials in headers.

Authorization (what a user is allowed to do) is separate from authentication (who they are). Check permissions on every request; never trust the client to hide a button and call it security.

Pagination, filtering, and versioning

Collections grow. Returning ten thousand records in one response is slow and rude, so paginate. Two common styles:

GET /articles?page=2&limit=20          offset-based, simple
GET /articles?limit=20&cursor=abc123   cursor-based, stable under inserts

Cursor pagination is sturdier for large or fast-changing data because it doesn't skip or duplicate items when rows are added mid-scroll. Whichever you pick, return the metadata clients need to continue — total count or a next cursor.

Filtering and sorting belong in the query string:

GET /articles?tag=git&author=42&sort=-createdAt

APIs change, and you can't break existing clients when they do. Versioning gives you room to evolve. The most common approach is a version in the path:

GET /v1/articles

Introduce a new version only for breaking changes — removing a field, renaming one, changing a type. Additive changes (a new optional field) don't need a new version; well-behaved clients ignore fields they don't recognize.

Errors people can act on

An error response should tell the client what went wrong and, ideally, how to fix it. A bare status code with an empty body leaves developers guessing.

Return a consistent, machine-readable shape:

{
  "error": {
    "code": "validation_failed",
    "message": "The title field is required.",
    "field": "title"
  }
}

Good error design means: a correct status code, a stable machine-readable code the client can branch on, and a human-readable message for the developer reading logs. Keep the shape identical across every endpoint so clients handle errors in one place.

If you're documenting these responses for others, technical documentation covers how to write reference docs people can actually follow.

Common mistakes

  • Verbs in URLs. /getUser and /deleteOrder fight the HTTP methods. Use nouns plus the right method.
  • Returning 200 for errors. Let the status code carry the outcome; don't bury it in the body.
  • Confusing 401 and 403. Not-authenticated is not the same as not-allowed.
  • Leaking data over HTTP or in URLs. Always HTTPS; keep tokens in headers.
  • No pagination. An endpoint that returns everything will eventually fall over.
  • Breaking changes without versioning. Renaming a field silently breaks every client at once.
  • Inconsistent responses. Different error shapes per endpoint force clients to special-case everything.
  • Ignoring idempotency. Retried POSTs create duplicates when there's no safeguard.

Checklist

  • Are my URLs resource nouns, with the HTTP method supplying the verb?
  • Am I returning accurate status codes, including the right 4xx for client errors?
  • Is every request authenticated over HTTPS, with tokens in headers not URLs?
  • Do collections support pagination and filtering?
  • Is there a versioning strategy for breaking changes?
  • Do errors return a consistent, machine-readable shape with a helpful message?
  • Are my PUT and DELETE operations genuinely idempotent?

Keep learning

The fastest way to internalize this is to consume a well-designed public API and a badly-designed one back to back — you'll feel the difference in minutes, and the good habits become obvious. Then design a small API of your own and hand the docs to a friend; the questions they ask reveal exactly where your design is unclear.

Concrete next steps:

  • Build a tiny CRUD API (articles, or a todo list) and exercise every method and status code by hand with curl.
  • Read about caching with ETag and Cache-Control — it's the next big lever after correctness.
  • Explore alternatives like GraphQL to understand the tradeoffs REST is making.
  • Learn to write great reference docs in technical documentation, and organize the codebase behind your API with structuring a software project.
← PreviousIntroduction to Linux for DevelopersNext →Writing Better Technical Documentation
Reading progress0%

Curated guide

Part of the Lexicon knowledge base — hand-written and kept up to date.

Browse all guides →