Build1 distinct publisher3 min readPublished
The Worker in this FastAPI pattern authorizes requests itself from a KV copy of the session, so a role change lands only when the origin's invalidation write does, and the 3600-second cap is the only other bound.
The Engineer · Build desk

Compiled by The EngineerSomething wrong?How this is made
Trace a role downgrade through this design. FastAPI writes the new permission set to Postgres. The edge still holds the old array under `session:<token>`, and the worker's check reads that array rather than the row [3][4]. Until an invalidation write lands, `data:write` is answered from a copy.
The post does name that step: logout or a permission change triggers invalidation from the origin [5]. The excerpt breaks off mid-route and never shows the endpoint [11], and the key shape is why that matters. Keys are per token [4], and the cached payload carries `user_id`, `tenant_id`, `permissions`, `email` and `expires_at` [6], with nothing mapping a user back to the token keys issued to them. Revoking one person with four live sessions is four deletes against keys the origin has to have remembered [5].
The cap does the safety work. `Math.min(ttl, 3600)` means an entry nobody manages to invalidate stops being servable within an hour of the write [4][1]. The worker also rejects any cached session whose `expires_at` has already passed, rather than trusting KV to have expired it [7]. That is careful work, and it narrows the exposure to one case: credentials still inside their validity window that should no longer carry the permissions attached to them.
Then the revalidation branch. When less than five minutes remain, the worker re-puts the object it already had, with `expirationTtl: cached.ttl` and an empty catch [8]. `SessionPayload` declares no `ttl`, so that argument is undefined [6][2]. More to the point, the branch never fetches the origin, so nothing on that path revalidates anything. The name describes an intention.
Now the number, which is a claim about the author's traffic in CitizenApp [10] rather than yours. Take the 60-80% at face value and back out the workload it implies. With one origin validation per token per hour as the ceiling [4], hit rate is roughly 1 - 1/N for N requests per token per hour, so 60% needs N of 2.5 and 80% needs N of 5 [3]. The same post describes session lookups running 200 or more times per second per user [2]. At that rate the hit rate rounds to 100 and the residual load is a rounding error, well under the 20-40% the quoted range leaves on Postgres [4]. Two hundred lookups per second per user is a figure I would want the trace for. The two numbers count different denominators, so neither one sizes your origin.
For the range to transfer you need repeat reads inside the TTL from the same token, sessions that outlive the TTL, and auth reads that dominate auth writes. Bursty logins from short-lived tokens give you the opposite: KV absorbs the tail, and the origin still eats the first read of every session, since misses fall through and write back [9].
In my context this ships, with the TTL set well below the 3600 ceiling and the deny path for write routes kept at the origin. What KV buys is a read cache at 200-plus locations for one line of binding config [9], and the operational relief the post claims against running Redis is real enough [1]. The price is that the TTL becomes a revocation SLA, and it should be picked by whoever owns offboarding rather than whoever owns p99.
Ranked by verification strength, evidence, and original report placement.
The published worker performs authorization at the edge on the cached payload: it returns 403 when session.tenant_id does not equal the route's tenant_id, and 403 when session.permissions does not include 'data:write', before proxying the request to the origin.
The SessionPayload interface declares user_id, tenant_id, permissions (string array), email and expires_at.
The worker middleware returns 401 when no session is returned or when session.expires_at is earlier than Date.now(), and 401 when the authorization header carries no token.
The described request flow states that on logout or permission change, FastAPI invalidates the KV entry.
The published excerpt ends mid-way through the proxy call of the example route handler and contains no origin-side invalidation implementation.
getSessionFromKV reads and writes the key `session:${token}` and writes with expirationTtl: Math.min(ttl, 3600), where ttl is derived from session.expires_at minus the current time.
Distinct publishers with included, body-backed reporting in this cluster.
dev.to
1 article · August 29, 2026
Follow any of these and your For You feed starts watching them — no settings page required.
build
A RAG stack lived seven hours before a hosted embedding endpoint returned 4041 distinct publisher
build
Per-tenant Claude clients belong in the dependency graph, not in middleware1 distinct publisher
build
Three services you can delete: queue, cache and search in one Postgres1 distinct publisher
build
The /userinfo fallback that quietly made Auth0 a hard dependency on every request1 distinct publisher
Evidence-backed comparisons of source perspectives and observed adoption signals. Read the methodology
Which Builder, Operator, and Investor concerns the observed source mix emphasized—not a truth score.
Evidence, demonstrated adoption, hype gap, incentives, and confidence are assessed independently, each on its own current evidence. How these are measured.
Code verifiable, numbers not
What can be checked here can be checked precisely, because the author pasted the worker: the 3600-second write cap, the tenant and permission comparisons, the fire-and-forget rewrite are all readable line by line. The two figures doing the persuading — 60–80% fewer auth queries and 200-plus lookups per second per user — arrive with no workload, no instrumentation and no second party, and the FastAPI half stops mid-query before the invalidation the design depends on ever appears.
One self-reported install
The entire adoption record is a developer saying he runs this in CitizenApp. No other deployment, no traffic figures, no third-party write-up of the pattern, and the near-outage he cites as experience is never described.
Overstated on both ends
"Zero operational overhead" is a strong phrase to place beside a revalidation path that passes expirationTtl: cached.ttl on an object with no ttl field, and an invalidation step that exists only as a bullet. The Redis comparison is scored on chores avoided while the cost that actually matters — a permission check that can trail the Postgres row by up to an hour — is left for the reader to derive. The per-user request rate cuts against the post's own hit-rate math, too.
Self-promotional, not commercial
The pattern is the author's, the product it runs in is the author's, and the alternative it beats is named. dev.to pays in attention rather than money, and nothing in the post suggests Cloudflare's involvement or discloses any pricing — so the pull is reputational, which is enough to explain a 60–80% figure that is stated rather than measured.
Firm on mechanics, thin on behaviour
We can read the code, so the hour-long staleness bound, the undefined TTL and the per-token revocation fan-out are solid conclusions. How any of it behaves under real traffic rests on a single unverified account, and the invalidation half is assessed on its absence rather than its content because the text stops before it.