npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spree/sdk

v1.0.1

Published

Official TypeScript SDK for Spree Commerce Store API

Downloads

1,558

Readme

@spree/sdk

Official TypeScript SDK for Spree Commerce API v3.

Installation

npm install @spree/sdk
# or
yarn add @spree/sdk
# or
pnpm add @spree/sdk

Quick Start

import { createClient } from '@spree/sdk';

// Initialize the client
const client = createClient({
  baseUrl: 'https://api.mystore.com',
  publishableKey: 'spree_pk_xxx',
});

// Browse products (Store API)
const products = await client.products.list({
  limit: 10,
  expand: ['variants', 'media'],
});

// Get a single product
const product = await client.products.get('spree-tote');

// Authentication
const { token, user } = await client.auth.login({
  email: '[email protected]',
  password: 'password123',
});

// Create a cart and add items
const cart = await client.carts.create();
await client.carts.items.create(cart.id, {
  variant_id: 'var_abc123',
  quantity: 2,
}, { spreeToken: cart.token });

// Update cart and complete
await client.carts.update(cart.id, {
  email: '[email protected]',
}, { spreeToken: cart.token });
await client.carts.complete(cart.id, { spreeToken: cart.token });

Client Architecture

All Store API resources are available directly on the client:

client.products.list()                      // Products
client.carts.create()                       // Create a cart
client.carts.get(cartId)                    // Get a cart by ID
client.carts.items.create(cartId, params)   // Cart line items
client.carts.complete(cartId, opt)          // Complete cart
client.carts.list(opt)                      // List active carts
client.customers.create(params)             // Registration
client.customer.get(opt)                    // Account
client.customer.orders.list()               // Order history

Authentication

The SDK supports multiple authentication modes:

1. Publishable Key Only (Guest/Public Access)

const client = createClient({
  baseUrl: 'https://api.mystore.com',
  publishableKey: 'spree_pk_xxx',
});

// Public endpoints work without user authentication
const products = await client.products.list();

2. Publishable Key + JWT (Authenticated Customer)

// Login to get tokens
const { token, user } = await client.auth.login({
  email: '[email protected]',
  password: 'password123',
});

// Use token for authenticated requests
const orders = await client.customer.orders.list({}, { token });

// Refresh token when needed
const newTokens = await client.auth.refresh({ token });

3. Register New Customer

const { token, user } = await client.customers.create({
  email: '[email protected]',
  password: 'password123',
  password_confirmation: 'password123',
  first_name: 'John',
  last_name: 'Doe',
});

Guest Checkout

For guest checkout, use the token returned when creating a cart:

// Create a cart (guest)
const cart = await client.carts.create();

// Use spreeToken for all cart operations
const options = { spreeToken: cart.token };

// Add items
await client.carts.items.create(cart.id, {
  variant_id: 'var_abc123',
  quantity: 1,
}, options);

// Update cart with email and addresses
await client.carts.update(cart.id, {
  email: '[email protected]',
}, options);

// Complete the order
await client.carts.complete(cart.id, options);

API Reference

Store

// Get current store information
const store = await client.store.get();

Products

// List products with filtering
const products = await client.products.list({
  page: 1,
  limit: 25,
  name_cont: 'shirt',
  sort: 'price asc',
  expand: ['variants', 'media', 'categories'],
});

// Get single product by ID or slug
const product = await client.products.get('spree-tote', {
  expand: ['variants', 'media'],
});

// Get product with prior price (EU Omnibus Directive compliance)
const product = await client.products.get('spree-tote', {
  expand: ['prior_price'],
});
console.log(product.prior_price); // { amount: "9.99", currency: "USD", display_amount: "$9.99", ... }

// Get available filters (price range, availability, options, categories)
const filters = await client.products.filters({
  category_id: 'ctg_abc123', // Optional: scope filters to a category
});

Categories

// List categories with filtering
const categories = await client.categories.list({
  depth_eq: 1,              // Top-level categories only
});

// Get single category by ID or permalink
const category = await client.categories.get('clothing/shirts', {
  expand: ['ancestors', 'children'], // For breadcrumbs and subcategories
});

// List products in a category
const categoryProducts = await client.categories.products.list('clothing/shirts', {
  page: 1,
  limit: 12,
  expand: ['media', 'default_variant'],
});

Carts

