Skip to main content
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

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

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

export const storefrontClient = createStorefrontClient({
  baseUrl: process.env.NEXT_PUBLIC_API_URL!,        // e.g. https://api.craveup.com/api/v1
  apiKey: process.env.NEXT_PUBLIC_API_KEY!,         // location-scoped API key
  getAuthToken: () => localStorage.getItem('craveup-auth-token'), // optional customer JWT
  defaultTimeoutMs: 15_000,                         // optional (defaults to 10s)
});

Client options

OptionRequiredDescription
baseUrlRoot URL for the storefront API. Trailing slashes are trimmed automatically.
apiKey⚠️*Location-scoped API key. Required unless you inject it via headers on every call.
getAuthTokenoptionalFunction that returns a customer JWT for authenticated flows (OTP login). Can resolve a promise for async storage.
fetchoptionalSupply a custom fetch implementation (e.g. cross-fetch) when running in Node.js.
defaultTimeoutMsoptionalGlobal request timeout in milliseconds (defaults to 10 000).
*If you omit apiKey, make sure you provide headers: { 'X-API-Key': '...' } in each request.

Using in Node.js / SSR environments

import fetch from 'cross-fetch';
import { createStorefrontClient } from '@craveup/storefront-sdk';

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

Request configuration

Every SDK method accepts an optional RequestConfig (RequestOptions under the hood):
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

const merchant = await storefrontClient.merchant.getBySlug('downtown-pizza');
merchant.locations.forEach((location) => console.log(location.restaurantDisplayName));

Locations & menus

// 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:
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

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.

Discounts

await storefrontClient.discounts.apply(locationId, {
  code: 'SAVE10',
  cartId,
});

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

Analytics events

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.
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:
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:
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. 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 for an end-to-end walkthrough.
I