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

# Storefront SDK

> Typed TypeScript helpers for every Crave Storefront REST endpoint.

The Storefront SDK (`@craveup/storefront-sdk`) gives you a thin, type-safe wrapper around the Crave Storefront API. It handles headers, timeouts, query strings, and customer auth tokens so you can focus on product workflows instead of request plumbing.

## Installation

```bash theme={null}
pnpm add @craveup/storefront-sdk
```

The CLI templates already include this dependency. Install it manually if you are integrating the SDK into an existing project.

## Creating a client

```ts theme={null}
// lib/storefront-client.ts
import { createStorefrontClient } from '@craveup/storefront-sdk';

export const storefrontClient = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_CRAVEUP_API_KEY,  // location-scoped API key
  getAuthToken: () => localStorage.getItem('craveup-auth-token'), // optional customer JWT
  defaultTimeoutMs: 15_000,                         // optional (defaults to 10s)
});
```

### Client options

| Option             | Required | Description                                                                                                        |
| ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `baseUrl`          | optional | Override the API base URL. When omitted, the SDK infers dev vs prod from the API key.                              |
| `apiKey`           | ⚠️\*     | Location-scoped API key. Required unless you inject it via headers on every call.                                  |
| `getAuthToken`     | optional | Function that returns a customer JWT for authenticated flows (OTP login). Can resolve a promise for async storage. |
| `fetch`            | optional | Supply a custom `fetch` implementation (e.g. `cross-fetch`) when running in Node.js.                               |
| `defaultTimeoutMs` | optional | Global request timeout in milliseconds (defaults to 10 000).                                                       |

\*If you omit `apiKey`, the client looks for `NEXT_PUBLIC_CRAVEUP_API_KEY`. You can also pass the header per-request via `headers: { 'X-API-Key': '...' }`.

### Using in Node.js / SSR environments

```ts theme={null}
import fetch from 'cross-fetch';
import { createStorefrontClient } from '@craveup/storefront-sdk';

export const storefrontClient = createStorefrontClient({
  apiKey: process.env.NEXT_PUBLIC_CRAVEUP_API_KEY,
  baseUrl: process.env.CRAVEUP_API_URL, // optional override
  fetch, // polyfill for Node runtimes
});
```

## Request configuration

Every SDK method accepts an optional `RequestConfig` (`RequestOptions` under the hood):

```ts theme={null}
await storefrontClient.cart.get(locationId, cartId, {
  query: { include: 'items' },      // appended to the URL
  headers: { 'X-Session-Id': sessionId },
  timeoutMs: 5_000,                 // per-call timeout
  skipAuth: true,                   // ignore apiKey + auth token for public endpoints
  credentials: 'include',           // passthrough to fetch
});
```

## Available modules

`createStorefrontClient` exposes a set of grouped helpers. The most common ones are shown below; all public types are re-exported from the package.

### Merchant

```ts theme={null}
const merchant = await storefrontClient.merchant.getBySlug('downtown-pizza');
merchant.locations.forEach((location) => console.log(location.restaurantDisplayName));
```

### Locations & menus

```ts theme={null}
// Basic location metadata
const location = await storefrontClient.locations.getById(locationId);

// Operating hours
const operatingHours = await storefrontClient.locations.getOrderTimes(locationId);

// Tip configuration
const gratuity = await storefrontClient.locations.getGratuity(locationId);
```

### Ordering sessions

Use ordering sessions to bootstrap a cart and persist metadata:

```ts theme={null}
const session = await storefrontClient.orderingSessions.start(locationId, {
  marketplaceId: locationId,
  returnUrl: 'https://my-storefront.com/checkout/success',
});

console.log(session.cartId); // use this cart ID for subsequent calls
```

### Cart lifecycle

```ts theme={null}
const cart = await storefrontClient.cart.get(locationId, cartId);

// Add an item
await storefrontClient.cart.addItem(locationId, cart.id, {
  productId: 'prod_margherita',
  quantity: 2,
  selections: [],                 // modifier selections (see SelectedModifierTypes)
  itemUnavailableAction: 'remove_item',
});

// Update customer details prior to checkout
await storefrontClient.cart.validateAndUpdateCustomer(locationId, cart.id, {
  customerName: 'Alex Johnson',
  emailAddress: 'alex@example.com',
});

// Schedule the order
await storefrontClient.cart.updateOrderTime(locationId, cart.id, {
  pickupType: 'LATER',
  orderDate: '2024-11-12',
  orderTime: '18:30',
});

// Configure fulfillment
await storefrontClient.cart.setDelivery(locationId, cart.id, {
  fulfilmentMethod: 'delivery',
  address: '123 Main Street',
  lat: 37.7749,
  lng: -122.4194,
});
```

Other helpers you can call on `cart`:

* `updateGratuity`
* `setTable`
* `setRoom`
* `updateItemQuantity`
* `delete` (clears the cart)

Need recommended items? Call the `/carts/{cartId}/products` endpoint with the [`http` helper](#direct-http-access).

### Discounts

```ts theme={null}
await storefrontClient.discounts.apply(locationId, {
  code: 'SAVE10',
  cartId,
});

await storefrontClient.discounts.remove(locationId, cartId);
```

### Analytics events

```ts theme={null}
await storefrontClient.analyticsEvents.track(locationId, {
  cartId,
  eventType: 'CART_VIEW',
  metadata: { total: cart.orderTotalWithServiceFee, currency: cart.currency },
});
```

## Direct HTTP access

Need an endpoint the SDK does not wrap yet? Use the underlying `http` helper—it exposes `get`, `post`, `put`, `patch`, and `delete` with the same `RequestConfig` options.

```ts theme={null}
const suggestions = await storefrontClient.http.get(
  `/api/v1/locations/${locationId}/carts/${cartId}/products`,
  { query: { limit: 5 } }
);
```

## Typed responses & imports

All DTOs used by the API are exported so you can declare explicit return types:

```ts theme={null}
import type { StorefrontCart, MerchantApiResponse } from '@craveup/storefront-sdk';

const merchant: MerchantApiResponse = await storefrontClient.merchant.getBySlug(slug);
const cart: StorefrontCart = await storefrontClient.cart.get(locationId, merchant.locations[0].id);
```

Refer to `packages/storefront-sdk/src/types` in the monorepo for the complete list.

## Error handling

The SDK throws an `ApiError` whenever the API responds with a non-2xx status:

```ts theme={null}
import { ApiError } from '@craveup/storefront-sdk';

try {
  await storefrontClient.discounts.apply(locationId, { code: 'INVALID', cartId });
} catch (error) {
  if (error instanceof ApiError) {
    console.error('Failed request', {
      status: error.status,
      url: error.url,
      body: error.body,
    });
  }
}
```

The thrown error includes `status`, `statusText`, `url`, and the raw response body (where provided). Handle specific status codes to deliver better UX—for example, show a toast when a discount is no longer valid.

## Working alongside the REST docs

Each SDK method corresponds one-to-one with a REST endpoint documented in the [Storefront API reference](/api-reference/overview/api-overview). Use the SDK for JavaScript/TypeScript apps and the REST examples for other platforms; the payloads and response shapes are identical.

Ready to put it all together? Follow the [Build a Custom Storefront guide](/guides/build-custom-storefront) for an end-to-end walkthrough.