All cart operations use explicit cart IDs (cart_xxx). The cart is authorized via spreeToken header (guest) or JWT token (authenticated user).

// Create a new cart
const cart = await client.carts.create();

// Get a cart by ID
const cart = await client.carts.get('cart_xxx', { spreeToken: cart.token });

// Delete / abandon a cart
await client.carts.delete('cart_xxx', { spreeToken: cart.token });

// Associate guest cart with authenticated user
await client.carts.associate('cart_xxx', {
  token: jwtToken,          // User's JWT token
});

// List all active carts for authenticated user
const { data: carts } = await client.carts.list({ token: jwtToken });

// Update cart (email, addresses, customer note)
await client.carts.update('cart_xxx', {
  email: '[email protected]',
  shipping_address: {
    first_name: 'John',
    last_name: 'Doe',
    address1: '123 Main St',
    city: 'New York',
    postal_code: '10001',
    phone: '+1 555 123 4567',
    country_iso: 'US',
    state_abbr: 'NY',
  },
}, options);

// Complete the order
await client.carts.complete('cart_xxx', options);

Cart Items (Line Items)

const options = { spreeToken: cart.token };

// Add item
await client.carts.items.create('cart_xxx', {
  variant_id: 'var_123',
  quantity: 2,
}, options);

// Update item quantity
await client.carts.items.update('cart_xxx', lineItemId, {
  quantity: 3,
}, options);

// Remove item
await client.carts.items.delete('cart_xxx', lineItemId, options);

Discount Codes

const options = { spreeToken: cart.token };

// Apply a discount code
await client.carts.discountCodes.apply('cart_xxx', 'SAVE20', options);

// Remove a discount code
await client.carts.discountCodes.remove('cart_xxx', 'SAVE20', options);

Gift Cards

const options = { spreeToken: cart.token };

// Apply a gift card
const cart = await client.carts.giftCards.apply('cart_xxx', 'GC-ABCD-1234', options);

// Remove a gift card (ID from cart.gift_card.id)
await client.carts.giftCards.remove('cart_xxx', 'gc_abc123', options);

// Check cart warnings (e.g. items removed due to stock changes)
if (cart.warnings.length > 0) {
  cart.warnings.forEach(w => console.log(w.code, w.message));
}

Fulfillments

const options = { spreeToken: cart.token };

// Fulfillments are included in the cart response.
// Select a delivery rate
await client.carts.fulfillments.update('cart_xxx', fulfillmentId, {
  selected_delivery_rate_id: 'rate_xxx',
}, options);

Store Credits

const options = { spreeToken: cart.token };

// Apply store credit (applies maximum available by default)
await client.carts.storeCredits.apply('cart_xxx', undefined, options);

// Apply specific amount of store credit
await client.carts.storeCredits.apply('cart_xxx', 25.00, options);

// Remove store credit
await client.carts.storeCredits.remove('cart_xxx', options);

Payments

const options = { spreeToken: cart.token };

// Payment methods and payments are included in the cart response.
// Each payment method includes `session_required` flag:
// - true  -> use paymentSessions (Stripe, Adyen, PayPal, etc.)
// - false -> use payments.create (Check, Cash on Delivery, Bank Transfer, etc.)

// Create a payment for a non-session payment method
// (e.g. Check, Cash on Delivery, Bank Transfer, Purchase Order)
const payment = await client.carts.payments.create('cart_xxx', {
  payment_method_id: 'pm_xxx',
  amount: '99.99',              // Optional, defaults to order total minus store credits
  metadata: {                   // Optional, write-only metadata
    purchase_order_number: 'PO-12345',
  },
}, options);

Payment Sessions

Payment sessions provide a unified, provider-agnostic interface for payment processing. They work with any payment gateway (Stripe, Adyen, PayPal, etc.) through a single API.

const options = { spreeToken: cart.token };

// Create a payment session (initializes a session with the payment gateway)
const session = await client.carts.paymentSessions.create('cart_xxx', {
  payment_method_id: 'pm_xxx',
  amount: '99.99',             // Optional, defaults to order total
  external_data: {              // Optional, provider-specific data
    return_url: 'https://mystore.com/checkout/complete',
  },
}, options);

// The session contains provider-specific data (e.g., Stripe client_secret)
console.log(session.external_data.client_secret);

