Asher Cohen
Back to posts

REST Is Stateless

That doesn't mean your app is

There's this recurring confusion in backend discussions:

"If REST is about Representational State Transfer, how can it be stateless? Aren't we literally transferring state?"

Let's break it down.

Stateless ≠ No State

Stateless means:

The server doesn't store per-client memory in RAM between requests.

Back in the early web days, we used server-side sessions (in-memory) to track users across requests. That made servers stateful and tightly coupled user flows to a specific machine.

That Broke Things

  • Couldn't scale horizontally (sticky sessions, ugh)
  • Crash a node? Lose user progress
  • Caching and retries were unpredictable

So we learned to externalize state—usually into a DB or cache—and make each request self-contained.

So What Does "State Transfer" Mean?

REST doesn't mean "no state"—it means state lives on the client.

  • The client sends all context needed to process a request
  • The server responds with the current representation of resource state

That's the "transfer" part—the client drives the flow.

Example: Onboarding Wizard

Say you build a 4-step onboarding flow:

POST /onboarding => { flowId: "abc123" }
PUT /onboarding/abc123/step/1 => { personalInfo }
PUT /onboarding/abc123/step/2 => { contractDetails }

✅ The server never remembers your session
✅ It uses flowId to load and persist state
✅ It's stateless, but still supports stateful flows

Why This Matters

Storing session state in server memory locks you in:

  • Hard to autoscale
  • Hard to recover from failures
  • Hard to decouple services

This is why statelessness became a best practice. Not because it's "clean"—but because it's scalable, resilient, and plays well with modern infra.

Gotchas

  • Stateless API ≠ no persistence — Your DB is the server's long-term memory
  • You can break the rule (e.g. multiplayer, WebSockets) — but that requires strong coordination (e.g. distributed state)

Real-World Pattern

You want to persist where users are in a workflow?

Don't track it in RAM. Persist it:

  1. In a backing store (Postgres, Redis, etc.)
  2. Or encoded in a token/URL

Client sends a flowId or token in each request. Server stays stateless—app stays stateful.

The Takeaway

  • Stateless server ≠ stateless app
  • State transfer = client includes the context in every request
  • You get robustness, scaling, and clarity—without giving up complex flows

TL;DR

  1. Stateless: Server holds no in-memory session
  2. State transfer: Client manages context, server processes statelessly
  3. Modern web flows: Use DBs or tokens, pass IDs in every call—and scale without fear