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

@sdg.la/wishlist-stack-sdk

v0.10.1

Published

TypeScript SDK for the Wishlist Stack API

Downloads

347

Readme

@sdg.la/wishlist-stack-sdk

npm version TypeScript

TypeScript/JavaScript SDK for the Wishlist Stack API.

Table of Contents

Features

  • TypeScript-first — Full type definitions with exported types for all requests and responses
  • Universal — Works in Node.js, browsers, and edge runtimes (Cloudflare Workers, Deno, etc.)
  • Flexible API — Choose between functional createWishlistStackClient() or class-based new WishlistStackClient()
  • Built-in error handling — Structured WishlistStackApiError with status codes and API error messages
  • Pagination support — All list endpoints support page and pageSize parameters

Requirements

  • Node.js 18+ (uses native fetch) or provide a custom fetch implementation
  • Modern browsers with native fetch support

Installation

npm install @sdg.la/wishlist-stack-sdk
yarn add @sdg.la/wishlist-stack-sdk
pnpm add @sdg.la/wishlist-stack-sdk

Quick Start

import { createWishlistStackClient } from '@sdg.la/wishlist-stack-sdk';

const client = createWishlistStackClient({
  apiKey: 'your-merchant-api-key',
  customerAccessToken: 'customer-access-token', // required for authenticated endpoints
});

// Fetch all groups
const { groups } = await client.groups.getAll();

// Fetch all lists
const { lists } = await client.lists.getAll();

Usage

Server / Node.js / Workers

import { createWishlistStackClient } from '@sdg.la/wishlist-stack-sdk';

const client = createWishlistStackClient({
  apiKey: process.env.WISHLIST_STACK_API_KEY!,
  baseUrl: process.env.WISHLIST_STACK_BASE_URL, // optional, defaults to production
  customerAccessToken: '...customer_access_token...',
});

const { groups } = await client.groups.getAll();

Browser

import { createWishlistStackClient } from '@sdg.la/wishlist-stack-sdk';

const client = createWishlistStackClient({
  apiKey: 'YOUR_API_KEY',
  baseUrl: 'https://wishlist.devteam.run', // optional default
  customerAccessToken: '...optional...',
});

const { lists } = await client.lists.getAll();

Note: If you use a strict Content Security Policy (CSP), you must allow your API domain in connect-src.

Class API

If you prefer a class-based API, use WishlistStackSDK:

import { WishlistStackSDK } from '@sdg.la/wishlist-stack-sdk';

const client = new WishlistStackSDK({
  apiKey: process.env.WISHLIST_STACK_API_KEY!,
  baseUrl: process.env.WISHLIST_STACK_BASE_URL,
  customerAccessToken: '...optional...',
});

await client.groups.getAll();
await client.lists.getById('list-id');

Back-compat aliases are available on the class:

await client.getGroups();        // alias for client.groups.getAll()
await client.getGroupById('id'); // alias for client.groups.getById('id')
await client.getLists();         // alias for client.lists.getAll()

Error Handling

Non-2xx responses throw WishlistStackApiError. Use the isWishlistStackApiError type guard for safe handling:

import { isWishlistStackApiError } from '@sdg.la/wishlist-stack-sdk';

try {
  await client.groups.getAll();
} catch (error) {
  if (isWishlistStackApiError(error)) {
    console.log(error.status);           // HTTP status code
    console.log(error.apiErrors);        // Array of { message, field? }
    console.log(error.apiErrorMessages); // Array of error message strings
    console.log(error.requestId);        // Request ID for debugging
  }
}

Pagination

Endpoints that return lists support pagination via query parameters:

// Paginate groups
await client.groups.getAll({ page: 2, pageSize: 10 });

// Paginate lists within a group
await client.groups.getById('group-id', { page: 1, pageSize: 25 });

// Paginate items within a list
await client.lists.getById('list-id', { page: 1, pageSize: 25 });

Pagination response structure:

{
  page: number;
  pageSize: number;
  totalCount: number;
  totalPages: number;
}

API Reference

Groups

View full Groups API reference


groups.getAll(query?)

Fetch all groups for the authenticated customer.

  • Endpoint: GET /api/groups
  • Parameters: query?{ page?: number; pageSize?: number, query?: string }
  • Returns: Promise<GetGroupsResponse>
