Updated on 09 Dec, 202550 mins read 63 views

REST (Representation State Transfer) is an architectural style for designing scalable and maintainable networked applications – most commonly used for building APIs over HTTP.

It is not protocol and not tied to JSON. REST is a set of architectural constraints. When followed correctly, these constraints produce systems that are:

  • Scalable
  • Cache-friendly
  • Loosely coupled
  • Fault tolerant
  • Evolvable over time

REST Is Not What Most People Think:

REST Is:

  • An architectural style
  • A set of constraints
  • A system-level design philosophy

Rest Is NOT:

MythReality
REST = JSONREST can use JSON, XML, protobuf, binary
REST = HTTPREST is an architecture; HTTP is a transport
REST = CRUDCRUD fits REST well, but REST is about resources and state transitions, not databases.

REST = Representational State Transfer

Let's break into this into parts:

1 Representational

A representation is the format of the data you send or receive.

A resource (like a user, product, order, or photo) is stored on the server, but the server does not send the actual database object. Instead, it sends a representation of it.

Below are the formats in which data can be represented:

  • JSON
  • XML
  • HTML
  • CSV
  • Protobuf
  • Even binary

Example Resource: User

Representation in JSON:

{
	"id": 7,
	"name": "TheJat",
	"email": "...."
}

The same resource could be represented as XML:

<user>
	<id>7</id>
	<name>TheJat</name>
</users>

Representation = formatted snapshot of a resource.

2 State

State means the current data of a resource.

When you request something, you want the state of that resource at that moment.

Example:

Product 300 State:

name: "MacBook"
price: 130000
stock: 15
colour: "silver"

This is the state of the product.

But you don't get the database object – you get a representation of that state (JSON/XML/etc.).

State = the real data stored on the server.

3 Transfer

Transfer means sending the representation of the state between client and server over HTTP.

  • Client: sends request
  • Server: transfers a representation of the resource state back
  • Client: updates its UI using that representation

Example:

Client:

GET /products/300

Server transfers representation:

{
  "id": 300,
  "name": "MacBook",
  "price": 130000,
  "stock": 15
}

This is the transfer part of REST.

Transfer = sending the resource's state (in some representation) over the network.

REST Core Constraints (The Laws of REST)

To call an API a true REST API, it must follow all six constraints:

1 Client-Server Separation

The client and server must be completely independent.

  • The client handles UI/UX
  • The server handles data and business logic
  • They evolve independently

They communicate only through requests and responses, not shared memory.

Benefits:

  • Independent deployments
  • Multiple client types (web, mobile, IoT)
  • Better testability
  • Cleaner architecture

2 Statelessness (The Backbone of Scalability)

Every request must be completely self-contained.

  • The server stores no client session
  • Each request includes:
    • Authentication
    • Authorization
    • Context
  • No memory of previous requests
  • The server treats every request as brand new

This enables:

  • Horizontal scaling
  • Easy load balancing
  • Easier caching
  • Simpler failover

Bad:

“Server remembers your session”

Good:

“Every request is self-contained”

3 Cacheability

Responses must explicitly define whether they can be cached.

Common HTTP headers:

  • Cache-Control
  • ETag
  • Last-Modified

What caching gives us:

  • Massive reduction in server load
  • Faster responses
  • CDN integration

Incorrect caching breaks correctness. Correct caching saves millions of dollars at scale.

4 Uniform Interface (The Heart of REST)

This is the most important REST constraint.

It consists of four sub-rules:

(a) Resource Identification via URIs

Each resource has a unique URL

/users
/orders
/products

A URI identifies a thing, not an action.

(b) Manipulation via Standard HTTP Methods

We use:

GET -> Read
POST -> Create
PUT -> Replace
PATCH -> Modify
DELETE -> Remove

No custom verbs. No RPC-style endpoints.

(c) Self-Descriptive Messages

Every request and response must fully describe:

  • What the data is
  • How to parse it
  • How to cache it
  • What authentication applies

(d) Hypermedia (HATEOAS)