// Get a payment session
const existing = await client.carts.paymentSessions.get(
  'cart_xxx', session.id, options
);

// Update a payment session (e.g., after order total changes)
await client.carts.paymentSessions.update('cart_xxx', session.id, {
  amount: '149.99',
}, options);

// Complete the payment session (after customer confirms payment on the frontend)
const completed = await client.carts.paymentSessions.complete(
  'cart_xxx',
  session.id,
  { session_result: 'success' },
  options
);
console.log(completed.status); // 'completed'

Orders

Completed orders can be looked up by ID or number:

// Get a completed order by ID or number
const order = await client.orders.get('R123456789', {
  expand: ['items', 'fulfillments'],
}, { spreeToken: orderToken });

For order history, use the customer orders resource:

const options = { token: jwtToken };

// List order history for authenticated customer
const orders = await client.customer.orders.list({}, options);

// Get a specific order from history
const order = await client.customer.orders.get('or_xxx', {
  expand: ['items', 'fulfillments'],
}, options);

Markets

// List all markets
const { data: markets } = await client.markets.list();
// [{ id: "mkt_xxx", name: "North America", currency: "USD", default_locale: "en", ... }]

// Get a single market
const market = await client.markets.get('mkt_xxx');

// Resolve which market applies for a country
const market = await client.markets.resolve('DE');
// => { id: "mkt_xxx", name: "Europe", currency: "EUR", default_locale: "de", ... }

// List countries in a market
const { data: countries } = await client.markets.countries.list('mkt_xxx');

// Get a country in a market (with states for address forms)
const country = await client.markets.countries.get('mkt_xxx', 'DE', {
  expand: ['states'],
});

Geography

// List countries available for checkout
const { data: countries } = await client.countries.list();

// Get country by ISO code (with states)
const usa = await client.countries.get('US', { expand: ['states'] });
console.log(usa.states); // Array of states

Customer Account

const options = { token: jwtToken };

// Get profile
const profile = await client.customer.get(options);

// Update profile
await client.customer.update({
  first_name: 'John',
  last_name: 'Doe',
}, options);

Customer Addresses

const options = { token: jwtToken };

// List addresses
const { data: addresses } = await client.customer.addresses.list({}, options);

// Get address by ID
const address = await client.customer.addresses.get('addr_xxx', options);

// Create address
await client.customer.addresses.create({
  first_name: 'John',
  last_name: 'Doe',
  address1: '123 Main St',
  city: 'New York',
  postal_code: '10001',
  country_iso: 'US',
  state_abbr: 'NY',
}, options);

// Update address
await client.customer.addresses.update('addr_xxx', { city: 'Brooklyn' }, options);

// Delete address
await client.customer.addresses.delete('addr_xxx', options);

// Set as default billing or shipping address
await client.customer.addresses.update('addr_xxx', { is_default_billing: true }, options);
await client.customer.addresses.update('addr_xxx', { is_default_shipping: true }, options);

Password Resets

// Request a password reset email
// Always returns { message: string } (202 status) — prevents email enumeration
await client.passwordResets.create({
  email: '[email protected]',
  redirect_url: 'https://myshop.com/reset-password', // optional, validated against store's allowed origins
});

// Reset password with token from email
// Returns AuthTokens (JWT + user) — auto-login on success
// Token expires in 15 minutes
const { token, user } = await client.passwordResets.update('reset_token_xxx', {
  password: 'newPassword123',
  password_confirmation: 'newPassword123',
});

Customer Credit Cards

const options = { token: jwtToken };

// List saved credit cards
const { data: cards } = await client.customer.creditCards.list({}, options);

// Get credit card by ID
const card = await client.customer.creditCards.get('cc_xxx', options);

// Delete credit card
await client.customer.creditCards.delete('cc_xxx', options);

Wishlists

const options = { token: jwtToken };

// List wishlists
const { data: wishlists } = await client.wishlists.list({}, options);

// Get wishlist by ID
const wishlist = await client.wishlists.get('wl_xxx', {
  expand: ['wishlist_items'],
}, options);

// Create wishlist
const newWishlist = await client.wishlists.create({
  name: 'Birthday Ideas',
  is_private: true,
}, options);

// Update wishlist
await client.wishlists.update('wl_xxx', {
  name: 'Updated Name',
}, options);

// Delete wishlist
await client.wishlists.delete('wl_xxx', options);

