Skip to content
Codeloom
REST APIs

What Is REST? The Architectural Style Explained

A clear introduction to REST — resources, URIs, HTTP verbs, statelessness, status codes, JSON conventions, and how REST compares to RPC and GraphQL.

·10 min read · By Codeloom
Beginner 11 min read

What you'll learn

  • What REST actually means — beyond "an API with URLs"
  • How to model resources and URIs
  • How HTTP verbs map to operations
  • Why statelessness matters
  • Which status codes to use, and when
  • How REST compares to RPC and GraphQL

Prerequisites

  • A rough idea of HTTP — requests, responses, headers
  • Comfort with JSON

REST is one of those words people use loosely. Strictly speaking, it is an architectural style described by Roy Fielding in his 2000 PhD thesis. Loosely, it is the dominant way people design HTTP APIs today: resources at URLs, verbs for actions, JSON bodies, sensible status codes.

This post gives you a working definition you can apply when designing or consuming an API.

What REST actually is

REST stands for Representational State Transfer. The unpacked version:

  • Representational — the client never touches the server’s internal data directly. It sees a representation (usually JSON) of the current state.
  • State Transfer — every request carries enough information for the server to act. The server keeps no per-client memory between requests.

Fielding’s thesis defined six constraints. The ones you will care about day to day:

  1. Client-server. Separate the UI from the data store.
  2. Stateless. Each request is self-contained.
  3. Cacheable. Responses are explicit about whether and how long they can be cached.
  4. Uniform interface. Everything is shaped consistently — resources, methods, status codes.
  5. Layered system. Clients should not care if they are talking to the origin, a load balancer, or a CDN.

The sixth — code on demand — is rarely used. The first four are what define REST in practice.

Resources

A resource is anything worth naming in your system: a user, an order, a blog post, a collection of comments. In a REST API, every resource has a URL (formally a URI) that identifies it.

/users
/users/42
/users/42/orders
/orders/9001

Two patterns to internalise:

  • Collections are plural nouns: /users, /orders, /todos.
  • A single item is the collection plus an identifier: /users/42.

Avoid verbs in URLs. The HTTP method is the verb — see the next section.

GET  /users           # list users
GET  /users/42        # get user 42
POST /users           # create a user

# Anti-pattern:
GET  /getAllUsers
POST /createUser

HTTP verbs

Each method has a defined meaning:

MethodPurposeIdempotentSafe
GETRead a resourceYesYes
POSTCreate a new resourceNoNo
PUTReplace a resourceYesNo
PATCHUpdate part of a resourceNo (in practice)No
DELETERemove a resourceYesNo

Two terms worth knowing:

  • Safe — does not change server state. Browsers will prefetch safe methods.
  • Idempotent — calling N times has the same effect as calling once. Important for retries.

The mapping for a typical resource looks like this:

GET    /todos        # list
GET    /todos/1      # read one
POST   /todos        # create
PUT    /todos/1      # replace
PATCH  /todos/1      # partial update
DELETE /todos/1      # delete

This is exactly the shape we built in Build Your First REST API with Express.

Statelessness

Every request must carry everything the server needs. The server does not remember the previous request. If the client is “logged in,” the credentials (a token, a cookie) accompany every request.

Why this matters:

  • Horizontal scaling. Any server in a fleet can handle any request. No “sticky sessions” required.
  • Reliability. A failed server can be replaced without losing per-client memory.
  • Cacheability. A stateless request and its response are a self-contained pair — easy to cache.

Statelessness does not forbid databases. The resource has state. The conversation between client and server does not.

Representations

The same resource can have multiple representations — JSON, XML, an image. Clients ask for what they want via the Accept header:

Accept: application/json
Accept: application/xml
Accept: text/csv

In modern APIs, you almost always serve JSON. A typical response:

{
  "id": 42,
  "name": "Alice",
  "email": "alice@example.com",
  "createdAt": "2026-06-15T10:30:00Z"
}

Conventions worth following:

  • camelCase for JSON keys in JavaScript ecosystems (snake_case in Python ones)
  • ISO 8601 for timestamps (2026-06-15T10:30:00Z)
  • Stable IDs — never change a resource’s ID once issued
  • Include the ID in the resource body, not only in the URL

Status codes

HTTP defines a status code for every outcome. Use them; do not return 200 with {"error": "..."} in the body.

Successful:

  • 200 OK — generic success with a body
  • 201 Created — a POST created a resource; usually return the new resource
  • 204 No Content — success with nothing to say (typical for DELETE)

Client errors (something is wrong with the request):

  • 400 Bad Request — malformed or invalid input
  • 401 Unauthorized — missing or bad credentials
  • 403 Forbidden — authenticated but not allowed
  • 404 Not Found — resource does not exist
  • 409 Conflict — request collides with current state (e.g. duplicate email)
  • 422 Unprocessable Entity — well-formed but semantically wrong (popular in some communities; 400 is fine too)
  • 429 Too Many Requests — rate limit hit

