Customer wishlists
Give signed-in customers a persistent list of products they can revisit across devices.
Add a save button to product pages and a page where customers can view or remove their saved items. Littleworks holds each customer’s list; Shopify supplies the current product information and prices.
Use Littleworks to build a wishlist for my Shopify store. Signed-in customers should be able to save product variants, view their saved items across devices, and remove them. Keep the wishlist in Littleworks collections and use the storefront’s current country and language for product information. Read https://littleworks.app/docs/recipe-wishlist.md and adapt it to my theme.What you are building
- A save/remove control for the selected variant and a clear sign-in prompt for logged-out shoppers.
- A paginated wishlist page with current product details, links back to products, and a useful unavailable-item state.
- Private records belonging to the verified customer, available on another device after that customer signs in.
Where the data lives
Use feature wishlist and collection items. Store one document per saved variant: customerId, productId, and variantId, with the document’s timestamps. Littleworks collections fit because this is customer-specific application state with frequent additions and removals. A Shopify customer metafield is not needed for the baseline.
Keep product IDs as references. Stored titles, availability, or prices must not become the authoritative catalog: refresh them from Shopify when displaying a page. Do not place private wishlists in publicly readable metaobjects.
Before you build
- Verify the real customer login and app proxy path. context.customer.id identifies the caller; an input customerId never does.
- The baseline uses the included Storefront catalog scope through shopify.storefront.graphql. It does not require read_customers just to store the verified ID. Additional Shopify operations may need extra grants.
- Confirm the Storefront context and publication rules for the products. The selected country and language come from the storefront. A verified proxy customer ID does not supply a customer access token or B2B pricing context.
The works to build
| Work | Entry point | Behavior |
|---|---|---|
| wishlist.save | Customer HTTP POST | Resolve a valid variant and save it for the verified customer |
| wishlist.list | Customer HTTP GET or POST | Return one page belonging to the verified customer |
| wishlist.remove | Customer HTTP POST | Check ownership and remove the saved item using its revision |
All three works share feature wishlist. During setup, use ensureUnique(["customerId", "variantId"]) on items in each intended environment. This is a uniqueness rule, not a collection schema; validate inputs in each work. Bound IDs, cursor length, page size, and any country/language values. Do not let callers select a feature, collection, or customer filter.
Implementation outline for the agent
- Save a real variant. Accept a variant ID and storefront localization. Query Shopify with shopify.storefront.graphql to resolve the published variant and its product. Reject missing or inaccessible variants. Write context.customer.id and the verified product/variant IDs. On a duplicate conflict, read the matching customer + variant record and report that it is already saved; do not discard the uniqueness rule.
- Read one customer’s page. Apply where: { customerId: context.customer.id } inside the work, with a bounded limit and the returned pagination cursor. Return only the item ID and product/variant references needed by the frontend. A caller-supplied cursor must never replace the fixed ownership filter.
- Resolve current catalog context. Fetch product details for the page in a bounded Storefront query using @inContext country and language variables. Keep currencyCode with every amount. Show a graceful unavailable state for deleted, unpublished, or market-inaccessible products. Avoid a separate work invocation for every item.
- Remove only owned items. Load the document, check data.customerId against context.customer.id, and delete with its revision. Use a consistent not-found response for missing and other customers’ records. If the revision changed, reload and reassess rather than overwriting concurrent work.
- Wire the theme. Use the installed app-proxy URLs from deployment. Disable duplicate in-flight clicks, then reconcile the UI with the actual response. Handle sign-in, limits, and unavailable products. Batch the initial saved-state lookup and avoid background polling for every product card.
How it uses Littleworks
Each saved variant occupies one document. Removing it releases that capacity. A save, a page read, or a removal invokes a work; a page read should resolve several items together within the current run and Shopify limits. Preview and Live share store allowances, even though their wishlist records are separate. Inspect usage before choosing page sizes and any per-customer wishlist limit.
Acceptance checks
- Saving the same variant simultaneously creates one item. Save, refresh, and sign in on another device: the item is still there.
- Customer A cannot list or remove customer B’s items by changing an ID, cursor, or request body. Signed-out and direct-endpoint requests cannot impersonate a customer.
- Changing the storefront country/language refreshes the product context. A removed or unavailable variant does not break the whole page.
- Pagination preserves the customer filter. Removing an item and retrying after a stale revision produces a clear, consistent state.
- Storage/run exhaustion and a paused work show an actionable message without an automatic retry loop.
Make it yours
Add named lists, notes, or an explicit move-to-cart action later. Storefront API carts and the theme’s existing cart are separate integrations; select the appropriate cart path and preserve buyer context. Guest wishlists and public sharing need their own ownership/sharing design. Never make the customer endpoint public to support them.