Skip to content

Statelessness in REST — Designing Scalable APIs Without Server-Side Session State

DodaTech Updated 2026-06-28 4 min read

In this tutorial, you will learn about Statelessness in REST. We cover key concepts, practical examples, and best practices to help you master this topic.

Statelessness in REST means every request from client to server must contain all information needed to understand and process the request, with no server-side session state between requests.

What You'll Learn

  • What statelessness means in the context of REST APIs
  • How to design stateless authentication and authorization
  • How statelessness enables horizontal scaling

Why It Matters

Statelessness is what makes the web scalable. Any server can handle any request because there is no session state to maintain. If a server goes down, requests are simply routed to another server. This is how Google, Amazon, and Netflix handle billions of requests.

Real-World Use

DodaTech's REST API is completely stateless. Each request includes a JWT token containing the user identity and permissions. Any of the 50 API server instances can handle any request. If an instance crashes during a request, the client retries and another instance completes it.

flowchart LR
    A["Client 1"] --> LB["Load Balancer"]
    B["Client 2"] --> LB
    C["Client 3"] --> LB
    LB --> S1["Server 1\n(stateless)"]
    LB --> S2["Server 2\n(stateless)"]
    LB --> S3["Server 3\n(stateless)"]
    LB --> S4["Server N\n(stateless)"]
    style LB fill:#dbeafe,stroke:#2563eb
    note right of LB: Any server can handle\nany request

Stateless Authentication

import jwt
from flask import Flask, request, jsonify
from functools import wraps

app = Flask(__name__)
SECRET_KEY = "your-secret-key"

def authenticate(f):
    @wraps(f)
    def decorated(*args, **kwargs):
        token = request.headers.get('Authorization', '').replace('Bearer ', '')
        if not token:
            return jsonify({"error": "missing_token"}), 401

        try:
            payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
            request.user = payload
        except jwt.ExpiredSignatureError:
            return jsonify({"error": "token_expired"}), 401
        except jwt.InvalidTokenError:
            return jsonify({"error": "invalid_token"}), 401

        return f(*args, **kwargs)
    return decorated

@app.route('/api/orders')
@authenticate
def get_orders():
    # All state comes from the token in the request
    user_id = request.user['sub']
    page = request.args.get('page', 1)
    orders = get_user_orders(user_id, page)
    return jsonify(orders)

Stateless Pagination

All pagination state must be in the request:

@app.route('/api/users')
def get_users():
    # Pagination state from query parameters
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('per_page', 20, type=int)
    sort = request.args.get('sort', 'created_at')
    order = request.args.get('order', 'desc')

    # Filter state from query parameters
    status = request.args.get('status')
    role = request.args.get('role')

    users, total = query_users(
        page=page,
        per_page=per_page,
        sort=sort,
        order=order,
        filters={'status': status, 'role': role}
    )

    return jsonify({
        "data": users,
        "page": page,
        "per_page": per_page,
        "total": total,
        "_links": {
            "self": {"href": f"/api/users?page={page}&per_page={per_page}"},
            "next": {"href": f"/api/users?page={page+1}&per_page={per_page}"} if page * per_page < total else None,
            "prev": {"href": f"/api/users?page={page-1}&per_page={per_page}"} if page > 1 else None
        }
    })

Common Mistakes

1. Using Server-Side Sessions

Storing session data on the server (like Flask session cookies or Java HttpSession) breaks statelessness. Use JWT tokens or client-managed state instead.

2. Assuming Users Are Authenticated Across Requests

Each request must authenticate independently. Do not assume that because a previous request was authenticated, the next one is too.

3. Storing Shopping Cart State on Server

A shopping cart is client state. Store it in localStorage, a client-side cookie, or in a database keyed by user ID (not session ID).

4. Using Sticky Sessions (Session Affinity)

Sticky sessions bind a client to a specific server, breaking statelessness. If that server goes down, the session is lost.

5. Relying on Server-Side Rate Limit Counters

Rate limit counters must be stored in a shared cache (Redis) not in local server memory, otherwise different servers have different counters.

Practice Questions

  1. What does statelessness mean in REST?
  2. How does statelessness improve scalability?
  3. How do you handle authentication in a stateless API?
  4. What is wrong with sticky sessions?
  5. How should you handle shopping cart state in a stateless API?

Answers

  1. Each request contains all information needed to process it, with no server-side session state. 2. Any server can handle any request, enabling horizontal scaling without session affinity. 3. Use self-contained tokens (JWT) that include all necessary identity and authorization data. 4. They create coupling between client and server, breaking statelessness and fault tolerance. 5. Store cart state on the client or in a database, not in server session memory.

Challenge

Build a stateless e-commerce API where: each request includes a JWT with user ID and role, all pagination and filtering state is in query parameters, order creation is fully self-contained (no multi-step checkout sessions), and Rate Limiting uses Redis (not server memory).

FAQ

What is statelessness in REST?

A constraint requiring each request to contain all context needed for processing, with no server-side session storage.

Does statelessness mean no state at all?

No, it means state is stored on the client or in a shared data store, not in server session memory.

How do I handle authentication in a stateless API?

Use JWT tokens that encode user identity and permissions in the token itself.

What is a sticky session?

A load balancer configuration that sends requests from the same client to the same server. Not RESTful.

Can I use cookies in a stateless API?

Yes, if the cookie contains the state (like a JWT) rather than a session ID that references server-side state.

Mini Project

Build a stateless REST API for a task management system: JWT-based authentication with user context in each request, query-parameter-based filtering and pagination, Redis-backed rate limiting, load-balanced across multiple instances, and a test that verifies any server can handle any request.

What's Next

  • Learn about Caching strategies with Cache-Control and ETag
  • Explore the layered system constraint with proxies and gateways
  • Continue to code-on-demand as an optional REST constraint

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro