Docs
Dashboard
View as Markdown
On this page
  1. The storefront client
  2. Lists
  3. Cart and checkout
  4. A signed-in buyer
  5. On your server
  6. Errors, retries and caching
  7. Next.js
  8. Webhooks
  9. The admin client

JavaScript SDK

@shopfast-sdk/client for browsers and servers: the storefront client, the admin client and webhook verification.

@shopfast-sdk/client is the typed client for the Storefront API, the REST API and webhooks. It has no dependencies and runs in browsers, Node 20+, Bun, Deno, edge runtimes and Next.js.

npm install @shopfast-sdk/client

Types are generated from the same contract the API validates against, and a schema snapshot is checked on every change to ShopFast, so an upgrade of the SDK never surprises you.

The storefront client

import { createStorefrontClient } from '@shopfast-sdk/client';

const shop = createStorefrontClient({
  shop: 'your-store',          // your handle, or the st_ id
  key: 'sfpk_live_…',          // a publishable key in browsers; a private key (sfsk_…) on your server
});

const info = await shop.shop.get();                              // name, currency, status, SEO, links
const page = await shop.products.list({ collection: 'staff-picks', limit: 24 });
const product = await shop.products.get('nimbus-vpn');
const live = await shop.products.availability('nimbus-vpn');     // never cached
const found = await shop.search.query({ q: 'vpn' });
Area Methods
shop get, policies
pages, reviews list, get · list
products list, iterate (every page), get, availability, reviews, recommendations
collections, categories list, get (with a page of products)
search query, suggest
cart create, get, addLines, updateLine, update, applyCode, removeCode, checkoutOptions, checkout, checkoutLink
orders get(sessionId, access)
customer requestCode, signIn, signOut, get, orders, library, cancelSubscription, wallet
support open, list, get, reply

Lists

Lists answer { items, total, nextCursor }. Pass nextCursor back as after, or let iterate walk every page:

for await (const product of shop.products.iterate({ category: 'software' })) {
  console.log(product.title, product.priceMinor);
}

Cart and checkout

const cart = await shop.cart.create({ lines: [{ productId: product.id, variantId: product.variants[0]?.id ?? null, quantity: 1 }] });
await shop.cart.applyCode(cart.id, 'LAUNCH10');

// Your own checkout page: what to ask for this cart, then start it and send the buyer to pay.
const options = await shop.cart.checkoutOptions(cart.id, { country: 'DE' });
try {
  const session = await shop.cart.checkout(cart.id, {
    email, name, country: 'DE', consent: true, provider: options.methods[0]?.provider ?? null, method: options.methods[0]?.method ?? null,
    returnUrl: 'https://shop.example.com/checkout/complete',
  });
  redirect(session.paid ? `/orders/${session.sessionId}` : session.payUrl);
} catch (error) {
  if (isShopFastError(error) && error.is('email-verification-required')) askForTheCode();
  else throw error;
}

// Or the hosted checkout, back to your site after paying.
const { checkoutUrl } = await shop.cart.checkoutLink(cart.id, { returnUrl: 'https://shop.example.com/checkout/complete' });

See Headless checkout for your own checkout, the embedded one and the hosted one.

A signed-in buyer

await shop.customer.requestCode('[email protected]');
const session = await shop.customer.signIn('[email protected]', '123456');
const buyer = shop.withCustomer(session.token);
const library = await buyer.customer.library();                  // keys, downloads, subscriptions

On your server

With a private key, pass the buyer’s address so limits and fraud checks see the buyer:

const shop = createStorefrontClient({ shop: 'your-store', key: process.env.SHOPFAST_STOREFRONT_KEY!, buyerIp: request.headers.get('x-forwarded-for') });

Errors, retries and caching

  • Errors throw ShopFastError with the problem fields: status, type, title, detail, fields and retryAfter. isShopFastError(e) narrows it.
  • Reads retry on 429 and 503, honoring Retry-After. Writes retry only when you pass an idempotency key.
  • cache: true keeps shared answers with their ETag and revalidates them for free (304). Carts, accounts and orders are never kept.

Next.js

import { createNextStorefrontClient } from '@shopfast-sdk/client/next';

export const shop = createNextStorefrontClient({ shop: 'your-store', key: process.env.SHOPFAST_STOREFRONT_KEY!, revalidate: false });

Every fetch carries next: { tags } for your shop, so the storefront.updated webhook refreshes exactly what changed:

// app/api/revalidate/route.ts
import { revalidateTag } from 'next/cache';
import { handleRevalidationWebhook } from '@shopfast-sdk/client/next';

export const POST = (request: Request) => handleRevalidationWebhook(request, { secret: process.env.SHOPFAST_WEBHOOK_SECRET!, revalidateTag });

The Next.js starter puts all of it together.

Webhooks

import { verifyWebhook } from '@shopfast-sdk/client';

const event = await verifyWebhook({ secret: process.env.SHOPFAST_WEBHOOK_SECRET!, headers: request.headers, body: await request.text() });
// throws WebhookVerificationError on a bad signature or a delivery older than 5 minutes

Pass two secrets during a rotation. The body must be the raw text, exactly as received.

The admin client

For your own store’s REST API with an API key or an OAuth token:

import { createAdminClient } from '@shopfast-sdk/client';

const admin = createAdminClient({ apiKey: process.env.SHOPFAST_API_KEY! });
const orders = await admin.request('GET', '/v1/store/orders', { query: { tab: 'paid', limit: 20 } });
await admin.request('POST', '/v1/store/products', { body: { name: 'Launch Pack', type: 'service', priceMinor: 2500 } });

Every write carries an Idempotency-Key (a fresh one unless you pass yours), so a retry after a timeout never runs twice. Pass parse (a zod schema’s parse, say) to get your own type back.

Was this page helpful?