The server tells the client:

  • What can be done next
  • What actions are valid
  • What transitions are allowed

This turns APIs into discoverable systems, not rigid contracts.

5 Layered System

A REST client must not know whether it is talking to:

  1. A load balancer
  2. A CDN
  3. A reverse proxy
  4. A cache
  5. A firewall
  6. The actual application server

This enables:

  1. Security layers
  2. CDN layers
  3. API gateways
  4. Zero-trust networks

6 Code-on-Demand (Optional)

The server can send executable code to the client (usually JavaScript)

This is optional and rarely used today in APIs.

Resources – The Heart of REST

Everything in REST is a resource. A REST API is built around resources.

A resource is:

  • A real-world entity
  • A concept with identity
  • A thing that can change state

Examples:

  • Users
  • Products
  • Orders
  • Payments
  • Books
  • Tickers

A REST API is simply a network of linked resources that evolve through state transitions.

Professional REST API Design Process

Let's now design a REST API the correct professional way.

Step 1 – Identify the Resources

Ask these two questions:

  • What real-world things exist in the system?
  • What data do we permanently store?

Examples of an E-commerce system:

  • user
  • product
  • category
  • cart
  • order
  • review
  • payment

These are all candidates for resources.

Step 2 – Convert Entities Into REST Resources

Follow this rule:

Objects -> Nouns -> Plural -> Resources

Example:

ObjectResource
User/users
Product/products
Order/orders
Payment/payments

Never user verbs.

Bad:

/getUser
/createOrder
/cancelPayment

Good:

/users
/orders
/payments

Use plural names:

users
products
categories
carts
orders
reviews
payments

Step 3 – Identify Resource Relationships

REST is not flat. Resources represent real-world relationships.

Types of relationships:

1 One-to-Many Relationships

  • A user has many orders
  • A book has many reviews

REST representation:

GET /users/{id}/orders
GET /books/{id}/reviews

2 Many-to-Many Relationships

  • Products belong to multiple categories
  • Books has many authors

REST Representation:

GET /products/{id}/categories
GET /categories/{id}/products

3 One-to-One Relationships

  • A user has one profile
  • An order has one payment

REST Representation:

GET /users/{id}/profile
GET /orders/{id}/payment

Step 4 – Avoid Over-Nesting

Bad:

/users/10/orders/5/items/2/payments/99/address/3

Good:

/items/2/payments
/payments/99/address

Rule: Never go more than 2 levels deep.

Step 5 – Define Resource Attributes (Fields)

Resources return representations, not database rows.

Each resource has fields:

Example: User Resource

id
name
email
phone
created_at
updated_at

Example: Order Resource

id
user_id
order_items
total_amount
status
created_at

This becomes your JSON structure.

Rules:

  • Field names must never change
  • You may add new fields
  • Never remove or rename existing fields
  • Hide internal DB fields
  • Optimize for clients, not your database

Step 6: Assign HTTP Methods to Resources

Now we assign correct methods to each resource based on operation semantics.

Users Resource:

Create user:

POST /users

Get all users:

GET /users

Get one user:

GET /users/{userId}

Update full user:

PUT /users/{userId}

Update partial user:

PATCH /users/{userId}

Delete user:

DELETE /users/{userId}

Orders Resource:

Create order:

POST /orders

Get all orders

GET /orders

Get one order:

GET /orders/{orderId}

Update order status:

PATCH /orders/{orderId}

Cancel order:

DELETE /orders/{orderId}

User -> Orders Relations:

Create order for user:
POST /users/{userId}/orders

Get all orders of a user:
GET /users/{userId}/orders

Order -> Items Relation:

Add item:
POST /orders/{orderId}/items

Update item quantity:
PATCH /orders/{orderId}/items/{itemId}

Remove item:
DELETE /orders/{orderId}/items/{itemId}

HTTP Semantics: Safe, Unsafe & Idempotent Methods

Safe Methods (read-only):

  • GET
  • HEAD

Unsafe Methods (modify data):

  • POST
  • PUT
  • PATCH
  • DELETE

