twilightMiddleware
The request middleware every theme lists first in app/start.ts; it opens the private, per-request context the rest of the engine reads.
import { twilightMiddleware } from '@salla.sa/twilight-theme-engine/tanstack';In plain words
One server serves every shopper of every store that points at your theme, often at the same moment. Each request needs its own notes: is this visitor signed in, which store did the address name, which host did it arrive on.
A middleware is a function that runs on the server for every request, before any page code. twilightMiddleware() reads the request's cookies and address, writes those notes into a context that belongs to this request only, and lets the request continue. Everything after it (the root route, loaders, head functions, the API client) reads that context.
You add it once, first in the requestMiddleware list of app/start.ts, and never call it anywhere else.
Signature
function twilightMiddleware(options?: {
cookieName?: string; // default 'token': the cookie holding the customer's auth token
}): AnyRequestMiddleware // from @tanstack/react-startExample
import { createStart } from '@tanstack/react-start';
import { twilightMiddleware, earlyHintsMiddleware } from '@salla.sa/twilight-theme-engine/tanstack';
export const startInstance = createStart(() => ({
// First: every middleware after it can read and write the request context.
requestMiddleware: [twilightMiddleware(), earlyHintsMiddleware()],
}));
How it behaves
It runs the rest of the request inside
runWithTwilightContext({ authToken, scope, storeId, storeBase, versionId, requestHost }, …), so every other field starts from its default for each request, and hands{ authToken }to TanStack as middleware context. It fetches nothing: the store settings, the locale and the translations are loaded afterwards by the root route thatcreateTwilightRootRoute()sets up.What it reads: the
tokencookie (the auth token), thescopecookie (the selected market or branch, as JSON), and the host from the request URL, falling back to theHostheader.storeIdcomes from?storeId=or thesalla_sidcookie,storeBasefrom the first path segment, andversionIdfrom?versionId=or thesalla_vidcookie. They are read only onpreview.salla.designand loopback hosts such as localhost. On any other host the hostname is the store, and all three staynull.The context lives in
AsyncLocalStoragefromnode:async_hooks, which is why the server runtime needs thenodejs_compatcompatibility flag (the reference theme and this playground both set it). If that import fails, the engine silently falls back to one module-level store shared by every request (src/twilight/context.ts).Middleware of your own that reads or writes the context goes after it in the list. The playground adds one,
pinPlaygroundStorein packages/playground/app/start.ts: whengetTwilightContext()shows nostoreIdand nostoreBase, it callsupdateTwilightContext({ storeId: '1510890315' }).cookieNamechanges only the cookie this middleware reads. The root route falls back to thetokencookie, which is also the oneTwilightProviderwrites after a sign-in, so leave the default.
Gotchas
Without it nothing renders on the server:
createRouter()and the root route both write into the context, and outside a request context that throws[Twilight] No request context. Wrap with runWithTwilightContext().Keep it inapp/start.ts.Never copy the playground's store pin into a theme. The playground is served from an address that is not a store, so it names Salla's demo store itself. A theme serves many stores, and on a merchant's own domain the hostname is the store: a pinned id would show the same shop on every domain that points at the theme.
docs/getting-started/04-theme-anatomy.md says it resolves the store, locale, direction and theme settings. It resolves only what the request names; the locale and settings come from the root route, which runs after it.
Related
Reads the engine's context outside React: store settings, language, location, route id, auth token and the data cache.
updateTwilightContextWrites values into the current twilight context: the request's own on the server, the page's single context in the browser.
earlyHintsMiddlewareOptional request middleware that adds preload Link headers for the SDK, fonts and theme CSS to every HTML response.
resolveStoreIdentifierDecides which store the current request is about: the explicit value, the preview request, the URL, the server render or the host.