Wishlist Items

const options = { token: jwtToken };

// Add item to wishlist
await client.wishlists.items.create('wl_xxx', {
  variant_id: 'var_123',
  quantity: 1,
}, options);

// Update item quantity
await client.wishlists.items.update('wl_xxx', 'wi_xxx', {
  quantity: 2,
}, options);

// Remove item from wishlist
await client.wishlists.items.delete('wl_xxx', 'wi_xxx', options);

Nested Resources

The SDK uses a resource builder pattern for nested resources:

| Parent Resource | Nested Resource | Available Methods | |-----------------|-----------------|-------------------| | carts | items | create, update, delete | | carts | discountCodes | apply, remove | | carts | giftCards | apply, remove | | carts | fulfillments | update | | carts | payments | create | | carts | paymentSessions | create, get, update, complete | | carts | storeCredits | apply, remove | | policies | — | list, get | | passwordResets | — | create, update | | customer | addresses | list, get, create, update, delete | | customer | creditCards | list, get, delete | | customer | giftCards | list, get | | customer | storeCredits | list, get | | customer | orders | list, get | | markets | countries | list, get | | categories | products | list | | wishlists | items | create, update, delete |

Example:

// Cart resources take cartId as first argument
await client.carts.items.create(cartId, params, options);
await client.carts.discountCodes.apply(cartId, code, options);
await client.carts.fulfillments.update(cartId, fulfillmentId, params, options);
await client.carts.payments.create(cartId, params, options);
await client.carts.paymentSessions.create(cartId, params, options);
await client.carts.storeCredits.apply(cartId, amount, options);

// Other nested resources follow the same pattern
await client.customer.addresses.list({}, options);
await client.customer.orders.list({}, options);
await client.markets.countries.list(marketId);
await client.categories.products.list(categoryId, params, options);
await client.wishlists.items.create(wishlistId, params, options);

Localization & Currency

Client-level defaults

Set locale, currency, and country when creating the client:

const client = createClient({
  baseUrl: 'https://api.mystore.com',
  publishableKey: 'spree_pk_xxx',
  locale: 'fr',
  currency: 'EUR',
  country: 'FR',
});

// All requests use fr/EUR/FR automatically
const products = await client.products.list();

Update defaults at any time:

client.setLocale('de');
client.setCurrency('EUR');
client.setCountry('DE');

Per-request overrides

Pass locale and currency headers with any request to override defaults:

const products = await client.products.list({}, {
  locale: 'fr',
  currency: 'EUR',
  country: 'FR',
});

Error Handling

import { SpreeError } from '@spree/sdk';

try {
  await client.products.get('non-existent');
} catch (error) {
  if (error instanceof SpreeError) {
    console.log(error.code);    // 'record_not_found'
    console.log(error.message); // 'Product not found'
    console.log(error.status);  // 404
    console.log(error.details); // Validation errors (if any)
  }
}

TypeScript Support

The SDK includes full TypeScript support with generated types from the API serializers:

import type {
  Product,
  Cart,
  Order,
  Variant,
  Category,
  LineItem,
  Address,
  Customer,
  PaginatedResponse,
} from '@spree/sdk';

// All responses are fully typed
const products: PaginatedResponse<Product> = await client.products.list();
const category: Category = await client.categories.get('clothing');
const cart: Cart = await client.carts.get('cart_xxx');

Available Types

All types are exported as unprefixed names (e.g., Product, Order). Legacy Store* prefixed aliases (e.g., StoreProduct) are still available for backward compatibility.

Core Types

  • Product - Product data
  • Variant - Variant data
  • Cart - Cart data (uses cart_ prefixed IDs)
  • Order - Completed order data (uses or_ prefixed IDs)
  • LineItem - Line item in cart
  • Category - Category
  • Country - Country with states
  • State - State/province
  • Address - Customer address
  • Customer - Customer profile
  • Market - Market configuration (currency, locales, countries)

Commerce Types

  • Payment - Payment record
  • PaymentMethod - Payment method
  • PaymentSession - Provider-agnostic payment session
  • Fulfillment - Fulfillment record
  • DeliveryRate - Delivery rate option
  • DeliveryMethod - Delivery method
  • CreditCard - Saved credit card
  • GiftCard - Gift card
  • Discount - Discount applied to a cart or order