Idempotent Methods (Same result if called many times)

  • GET
  • PUT
  • DELETE

Non-Idempotent Methods:

  • POST

This is why:

  • Payments must user idempotency keys with POST
  • Update operations prefer PUT/PATCH

Pagination, Filtering & Sorting

Pagination, filtering, and sorting are not optional features in real-world REST APIs. They are mandatory once your system moves beyond a few records.

If your API:

  • Returns thousands of records in one response
  • Has no filtering
  • Has no consistent sorting

Then it is not production-ready, no matter how correct your resources are.

Pagination:

Why Pagination Is Mandatory

Without pagination, your API suffers from:

  • Memory exhaustion
  • Slow query execution
  • Network timeouts
  • Poor mobile performance
  • CDN inefficiency
  • Database connection starvation

Example of a Dangerous API

GET /users

If this returns 5 million users, your system is already broken.

Every list endpoint in production must be paginated.

Types of Pagination:

There are three major pagination strategies used in real systems:

TypeUsed ByBest For
Offset-basedTraditional systemsAdmin dashboards
Cursor-basedFacebook, Instagram, StripeInfinite scroll
Keyset-basedHigh-performance data APIsLarge distributed DBs

Offset-based Pagination (Beginner Friendly)

Request:

GET /users?page=3&limit=20

Meaning:

  • Skip first 40 records
  • Return next 20 records

SQL Equivalent

LIMIT 20 OFFSET 40

Response Example:

{
	"data": [...],
	"pagination": {
		"page": 3,
		"limit": 20,
		"totalPages": 50,
		"totalRecords": 1000
	}
}

Pros:

  • Simple to implement
  • Easy to understand
  • Works well for small datasets

Cons:

  • Becomes slow for large datasets
  • Records shift when insert happen
  • Can cause duplicate/missing records
  • High DB cost at scale

Use only for:

  • Admin dashboards
  • Internal tools
  • Small datasets

Cursor-Based Pagination (Used by Big Tech)

Request

GET /orders?limit=20&cursor=eyJpZCI6IjEwMDAifQ==

Idea

Instead of saying:

"Give me page 5"

You say:

"Give me records after this one"

Response:

{
	"data": [...],
	"nextCursor": "eyJpZCI6IjEwMjAifQ==
}

The client uses nextCursor for the next request.

Pros:

  • Extremely fast
  • No record shifting
  • Perfect for infinite scroll
  • Works with real-time inserts

Cons:

  • Harder to debug
  • Cannot jump to arbitrary page number
  • More complex backend logic

Used by:

  • Facebook feed
  • Instagram timeline
  • Stripe transactions
  • Twitter timeline

Keyset Pagination (Database-Optimized)

GET /orders?limit=20&afterId=10500

Equivalent SQL:

WHERE id > 10500
ORDER BY id ASC
LIMIT 20

Pros:

  • Fastest possible pagination
  • DB index friendly
  • No shifting
  • Works on massive tables

Cons:

  • Requires ordered unique field
  • No random page jumps

Best for:

  • Orders
  • Transactions
  • Logs
  • Time-series data

Filtering:

Filtering means reducing result set based on conditions.

Basic Filtering

GET /users?status=active
GET /orders?state=delivered

Always treat query params as filters

Never use path params for dynamic filters

Multiple Filters

GET /users?status=active&role=admin&country=india

Backend interpretation:

status = active AND role = admin AND country = india

Range Filtering (Dates, Numbers, Prices)

GET /orders?amountMin=500&amountMax=5000
GET /orders?createdAfter=2024-01-01

Boolean Filtering

GET /products?inStock=true
GET /users?verified=false

Text & Search Filtering

GET /products?search=iphone
GET /users?nameContains=man

Sorting:

Sorting defines result order.

Basic Sorting

GET /users?sort=createdAt

Default:

ASC (ascending)

Descending Order

GET /users?sort=createdAt:desc

OR

GET /users?sort=-createdAt

Multi-Field Sorting

GET /users?sort=role,-createdAt