Server errors (something is wrong on your side):

  • 500 Internal Server Error — generic failure
  • 502 Bad Gateway — an upstream service failed
  • 503 Service Unavailable — temporary overload or maintenance

A request that looks fine but causes a server bug is 500, not 400.

Try it yourself. For each scenario, write down the right status code:

  1. A client POSTs {} to /users when email is required.
  2. A client GETs /users/9999 and no such user exists.
  3. A client DELETEs /users/5 and the deletion succeeds.
  4. A client POSTs /users with an email that is already taken.
  5. The database connection drops mid-request.

Answers: 400, 404, 204, 409, 500.

Query strings

Use the URL path for identifying resources. Use the query string for filters, sorting, paging, and search.

GET /todos?done=false&sort=createdAt&limit=20&page=2
GET /products?category=shoes&priceMax=100
GET /search?q=hello+world

Two paging conventions:

  • Offset/limit — simple, fine for small datasets, breaks down on huge ones
  • Cursor-based — pass an opaque token from the previous response; scales well

Versioning

APIs change. The two common strategies:

# In the URL
GET /v1/users
GET /v2/users

# In a header
GET /users
Accept: application/vnd.example.v2+json

URL versioning is more common because it is visible and easy to route. Header-based is purer (the URL is the resource, not the version) but harder to debug.

Bumping a major version is for breaking changes. Adding optional fields to a response is not breaking — old clients ignore them.

HATEOAS

You will occasionally hear the term HATEOAS — Hypermedia As The Engine Of Application State. The idea: responses include links to related resources, so clients discover the API by following links rather than knowing URLs in advance.

{
  "id": 42,
  "name": "Alice",
  "links": {
    "self":   "/users/42",
    "orders": "/users/42/orders"
  }
}

In theory, this is what makes an API “truly RESTful.” In practice, very few real-world APIs do it. Most ship URL conventions in documentation and call themselves REST anyway. That trade-off is fine; just know the term if it comes up.

REST vs RPC

RPC (Remote Procedure Call) APIs are organised around actions, not resources:

POST /createUser
POST /sendEmail
POST /cancelOrder

JSON-RPC, gRPC, and SOAP are RPC styles. The trade-offs:

  • RPC is great when your domain is action-heavy (“compute tax,” “cancel reservation”). gRPC in particular has excellent tooling for microservices.
  • REST is great when your domain maps cleanly onto nouns and CRUD operations. It is also the default for public APIs because browsers, caches, and HTTP tooling already understand it.

Most teams pick REST and end up sneaking in the occasional RPC-style endpoint (POST /orders/123/cancel) where the resource model is awkward. That is fine. Pragmatism beats purity.

REST vs GraphQL

GraphQL is a query language that lets the client ask for exactly the fields it wants, in a single request:

{
  user(id: 42) {
    name
    orders(last: 3) {
      total
      items { name price }
    }
  }
}

Strengths:

  • One round trip can fetch nested data that REST needs several requests for
  • The client picks the fields, reducing over-fetching
  • Strongly typed schema with great editor tooling

Trade-offs:

  • More server complexity — resolvers, schema, caching strategies
  • HTTP caching is harder (everything is a POST to /graphql)
  • Mental model is heavier for beginners

Use GraphQL when the client’s data needs are highly varied (a complex frontend hitting many entities). Use REST when the data model is roughly flat and shaped like resources. Many companies ship both — REST for partners and webhooks, GraphQL for their own frontend.

A “good enough” REST checklist

When you build or review a REST API, the questions worth asking:

  • Are URLs resource-based with plural collections and IDs?
  • Does each endpoint use the right HTTP method?
  • Does each response carry the right status code?
  • Is the API stateless — no per-client memory on the server?
  • Are errors shaped consistently ({"error": "..."} or similar)?
  • Do timestamps use ISO 8601 and IDs stay stable?
  • Are paging, sorting, and filtering done with query strings?

Most “REST violations” you will see in the wild are not deep philosophical errors. They are tiny inconsistencies: a 200 OK with an error body, a POST that should have been a DELETE, an action verb in a URL. Polish those, and your API will feel professional.

Try it yourself. Sketch the URLs and methods for a blogging API with posts, comments on each post, and tags. Cover: list all posts; create a post; fetch one post with its comments; add a comment; delete a comment; list posts that have a given tag. Mark each line with the HTTP method.

Recap

You now know:

  • REST is an architectural style, not a protocol
  • Resources live at URLs; HTTP verbs are the actions
  • The server is stateless — every request carries what it needs
  • Status codes are the API’s way of speaking precisely
  • JSON, ISO timestamps, and stable IDs are the conventions to follow
  • RPC suits action-heavy domains; GraphQL suits clients with varied data needs

Next steps

You have the theory; you have an Express implementation. The natural progression from here is persistence (a database), validation (a schema library), authentication (tokens or sessions), and testing. Each builds on the resource-and-status mental model you now have.

For a hands-on starting point, revisit Build Your First REST API with Express and try replacing the in-memory array with a SQLite database.

Questions or feedback? Email codeloomdevv@gmail.com.