Building a Per‑User Session Store with Cloudflare Workers and Durable Objects
Step‑by‑step guide to building a Cloudflare Worker backed by a Durable Object for per‑user session state, including setup, code, local testing, deployment, verification, and rollback.
11 Sept 2025, 11:25 UTC

Desired outcome
Create a Cloudflare Worker that acts as a thin proxy to a Durable Object, allowing each unique identifier (e.g., a user ID) to maintain its own mutable state—such as a session counter—across requests.
Prerequisites
- Node.js version 14 or newer installed.
- Wrangler CLI installed globally (
npm install -g wrangler). - A Cloudflare account with API token permission to edit Workers and Durable Objects.
- Access to a terminal where you can run
wrangler loginto authenticate.
Procedure
1. Initialize a Worker project
Run the following in your workspace directory:
wrangler init durable-session-worker --type=javascript
cd durable-session-worker
This creates a basic Worker skeleton with a src folder and a wrangler.toml manifest.
2. Add the Durable Object class
Replace the contents of src/index.ts (or src/index.js if you kept JavaScript) with the following code:
export class SessionState {
constructor(state: DurableObjectState, env: Env) {
this.state = state;
}
async fetch(request: Request) {
const url = new URL(request.url);
const action = url.pathname.slice(1) || 'get'; // e.g., /inc or /get
// Retrieve current value (default 0)
let value = await this.state.storage.get('count') ?? 0;
if (action === 'inc') {
value += 1;
await this.state.storage.put('count', value);
return new Response(String(value));
}
return new Response(String(value));
}
}
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// The binding name is defined in wrangler.toml as SESSION
const id = env.SESSION.idFromName('default'); // single object for demo
const obj = env.SESSION.get(id);
return obj.fetch(request);
}
};
This Durable Object stores a single integer key count in its private storage and increments it when the path /inc is requested.
3. Bind the Durable Object in wrangler.toml
Add a [durable_objects] section to the manifest:
name = "durable-session-worker"
main = "src/index.js"
compatibility_date = "2024-09-01"
[durable_objects]
binding = "SESSION"
class_name = "SessionState"
The binding name (SESSION) is used in the Worker code to access the object.
4. Test locally with wrangler dev
Start the local development server:
wrangler devIn another terminal, send requests to the local endpoint (default
http://127.0.0.1:8787):# First request – should return 0 curl http://127.0.0.1:8787/get # Increment – should return 1 curl http://127.0.0.1:8787/inc # Get again – should return 1 curl http://127.0.0.1:8787/get # Increment again – should return 2 curl http://127.0.0.1:8787/incVerify that the numeric response increases as expected, confirming that state persists across calls to the same Durable Object.
5. Deploy to the Cloudflare edge
When local testing behaves as expected, publish the Worker:
wrangler publishTake note of the generated URL (e.g.,
https://durable-session-worker..workers.dev). Usecurlor a browser to hit the public endpoint and confirm the same increment behavior.Expected checks
- Local test: successive
/inccalls return monotonically increasing integers. - Deployed test: the public URL reproduces the same increment pattern.
- Cloudflare dashboard: under Workers → Logs, you should see entries indicating Durable Object creation (
new) and subsequent fetches (existing). - No errors in the console or Wrangler output during
wrangler devorwrangler publish.
Recovery / rollback options
Publishing a new version changes the live Worker state. To revert to a known‑good version:
- If you have a previous
wrangler.tomlor source commit, redeploy it:
wrangler publish --env production # assumes you keep env‑specific configs
- Alternatively, use the Cloudflare dashboard to select a prior deployment under Workers → Overview → Deployments and click “Rollback”.
After rollback, repeat the verification steps to ensure the previous behavior (e.g., the counter starting at zero again) is restored.
Limitations and practical considerations
- Durable Objects incur extra compute and storage charges; monitor usage via the Cloudflare billing page, especially if you plan to create many distinct object IDs (e.g., per‑user IDs).
- State is scoped to the object ID. Changing the ID scheme after deployment will make prior data inaccessible unless you implement a migration strategy that copies or transforms the old storage.
- The example uses a single hard‑coded ID (
'default') for simplicity. In a real per‑user scenario you would derive the ID from a request attribute (such as a cookie or JWT sub claim) usingenv.SESSION.idFromName(userId).
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.