Meaning:

  1. Sort by role
  2. Then by createdAt descending

Sorting + Pagination Together

GET /orders?sort=createdAt:desc&page=1&limit=50

Always:

  • Sort first
  • Then paginate

Never paginate unsorted datasets

Combining Pagination + Filtering + Sorting

A fully professional query look like:

GET /orders?
status=delivered&
amountMin=500&
amountMax=5000&
sort=createdAt:desc&
limit=20&
cursor=eyJpZCI6IjEwMDAifQ==

This single endpoint supports:

  • Pagination
  • Filtering
  • Sorting
  • Infinite scrolling
  • High performance

API Versioning

API Versioning is not a feature.

It is a long-term survival strategy for your platform.

If you deploy APIs without a versioning strategy:

  • You will break clients
  • You will block future features
  • You will accumulate technical debt
  • You will eventually face an unfixable migration crisis

What Is API Versioning?

API Versioning is the controlled evolution of your API contract over time without breaking existing clients.

It allows you to:

  • Change request/response formats
  • Modify behaviour
  • Add or deprecate features
  • Rewrite internal systems

    without forcing all users to upgrade immediately

Without versioning:

Your API becomes frozen on Day 1.

Why API Versioning Is Absolutely Mandatory

Any real system will eventually need to:

  • Rename fields
  • Change data structures
  • Split or merge resources
  • Improve validation rules
  • Optimize performance
  • Add security constraints

All of these are breaking changes.

If you deploy breaking changes without versioning:

  • Mobile apps crash
  • Partner integration fail
  • Web clients break in production
  • You lose trust permanently

Versioning = Controlled Change

What Is a “Breaking Change”?

A change is breaking if it forces clients to change their code.

Examples of Breaking Changes

  • Renaming a field
    • totalAmount -> amount
  • Removing a field
  • Changing data type
    • price: string -> number
  • Making optional fields mandatory
  • Changing authentication rules
  • Modifying business behaviour
  • Changing pagination format

Non-Breaking Changes

  • Adding new optional fields
  • Adding new endpoints
  • Adding new filters
  • Adding new response metadata

Only breaking changes require a new API version.

The 4 Real API Versioning Strategies

There are only four valid strategies used in production.

StrategyUsed ByIndustry Adoption
URI VersioningTwitter, Shopify, StripeVery High
Header VersioningGitHub (partial), MicrosoftHigh
Media Type VersioningNetflix, some hypermedia APIsMedium
Query Parameter VersioningLegacy systemsLow

1 URI Versioning (Most Common & Most Practical)

Format

/v1/users
/v2/users

Examples:

GET /v1/users/123
GET /v2/users/123

Advantages:

  • Easier to understand
  • Human readable
  • CDN friendly
  • Easy routing
  • Works with all clients
  • Excelled for monitoring & logging

Disadvantages:

  • Breaks the “pure” REST URI philosophy (minor issue)
  • Requires duplicate routes

This is the best default strategy for 90% of systems.

2 Header Versioning (Enterprise-Grade)

Format

GET /users
Accept-Version: v2

OR:

Accept: application/vnd.myapp.v2+json

Advantages:

  • Clean URIs
  • Strict RESTful purity
  • No URI duplication

Disadvantages:

  • Hard to debug
  • Poor browser support
  • CDN caching complications
  • Developers forget headers

Best for:

  • Internal microservices
  • SDK-driven platforms

3 Media Type Versioning (Advanced REST / HATEOAS)

Accept: application/vnd.shop.v2+json

Advantages:

  • Strong content negotiation
  • Multiple versions via same URI
  • Hypermedia friendly

Disadvantages

  • Very complex
  • Few teams implement it correctly
  • Tooling support is weak

4 Query Parameter Versioning (NOT Recommended)

GET /users?version=2

Why this is bad:

  • Breaks caching
  • Breaks REST semantics
  • Poor debugging
  • Easy to misuse

Only acceptable for:

  • Legacy systems
  • Temporary migrations
Buy Me A Coffee

Leave a comment

Your email address will not be published. Required fields are marked *