Product Types

  • Media - Product media (images, videos)
  • Price - Price data
  • PriceHistory - Prior price data (for EU Omnibus Directive compliance)
  • OptionType - Option type (e.g., Size, Color)
  • OptionValue - Option value (e.g., Small, Red)
  • DigitalLink - Digital download link
  • CustomField - Custom field data

Wishlist Types

  • Wishlist - Wishlist
  • WishlistItem - Wishlist item

Client Types

  • Client - Main client interface
  • StoreClient - Store API client class
  • ClientConfig - Client configuration
  • RequestOptions - Per-request options
  • RetryConfig - Retry behavior configuration

Utility Types

  • PaginatedResponse<T> - Paginated API response
  • ListResponse<T> - List API response
  • AuthTokens - JWT tokens from login
  • AddressParams - Address input parameters
  • UpdateCartParams - Cart update parameters (email, addresses, etc.)
  • CreatePaymentParams - Direct payment creation parameters (for non-session payment methods)
  • CreatePaymentSessionParams - Payment session creation parameters
  • UpdatePaymentSessionParams - Payment session update parameters
  • CompletePaymentSessionParams - Payment session completion parameters
  • ProductFiltersResponse - Product filters response
  • CheckoutRequirement - Checkout requirement ({ step, field, message })

Custom Fetch

You can provide a custom fetch implementation:

import { createClient } from '@spree/sdk';

const client = createClient({
  baseUrl: 'https://api.mystore.com',
  publishableKey: 'spree_pk_xxx',
  fetch: customFetchImplementation,
});

Webhooks

The SDK includes webhook signature verification and types via the @spree/sdk/webhooks subpath:

import { verifyWebhookSignature, type WebhookEvent } from '@spree/sdk/webhooks';
import type { Order } from '@spree/sdk';

// Verify a webhook signature (works with any framework)
const isValid = verifyWebhookSignature(
  rawBody,                                              // raw request body string
  request.headers.get('x-spree-webhook-signature')!,    // HMAC signature
  request.headers.get('x-spree-webhook-timestamp')!,    // unix timestamp
  process.env.SPREE_WEBHOOK_SECRET!                     // endpoint secret key
);

// Type your webhook handlers using SDK types
type OrderEvent = WebhookEvent<Order>;

function handleOrderCompleted(event: OrderEvent) {
  const order = event.data; // fully typed Order
  console.log(order.number, order.email, order.display_total);
}

Webhook payloads use the same V3 serializers as the REST API, so all SDK types (Order, Cart, Payment, Fulfillment, etc.) work directly as the data field type.

For Next.js projects, the Spree Storefront includes a ready-made webhook route handler with signature verification and event routing.

Development

Setup

cd sdk
npm install

Scripts

| Command | Description | |---------|-------------| | npm test | Run tests once | | npm run test:watch | Run tests in watch mode | | npm run test:coverage | Run tests with coverage report | | npm run typecheck | Type-check with tsc --noEmit | | npm run build | Build CJS + ESM bundles with tsup | | npm run dev | Build in watch mode | | npm run console | Interactive REPL for testing the SDK |

Testing

Tests use Vitest with MSW (Mock Service Worker) for API mocking at the network level.

# Run all tests
npm test

# Run in watch mode during development
npm run test:watch

# Run with coverage
npm run test:coverage

Test files live in tests/ and follow the structure:

  • tests/mocks/handlers.ts - MSW request handlers with fixture data
  • tests/mocks/server.ts - MSW server instance
  • tests/setup.ts - Server lifecycle (listen/reset/close)
  • tests/helpers.ts - createTestClient() and constants
  • tests/*.test.ts - Test suites per resource (auth, products, orders, etc.)

To add tests for a new endpoint, add an MSW handler in handlers.ts and create a corresponding test file.

Releasing

This package uses Changesets for version management and publishing.

After making changes:

npx changeset

This prompts you to select a semver bump type (patch/minor/major) and write a summary. A changeset file is created in .changeset/.

How releases work:

  1. Changeset files are committed with your PR
  2. When merged to main, a GitHub Action creates a "Version Packages" PR that bumps the version and updates the CHANGELOG
  3. When that PR is merged, the package is automatically published to npm

Manual release (if needed):

npm run version   # Apply changesets and bump version
npm run release   # Build and publish to npm

License

MIT