const { groups, pagination } = await client.groups.getAll({ page: 1, pageSize: 10, query: 'holiday' });
{
  "groups": [
    {
      "id": "cml8drad90001js09weonjojg",
      "name": "Holiday Lists",
      "description": "All my holiday wishlists",
      "position": 1,
      "shared": false,
      "listCount": 2,
      "createdAt": "2025-01-10T08:00:00.000Z",
      "updatedAt": "2025-01-18T12:00:00.000Z",
      "featuredItems": [
        {
          "variantId": 46932429275374,
          "image": {
            "altText": "Product image",
            "url": "https://cdn.shopify.com/s/files/1/example.jpg",
            "width": 800,
            "height": 800
          }
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 10,
    "totalCount": 1,
    "totalPages": 1
  }
}

groups.getById(groupId, query?)

Fetch a single group by ID. Lists within the group are paginated.

  • Endpoint: GET /api/groups/{groupId}
  • Parameters:
    • groupIdstring
    • query?{ page?: number; pageSize?: number, query?: string }
  • Returns: Promise<GetGroupResponse>
const group = await client.groups.getById('group-id', { page: 1, pageSize: 25, query: 'gift' });
{
  "id": "cml8drad90001js09weonjojg",
  "name": "Holiday Lists",
  "description": "All my holiday wishlists",
  "position": 1,
  "shared": false,
  "listCount": 1,
  "createdAt": "2025-01-10T08:00:00.000Z",
  "updatedAt": "2025-01-18T12:00:00.000Z",
  "lists": [
    {
      "id": "cml8drg8x0003js093j4ua6p8",
      "name": "Gift Ideas",
      "description": "Birthday gift ideas",
      "position": 1,
      "shared": false,
      "itemCount": 2,
      "featuredItems": [
        {
          "variantId": 46932429275374,
          "image": {
            "altText": "Product image",
            "url": "https://cdn.shopify.com/s/files/1/example.jpg",
            "width": 800,
            "height": 800
          }
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 10,
    "totalCount": 1,
    "totalPages": 1
  }
}

groups.create(body)

Create a new group.

  • Endpoint: POST /api/groups
  • Parameters: body{ name?: string; description?: string }
  • Returns: Promise<CreateGroupResponse>
const group = await client.groups.create({ name: 'Favorites', description: 'My favorite items' });
{
  "id": "cml8drad90001js09weonjojg",
  "name": "Favorites",
  "description": "My favorite items",
  "position": 1,
  "shared": false,
  "listCount": 0,
  "createdAt": "2025-01-10T08:00:00.000Z",
  "updatedAt": "2025-01-10T08:00:00.000Z"
}

groups.update(groupId, body)

Update a group's name or description.

  • Endpoint: PATCH /api/groups/{groupId}
  • Parameters:
    • groupIdstring
    • body{ name?: string; description?: string }
  • Returns: Promise<UpdateGroupDetailsResponse>
await client.groups.update('group-id', { name: 'Renamed Group' });
{
  "id": "cml8drad90001js09weonjojg",
  "name": "Renamed Group",
  "description": "All my holiday wishlists",
  "position": 1,
  "shared": false,
  "listCount": 2,
  "createdAt": "2025-01-10T08:00:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

groups.remove(groupId)

Remove a group. Lists assigned to the group are unassigned, not deleted.

  • Endpoint: DELETE /api/groups/{groupId}
  • Parameters: groupIdstring
  • Returns: Promise<RemoveGroupResponse>
await client.groups.remove('group-id');
{ "ok": true }

groups.reorder(groupId, body)

Reorder lists within a group.

  • Endpoint: POST /api/groups/{groupId}/reorder
  • Parameters:
    • groupIdstring
    • body{ listIds?: string[] }
  • Returns: Promise<ReorderGroupResponse>
await client.groups.reorder('group-id', { listIds: ['list-3', 'list-1', 'list-2'] });

groups.share(groupId)

Mark a group as shared (public read-only). Also shares all lists within the group.

  • Endpoint: POST /api/groups/{groupId}/share
  • Parameters: groupIdstring
  • Returns: Promise<MarkGroupSharedResponse>
await client.groups.share('group-id');
{
  "id": "cmlbe49qk0001ju094x1ya9co",
  "name": "Holiday Lists",
  "description": "All my holiday wishlists",
  "position": 1,
  "shared": true,
  "listCount": 2,
  "createdAt": "2025-01-10T08:00:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

groups.unshare(groupId)

Revoke sharing for a group and all of its lists.

  • Endpoint: DELETE /api/groups/{groupId}/share
  • Parameters: groupIdstring
  • Returns: Promise<RevokeGroupSharedResponse>
await client.groups.unshare('group-id');
{
  "id": "cmlbe49qk0001ju094x1ya9co",
  "name": "Holiday Lists",
  "description": "All my holiday wishlists",
  "position": 1,
  "shared": false,
  "listCount": 2,
  "createdAt": "2025-01-10T08:00:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

Lists

View full Lists API reference


lists.getAll(query?)

Fetch all lists for the authenticated customer.

  • Endpoint: GET /api/lists
  • Parameters: query?{ page?: number; pageSize?: number, query?: string }
  • Returns: Promise<GetListsResponse>
const { lists, pagination } = await client.lists.getAll({ page: 1, pageSize: 10 });
{
  "lists": [
    {
      "id": "cml8drg8x0003js093j4ua6p8",
      "name": "Gift Ideas",
      "description": "Birthday gift ideas",
      "position": 1,
      "shared": false,
      "itemCount": 3,
      "createdAt": "2025-01-15T10:30:00.000Z",
      "updatedAt": "2025-01-20T14:00:00.000Z",
      "featuredProducts": [
        {
          "variantId": 46932429275374,
          "image": {
            "altText": "Product image",
            "url": "https://cdn.shopify.com/s/files/1/example.jpg",
            "width": 800,
            "height": 800
          }
        }
      ]
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 10,
    "totalCount": 1,
    "totalPages": 1
  }
}

lists.getById(listId, query?)

Fetch a single list by ID. Items are hydrated with Shopify product data and paginated.

  • Endpoint: GET /api/lists/{listId}
  • Parameters:
    • listIdstring
    • query?{ page?: number; pageSize?: number }
  • Returns: Promise<GetListResponse>
const list = await client.lists.getById('list-id', { page: 1, pageSize: 25 });
{
  "id": "cml8drg8x0003js093j4ua6p8",
  "name": "Gift Ideas",
  "description": "Birthday gift ideas",
  "position": 1,
  "shared": false,
  "itemCount": 1,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-20T14:00:00.000Z",
  "items": [
    {
      "id": "cml8dveoj0002jf09vsqw10r2",
      "userNote": "Love this one",
      "quantity": 1,
      "position": 1,
      "product": {
        "id": "gid://shopify/Product/123456789",
        "title": "Classic T-Shirt",
        "description": "A comfortable classic t-shirt",
        "availableForSale": true,
        "handle": "classic-t-shirt",
        "productType": "Apparel",
        "category": null,
        "tags": ["summer", "casual"],
        "onlineStoreUrl": "https://example.myshopify.com/products/classic-t-shirt",
        "metafields": [],
        "variant": {
          "id": "gid://shopify/ProductVariant/46932429275374",
          "title": "Medium / Blue",
          "sku": "TSHIRT-M-BLUE",
          "price": { "amount": "29.99", "currencyCode": "USD" },
          "compareAtPrice": null,
          "availableForSale": true,
          "currentlyNotInStock": false,
          "selectedOptions": [
            { "name": "Size", "value": "Medium" },
            { "name": "Color", "value": "Blue" }
          ],
          "image": {
            "altText": "Classic T-Shirt in Blue",
            "url": "https://cdn.shopify.com/s/files/1/example.jpg",
            "width": 800,
            "height": 800
          },
          "metafields": []
        },
        "vendor": "Example Brand"
      },
      "properties": null
    }
  ]
}

lists.create(body)

Create a new list, optionally assigned to a group.

  • Endpoint: POST /api/lists
  • Parameters: body{ name?: string; description?: string; groupId?: string }
  • Returns: Promise<CreateListResponse>
const list = await client.lists.create({ name: 'Gift Ideas', groupId: 'group-id' });
{
  "id": "cml8drg8x0003js093j4ua6p8",
  "name": "Gift Ideas",
  "description": null,
  "position": 1,
  "shared": false,
  "itemCount": 0,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-15T10:30:00.000Z"
}

lists.update(listId, body)

Update a list's name, description, or group assignment.

  • Endpoint: PATCH /api/lists/{listId}
  • Parameters:
    • listIdstring
    • body{ name?: string; description?: string; groupId?: string }
  • Returns: Promise<UpdateListDetailsResponse>
await client.lists.update('list-id', { name: 'Renamed List' });
{
  "id": "cml8drg8x0003js093j4ua6p8",
  "name": "Renamed List",
  "description": "Birthday gift ideas",
  "position": 1,
  "shared": false,
  "itemCount": 3,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

lists.remove(listId)

Remove a list and all of its items.

  • Endpoint: DELETE /api/lists/{listId}
  • Parameters: listIdstring
  • Returns: Promise<RemoveListResponse>
await client.lists.remove('list-id');
{ "ok": true }

lists.addItems(listId, body)

Add one or more items to a list.

  • Endpoint: POST /api/lists/{listId}/add
  • Parameters:
    • listIdstring
    • bodyAddItemsToListBody
type AddItemsToListBody = {
  items?: Array<{
    variantId?: string;
    quantity?: number;
    note?: string;
    properties?: Record<string, unknown> | null;
  }>;
};
  • Returns: Promise<AddItemsToListResponse> — the updated list with hydrated items (same shape as getById)
await client.lists.addItems('list-id', {
  items: [
    { variantId: 'gid://shopify/ProductVariant/123', quantity: 1 },
    { variantId: 'gid://shopify/ProductVariant/456', note: 'Size M' },
  ],
});

lists.updateItem(listId, itemId, body)

Update an item on a list (variant, quantity, note, position, or custom properties). When variantId is provided, the item's product data is re-fetched from Shopify to reflect the new variant.

  • Endpoint: PATCH /api/lists/{listId}/items/{itemId}
  • Parameters:
    • listIdstring
    • itemIdstring
    • body{ variantId?: string; quantity?: number; note?: string; position?: number; properties?: Record<string, unknown> | null }
  • Returns: Promise<UpdateListItemResponse> — the updated hydrated item
await client.lists.updateItem('list-id', 'item-id', { quantity: 3, note: 'Updated note' });

// Change the variant stored on the item
await client.lists.updateItem('list-id', 'item-id', { variantId: '46932429275374' });
{
  "id": "cml8dveoj0002jf09vsqw10r2",
  "userNote": "Updated note",
  "quantity": 3,
  "position": 1,
  "product": {
    "id": "gid://shopify/Product/123456789",
    "title": "Classic T-Shirt",
    "description": "A comfortable classic t-shirt",
    "availableForSale": true,
    "handle": "classic-t-shirt",
    "productType": "Apparel",
    "category": null,
    "tags": ["summer", "casual"],
    "onlineStoreUrl": "https://example.myshopify.com/products/classic-t-shirt",
    "metafields": [],
    "variant": {
      "id": "gid://shopify/ProductVariant/46932429275374",
      "title": "Medium / Blue",
      "sku": "TSHIRT-M-BLUE",
      "price": { "amount": "29.99", "currencyCode": "USD" },
      "compareAtPrice": null,
      "availableForSale": true,
      "currentlyNotInStock": false,
      "selectedOptions": [
        { "name": "Size", "value": "Medium" },
        { "name": "Color", "value": "Blue" }
      ],
      "image": {
        "altText": "Classic T-Shirt in Blue",
        "url": "https://cdn.shopify.com/s/files/1/example.jpg",
        "width": 800,
        "height": 800
      },
      "metafields": []
    },
    "vendor": "Example Brand"
  },
  "properties": null
}

lists.removeItem(listId, itemId)

Remove an item from a list.

  • Endpoint: DELETE /api/lists/{listId}/items/{itemId}
  • Parameters:
    • listIdstring
    • itemIdstring
  • Returns: Promise<RemoveItemFromListResponse>
await client.lists.removeItem('list-id', 'item-id');
{ "ok": true }

lists.reorderItems(listId, body)

Reorder items in a list. You can submit all items or just a subset. Each submitted item specifies its ID and desired 1-based position. Items not included in the request keep their relative order and fill the remaining slots.

  • Endpoint: POST /api/lists/{listId}/reorder
  • Parameters:
    • listIdstring
    • body{ items: Array<{ id: string; position: number }> }
  • Returns: Promise<ReorderListItemsResponse>
await client.lists.reorderItems('list-id', {
  items: [
    { id: 'item-3', position: 1 },
    { id: 'item-1', position: 2 },
    { id: 'item-2', position: 3 },
  ],
});
{
  "updated": [
    { "id": "item-3", "position": 1 },
    { "id": "item-1", "position": 2 },
    { "id": "item-2", "position": 3 }
  ]
}

lists.share(listId)

Mark a list as shared (public read-only).

  • Endpoint: POST /api/lists/{listId}/share
  • Parameters: listIdstring
  • Returns: Promise<MarkListSharedResponse>
await client.lists.share('list-id');
{
  "id": "cml8drg8x0003js093j4ua6p8",
  "name": "Gift Ideas",
  "description": "Birthday gift ideas",
  "position": 1,
  "shared": true,
  "itemCount": 3,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

lists.unshare(listId)

Revoke sharing for a list.

  • Endpoint: DELETE /api/lists/{listId}/share
  • Parameters: listIdstring
  • Returns: Promise<RevokeListSharedResponse>
await client.lists.unshare('list-id');
{
  "id": "cml8drg8x0003js093j4ua6p8",
  "name": "Gift Ideas",
  "description": "Birthday gift ideas",
  "position": 1,
  "shared": false,
  "itemCount": 3,
  "createdAt": "2025-01-15T10:30:00.000Z",
  "updatedAt": "2025-01-22T09:15:00.000Z"
}

Shared

Public read-only endpoints for accessing shared lists and groups. These require only the merchant apiKey — no customer access token needed.

View full Shared API reference


shared.getSharedList(listId)

Fetch a public shared list with hydrated product details.

  • Endpoint: GET /api/shared/list/{listId}
  • Parameters: listIdstring
  • Returns: Promise<GetSharedListResponse> — same response shape as lists.getById
const list = await client.shared.getSharedList('list-id');

shared.getSharedGroup(groupId)

Fetch a public shared group and its lists.

  • Endpoint: GET /api/shared/group/{groupId}
  • Parameters: groupIdstring
  • Returns: Promise<GetSharedGroupResponse> — same response shape as groups.getById
const group = await client.shared.getSharedGroup('group-id');

TypeScript Types

The package exports all request, response, and domain types. Import what you need:

import type {
  // Client
  CreateWishlistStackClientOptions,
  WishlistStackClient,

  // Errors
  WishlistStackApiError,
  WishlistStackApiErrorDetails,

  // Groups
  GetGroupsResponse,
  GetGroupResponse,
  GroupSummary,
  GroupDetail,
  GroupDetailList,
  CreateGroupBody,
  UpdateGroupBody,
  ReorderGroupBody,

  // Lists
  GetListsResponse,
  GetListResponse,
  ListSummary,
  ListDetail,
  CreateListBody,
  UpdateListBody,
  AddItemsToListBody,
  UpdateListItemBody,
  ReorderListItemsBody,
  ReorderListItemsResponse,

  // Shared
  GetSharedListResponse,
  GetSharedGroupResponse,

  // Items & Products
  HydratedWishlistItem,
  HydratedProduct,
  HydratedVariant,
  Money,
  SelectedOption,
  Metafield,

  // Common
  Pagination,
  PaginationParams,
  OkResponse,
  Image,
  FeaturedItem,
} from '@sdg.la/wishlist-stack-sdk';

Domain Types

Below are the key domain types returned by the SDK. Expand any section to see the full shape.

type Pagination = {
  page: number;
  pageSize: number;
  totalCount: number;
  totalPages: number;
};
type GroupSummary = {
  id: string;
  name: string;
  description: string | null;
  position: number;
  shared: boolean;
  listCount: number;
  featuredItems: FeaturedItem[];
  createdAt: string;
  updatedAt: string;
};
type GroupDetail = {
  id: string;
  name: string;
  description: string | null;
  position: number;
  shared: boolean;
  listCount: number;
  createdAt: string;
  updatedAt: string;
  listsCount: number;
  lists: GroupDetailList[];
  pagination: Pagination;
};

type GroupDetailList = {
  id: string;
  position: number;
  name: string;
  description: string | null;
  isShared: boolean;
  itemCount: number;
  items: string[];
  featuredItems: FeaturedItem[];
};
type ListSummary = {
  id: string;
  name: string;
  description: string | null;
  position: number;
  shared: boolean;
  itemCount: number;
  createdAt: string;
  updatedAt: string;
  featuredProducts?: FeaturedItem[];
};
type ListDetail = {
  id: string;
  name: string;
  description: string | null;
  position: number;
  shared: boolean;
  itemCount: number;
  createdAt: string;
  updatedAt: string;
  items: HydratedWishlistItem[];
};
type HydratedWishlistItem = {
  id: string;
  userNote?: string | null;
  quantity: number;
  position: number;
  product: HydratedProduct;
  properties?: Record<string, unknown> | null;
};
type HydratedProduct = {
  id: string | number;
  title: string;
  description: string;
  availableForSale: boolean;
  handle: string;
  productType: string | null;
  category: string | null;
  tags: string[];
  onlineStoreUrl: string | null;
  metafields: Metafield[];
  variant: HydratedVariant;
  vendor: string | null;
};

type HydratedVariant = {
  id: string | number;
  title: string;
  sku?: string | null;
  price: Money;
  compareAtPrice?: Money | null;
  availableForSale: boolean;
  currentlyNotInStock: boolean;
  selectedOptions: SelectedOption[];
  image?: Image | null;
  metafields: Metafield[];
};

type Money = { amount: string; currencyCode: string };
type SelectedOption = { name: string; value: string };
type Metafield = { key: string; value: string };
type Image = {
  altText?: string | null;
  url: string;
  width?: number | null;
  height?: number | null;
};

type FeaturedItem = {
  variantId: string | number;
  image?: Image | null;
};
type OkResponse = { ok: true };

Related Packages