PolyesterServerClient is the core client plus session awareness. It parses the cookies your
browser client sets, uses the bearer token for authenticated calls, and exposes what it knows
about the user for rendering.
One client per request
Create the client from the incoming request (or your framework's cookie API) in the handler:
import {
createPolyesterServerClientFromRequest,
POLYESTER_DEVNET_ENVIRONMENT,
} from "@polyester/sdk";
export async function handle(request: Request) {
const client = createPolyesterServerClientFromRequest({
environment: POLYESTER_DEVNET_ENVIRONMENT,
request,
});
if (client.hasUsableBearerToken) {
const balances = await client.balances.list();
// ...
}
}Clients are cheap (services, realtime, and catalog are lazy), so per-request creation is the intended pattern.
With a framework cookie API instead of a Request, await an asynchronous store before passing it
to the SDK. For example, current Next.js versions:
import { cookies } from "next/headers";
import {
createPolyesterServerClientFromCookies,
POLYESTER_DEVNET_ENVIRONMENT,
} from "@polyester/sdk";
const client = createPolyesterServerClientFromCookies({
environment: POLYESTER_DEVNET_ENVIRONMENT,
cookies: await cookies(),
cookieLocation: { hostname: "localhost", port: "3000", protocol: "http:" },
});The factory accepts a Request, a plain { [name]: value } record, or a synchronous .get(name) store that returns a string or { value: string }. A Request supplies its own URL. For a framework
cookie store on loopback, .localhost, or an RFC1918 private IPv4 host, provide cookieLocation for the page request. The SDK scopes local bearer and display-session cookie names by port, including
default HTTP (80) and HTTPS (443) ports, so it needs that location to select the right cookies.
Public hosts keep stable names.
Display session vs authenticated session
Two cookies. The SDK keeps them apart on purpose:
| Cookie | Contents | Trust level |
|---|---|---|
| Session cookie | Who the user appears to be: username, active account, wallets | Unsigned. Display only. |
| Auth token cookie | The JWT bearer token | Proof, but verify before trusting |
client.hasDisplaySession; // can I render a username immediately?
client.session; // the parsed ServerSessionSnapshot
client.hasBearerToken; // is a token present?
client.hasUsableBearerToken; // ...and not expired?
const me = await client.verifySession(); // backend-verified identity, or nullUse the display session for instant, non-sensitive rendering (avatar, username). Call verifySession() before anything that must be correct. It returns null only when credentials are
missing or rejected as unauthenticated; transport and backend failures throw, so an outage is not
mistaken for a logout. Data requests are still authorized by the backend on every call.
A missing or malformed display cookie does not discard a valid bearer cookie. An explicitly different-environment display cookie is rejected as a whole, so its snapshot and bearer are not used for the requested environment.
ServerSessionSnapshot) on purpose. Authorization belongs to the backend, which
validates the bearer token on each request.Subaccount defaulting
By default, server clients scope calls to the main account. If your account switcher should carry over to SSR, opt in:
import { createPolyesterServerClientFromRequest } from "@polyester/sdk";
const client = createPolyesterServerClientFromRequest({
environment,
request,
useDisplaySessionActiveAccountAsDefault: true,
});That treats the display session's active account as caller intent for calls that omit account.
The backend still enforces access.
Hydrating the browser
Two handoffs keep client startup quiet.
Auth state: render without a logged-out flash, then restore:
import { PolyesterBrowserClient } from "@polyester/sdk";
const client = new PolyesterBrowserClient({ environment });
client.auth.hydrateAuthState({
mainAccountId,
username,
activeAccountId,
});
await client.auth.restoreSession();Catalog snapshot: skip the reference-data fetch by baking the server's catalog into the page:
import { PolyesterBrowserClient } from "@polyester/sdk";
// server
await serverClient.catalog.ensureReady();
const snapshot = serverClient.catalog.snapshot();
// browser
const client = new PolyesterBrowserClient({
environment,
catalogSnapshot: snapshot,
});Lower-level helpers
@polyester/sdk/server-session exposes the primitives without a client: parseSessionCookie(cookies, environment, options?), emptyServerSessionSnapshot(), and isJwtValid(token). Read a bearer token through the parsed snapshot:
import { parseSessionCookie } from "@polyester/sdk/server-session";
const { bearerToken } = parseSessionCookie(cookies, environment, {
cookieLocation: new URL(request.url),
});Use the incoming request URL for cookieLocation when reading a framework store or cookie record.
A Request source supplies its own location. Pass tokenCookieName in the options when browser
storage uses a custom bearer-cookie base name.
The root export has the cookie-name constants POLYESTER_AUTH_TOKEN_COOKIE_NAME and POLYESTER_SESSION_COOKIE_NAME.
isJwtValid accepts unknown and returns false for missing, malformed, or expired values.
Edge runtimes
The SDK runs on edge/serverless (Cloudflare Workers, Vercel Edge): fetch-based, ESM-only, heavy deps deferred. Realtime subscriptions work from any long-lived server process, but not from an SSR render pass. When rendering a page, fetch a snapshot server-side and subscribe after hydration. See Streaming.