Skip to content

Bearer-token API clients don't get D1 read-your-writes (sessionUser only set from astro-session cookie) #1046

Description

@xpressmike

Summary

EmDash's Cloudflare D1 session adapter (@emdash-cms/cloudflare/src/db/d1.ts) routes reads to either first-primary, the bookmark from a prior write, or first-unconstrained based on opts.isAuthenticated. In the Astro middleware (src/astro/middleware.ts:282), isAuthenticated is set to !!sessionUser, and sessionUser is read only from the astro-session cookie:

const hasSessionCookie = cookies.get("astro-session") !== undefined;
const sessionUser = context.isPrerendered || !hasSessionCookie
    ? null
    : await context.session?.get("user");

As a result, requests authenticated via Authorization: Bearer <PAT> (issued by _emdash/api/auth or directly to D1) are treated as anonymous by the D1 adapter:

  • Writes still hit primary (correct — isWrite=true forces first-primary).
  • Reads go to first-unconstrained (any replica), and the bookmark cookie is never persisted because commit() early-returns when !opts.isAuthenticated.

This means: a Bearer-authenticated client can POST data, get 200 back, and a subsequent GET (even seconds later) returns stale results from a replica that hasn't caught up.

Repro

A Python script using Authorization: Bearer ec_pat_… to call POST /content/posts/{slug}/terms/{tax} and then immediately GET the same path: POST returns the term in the response body, GET returns empty.

Workaround we adopted

Switched astro.config.mjs d1({ binding: "DB", session: "primary-first" }). This forces every read (anonymous or not) to start at primary — kills replica reads entirely. Tolerable on a small site, but defeats the purpose of read replicas on larger ones.

Suggested fix

Populate sessionUser (or a sibling flag like isApiAuthenticated) from the resolved user when Bearer auth succeeded. The auth resolver already runs upstream of createRequestScopedDb; passing user rather than re-reading the cookie would let API clients benefit from primary-first / bookmark-resumed reads.

Tested against emdash 0.9.0. Happy to discuss approach if helpful.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions