Home
Backend from First Principles / Module 8 — Request Context

Request Context

AsyncLocalStorage, contextvars, context.Context — sharing state down the call tree.


The Problem Request Context Solves

As a request flows through middleware → handlers → services → database layers, each layer needs information about the request: who's the user, what's the request ID, what's the tenant ID.

Snippet
Without context, you'd have to pass this as function arguments all the way down:
  getUser(userId, requestId, tenantId, currentUser)

That's ugly and tightly coupled. Request context solves this: a request-scoped storage object that any function can read from during the life of one request.


How It Works

Middleware creates a context object at the start of each request. All layers can access it via an AsyncLocalStorage (Node.js), context.Context (Go), or threading.local (Python).

Node.js with AsyncLocalStorage:

JavaScript
const store = new AsyncLocalStorage();

// In middleware:
store.run({ requestId: uuid(), user: null }, () => {
  next(); // all downstream code shares this store
});

// In a service function (no function arguments needed!):
function getCurrentUser() {
  return store.getStore().user;
}

Go passes context.Context explicitly as the first argument to every function — a language idiom. Python uses contextvars.


What Goes in Context

Standard context values:

Rules:
• Context is read-only after creation (treat it as immutable)
• Don't store mutable shared state in context
• Don't pass context values in response bodies
• Keep context small — large objects per request = memory pressure


Source & Credit

The Backend from First Principles series is based on what I learnt from Sriniously's YouTube playlist — a thoughtful, framework-agnostic walk through backend engineering. If this material helped you, please go check the original out: youtube.com/@Sriniously. The notes here are my own restatement for revisiting later.

⁂ Back to all modules