> ## Documentation Index
> Fetch the complete documentation index at: https://www.commercengine.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# SSR Utils

> Build custom SSR bindings for frameworks without a first-party wrapper. Cookie-backed server token storage for Nuxt and anything else that renders on the server.

`@commercengine/ssr-utils` gives you the two pieces you need to wire the Storefront SDK into a server-rendered framework that Commerce Engine does not already ship a wrapper for: a cookie adapter and a server-side token store.

<Note>
  Reach for this package **only** when your framework has no first-party wrapper. If you are on Next.js, TanStack Start, Astro, or SvelteKit, use the wrapper instead — it does all of this for you and stays in sync with the SDK.
</Note>

## When to use it

| Framework              | What to use                                                                                        |
| ---------------------- | -------------------------------------------------------------------------------------------------- |
| Next.js                | `@commercengine/storefront/nextjs` — see [Next.js](/docs/sdk/nextjs-integration)                        |
| TanStack Start         | `@commercengine/storefront/tanstack-start` — see [TanStack Start](/docs/sdk/tanstack-start-integration) |
| Astro                  | `@commercengine/storefront/astro` — see [Astro](/docs/sdk/astro-integration)                            |
| SvelteKit              | `@commercengine/storefront/sveltekit` — see [SvelteKit](/docs/sdk/sveltekit-integration)                |
| Nuxt, or anything else | `@commercengine/ssr-utils` — **this page**                                                         |

## Why it exists

The SDK splits public reads from live session flows, and only the second needs cookies:

```
Public render / prerender
  └─ PublicStorefrontSDK
     └─ API key only
     └─ No token bootstrap, refresh, or cookie writes

Live SSR request
  └─ SessionStorefrontSDK
     └─ tokenStorage: ServerTokenStorage(adapter)
     └─ Reads and writes request cookies
     └─ Can bootstrap anonymous auth and refresh tokens
```

`ssr-utils` exists for the live-request case. Public catalog reads never touch it.

## What's in the package

| Export                     | What it does                                                                                 |
| -------------------------- | -------------------------------------------------------------------------------------------- |
| `CookieAdapter`            | Normalizes a framework's cookie API into `{ get, set, delete }`                              |
| `ServerTokenStorage`       | Implements the SDK's `TokenStorage` interface on top of any `CookieAdapter`                  |
| `createCookieAdapter(...)` | Helper for frameworks whose cookie store already exposes compatible `get` / `set` / `delete` |

## Installation

```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
npm install @commercengine/storefront @commercengine/ssr-utils
```

## Build the binding

<Steps>
  <Step title="Write a cookie adapter">
    Map your framework's cookie API onto `{ get, set, delete }`. For Nuxt, that means wrapping `h3`:

    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { deleteCookie, getCookie, setCookie } from "h3";

    const adapter = {
      get: (name: string) => getCookie(event, name) ?? null,
      set: (
        name: string,
        value: string,
        options?: Parameters<typeof setCookie>[3],
      ) => setCookie(event, name, value, options),
      delete: (name: string) => deleteCookie(event, name),
    };
    ```

    If your framework's cookie store already has compatible methods, use `createCookieAdapter(...)` instead of hand-writing this.
  </Step>

  <Step title="Create the token storage">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { ServerTokenStorage } from "@commercengine/ssr-utils";

    const tokenStorage = new ServerTokenStorage(adapter, {
      prefix: "myapp_",
      maxAge: 2592000,
      path: "/",
      sameSite: "lax",
    });
    ```

    <Warning>
      Keep `prefix`, `path`, `secure`, `sameSite`, and encoding identical between your server and client cookie handling. Drift here produces sessions that silently fail to resume.
    </Warning>
  </Step>

  <Step title="Create the session SDK">
    ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
    import { SessionStorefrontSDK } from "@commercengine/storefront";

    const sdk = new SessionStorefrontSDK({
      storeId: process.env.STORE_ID!,
      apiKey: process.env.API_KEY!,
      tokenStorage,
    });
    ```
  </Step>
</Steps>

## Public reads

For build-time and public SSR reads, skip `ServerTokenStorage` entirely — there is no session to carry:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
import { PublicStorefrontSDK } from "@commercengine/storefront";

const publicSdk = new PublicStorefrontSDK({
  storeId: process.env.STORE_ID!,
  apiKey: process.env.API_KEY!,
});

const { data, error } = await publicSdk.catalog.listProducts();
if (error) {
  // handle the failure — do not assume `data` is populated
}
```

## Session bootstrap

Session-aware calls resolve the session themselves. The middleware handles session creation and `user_id` / `customer_id` resolution, so you do **not** need to bootstrap before every call:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const { data, error } = await sdk.cart.getWishlist();
```

If you want the session established eagerly at the top of a request instead, call `ensureAccessToken()` once in a request bootstrap helper:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
await sdk.ensureAccessToken();
const { data, error } = await sdk.cart.getWishlist();
```

<Tip>
  Centralize eager bootstrap in one place. Scattering `ensureAccessToken()` through feature code adds a round trip per call site for no benefit.
</Tip>

## Hosted Checkout token sync

When Hosted Checkout is present, the Storefront SDK stays the session owner. Wire `onTokensUpdated` so the checkout runtime follows along on the client:

```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
const sdk = new SessionStorefrontSDK({
  storeId: process.env.STORE_ID!,
  apiKey: process.env.API_KEY!,
  tokenStorage,
  onTokensUpdated: (accessToken, refreshToken) => {
    if (typeof window !== "undefined") {
      import("@commercengine/checkout").then(({ getCheckout }) => {
        getCheckout().updateTokens(accessToken, refreshToken);
      });
    }
  },
});
```

## Common pitfalls

| Severity | Issue                                                             | Fix                                                |
| -------- | ----------------------------------------------------------------- | -------------------------------------------------- |
| Critical | Using `BrowserTokenStorage` in server code                        | Use `ServerTokenStorage`                           |
| Critical | Using the session SDK for build or prerender pages                | Use `PublicStorefrontSDK` or `storefront.public()` |
| High     | Using `ssr-utils` on Next.js, TanStack Start, Astro, or SvelteKit | Use the first-party wrapper for that framework     |
| High     | Client and server cookie formats drifting                         | Align prefix, path, secure, sameSite, and encoding |
| Medium   | `ensureAccessToken()` scattered across feature code               | Centralize it in one request bootstrap helper      |

## Next steps

<CardGroup cols={2}>
  <Card title="Token Management" icon="key" href="/docs/sdk/token-management">
    How the SDK stores, refreshes, and rotates tokens across environments.
  </Card>

  <Card title="Configuration" icon="gear" href="/docs/sdk/configuration">
    Every SDK configuration option, including storage selection.
  </Card>
</CardGroup>
