{"id":17055,"plugin_id":"plugins_6a701c7b1f9481919cf7c7448ddc1bd4","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:13:56.482Z","digest":"fa9d66883b4aa2acc22b2a3bfcfe35ae69ca3075bf3c6f23e094e10be56824a5","against":null,"payload":{"description":"Build customer-facing storefront applications with Shopify Storefront API. Access product catalogs, collections, checkout flows, cart management, and customer accounts using public/private tokens. Includes GraphQL queries, Market directives, Customer Account API, and TypeScript examples. Triggers include: 'storefront api', 'customer-facing shopify', 'shopping cart api', 'product catalog query', 'checkout flow', 'customer account api', 'market directive', 'storefront token'.","included_files":[],"name":"storefront-api","skill_md_contents":"---\nname: storefront-api\ndescription: \"Build customer-facing storefront applications with Shopify Storefront API. Access product catalogs, collections, checkout flows, cart management, and customer accounts using public/private tokens. Includes GraphQL queries, Market directives, Customer Account API, and TypeScript examples. Triggers include: 'storefront api', 'customer-facing shopify', 'shopping cart api', 'product catalog query', 'checkout flow', 'customer account api', 'market directive', 'storefront token'.\"\n---\n\n## When to Use Storefront API\n\nUse **Storefront API** for customer-facing applications:\n- Building custom storefronts (headless commerce)\n- Shopping cart and checkout flows\n- Product browsing and search\n- Customer account management (orders, addresses)\n- Subscription management\n- Market-specific pricing and inventory (with Market directives)\n- Cart line operations (add, remove, update)\n- Customer authentication and profiles\n\n**DO NOT use for:** store management, admin operations, or internal tools (use Admin API instead).\n\n---\n\n## Token Types & Scopes\n\n### Public Access Tokens\n**Use for:** Frontend applications, public data access\n\n```javascript\nconst publicToken = 'Xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'; // Public token\nconst shopDomain = 'mystore.myshopify.com';\nconst endpoint = `https://${shopDomain}/api/2026-01/graphql.json`;\n\nconst headers = {\n  'Content-Type': 'application/json',\n  'X-Shopify-Storefront-Access-Token': publicToken,\n};\n```\n\n**Scopes enabled with public token:**\n- Read products\n- Read product collections\n- Read shop information\n- Read customer information (requires customer login)\n- Create shopping carts\n- Manage shopping carts\n- Access checkout URLs\n\n### Private Access Tokens (Storefront)\n**Use for:** Backend/server-side access with elevated permissions\n\n```javascript\nconst privateToken = process.env.SHOPIFY_STOREFRONT_PRIVATE_TOKEN;\n\nconst headers = {\n  'Content-Type': 'application/json',\n  'X-Shopify-Storefront-Access-Token': privateToken,\n};\n```\n\n**Additional scopes with private token:**\n- Full customer account access\n- All storefront operations\n- No rate limiting (unlike public token: 2 requests/second per IP)\n\n---\n\n## Headers & Configuration\n\n**Standard Storefront Headers:**\n```javascript\nconst headers = {\n  'Content-Type': 'application/json',\n  'X-Shopify-Storefront-Access-Token': accessToken,\n};\n```\n\n**TypeScript Headers Interface:**\n```typescript\ninterface StorefrontHeaders {\n  'Content-Type': 'application/json';\n  'X-Shopify-Storefront-Access-Token': string;\n  'Accept-Language'?: string; // For locale-specific data\n}\n```\n\n**Add Market/Localization (Market Directive):**\n```javascript\nconst headers = {\n  'Content-Type': 'application/json',\n  'X-Shopify-Storefront-Access-Token': accessToken,\n  'Accept-Language': 'en-US', // for Market resolution\n};\n```\n\n---\n\n## Product Queries\n\n### Get Product by Handle\n\n```graphql\nquery GetProduct($handle: String!) {\n  product(handle: $handle) {\n    id\n    title\n    description\n    handle\n    vendor\n    productType\n    images(first: 10) {\n      edges {\n        node {\n          url\n          altText\n        }\n      }\n    }\n    priceRange {\n      minVariantPrice {\n        amount\n        currencyCode\n      }\n      maxVariantPrice {\n        amount\n        currencyCode\n      }\n    }\n    variants(first: 100) {\n      edges {\n        node {\n          id\n          title\n          sku\n          price {\n            amount\n            currencyCode\n          }\n          availableForSale\n          quantityAvailable\n          compareAtPrice {\n            amount\n            currencyCode\n          }\n          selectedOptions {\n            name\n            value\n          }\n        }\n      }\n    }\n    collections(first: 5) {\n      edges {\n        node {\n          title\n          handle\n        }\n      }\n    }\n  }\n}\n```\n\n**Variables:**\n```json\n{\n  \"handle\": \"wireless-headphones\"\n}\n```\n\n### List Products (Paginated)\n\n```graphql\nquery ListProducts($first: Int!, $after: String, $query: String) {\n  products(first: $first, after: $after, query: $query) {\n    pageInfo {\n      hasNextPage\n      endCursor\n    }\n    edges {\n      node {\n        id\n        title\n        handle\n        priceRange {\n          minVariantPrice {\n            amount\n            currencyCode\n          }\n        }\n        images(first: 1) {\n          edges {\n            node {\n              url\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Search Products\n\n```graphql\nquery SearchProducts($query: String!, $first: Int) {\n  products(first: $first, query: $query) {\n    edges {\n      node {\n        id\n        title\n        handle\n        description\n      }\n    }\n  }\n}\n```\n\n---\n\n## Collection Queries\n\n### Get Collection Products\n\n```graphql\nquery GetCollection($handle: String!, $first: Int) {\n  collection(handle: $handle) {\n    id\n    title\n    description\n    image {\n      url\n      altText\n    }\n    products(first: $first) {\n      pageInfo {\n        hasNextPage\n        endCursor\n      }\n      edges {\n        node {\n          id\n          title\n          handle\n          priceRange {\n            minVariantPrice {\n              amount\n              currencyCode\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### List Collections\n\n```graphql\nquery ListCollections($first: Int, $after: String) {\n  collections(first: $first, after: $after) {\n    pageInfo {\n      hasNextPage\n      endCursor\n    }\n    edges {\n      node {\n        id\n        title\n        handle\n        image {\n          url\n        }\n      }\n    }\n  }\n}\n```\n\n---\n\n## Cart & Checkout Workflow\n\n### Create Cart\n\n```graphql\nmutation CreateCart($input: CartInput!) {\n  cartCreate(input: $input) {\n    cart {\n      id\n      checkoutUrl\n      lines(first: 10) {\n        edges {\n          node {\n            id\n            quantity\n            merchandise {\n              ... on ProductVariant {\n                id\n                title\n                price {\n                  amount\n                  currencyCode\n                }\n              }\n            }\n          }\n        }\n      }\n      cost {\n        subtotalAmount {\n          amount\n          currencyCode\n        }\n        totalAmount {\n          amount\n          currencyCode\n        }\n      }\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n### Add to Cart\n\n```graphql\nmutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!) {\n  cartLinesAdd(cartId: $cartId, lines: $lines) {\n    cart {\n      id\n      lines(first: 10) {\n        edges {\n          node {\n            id\n            quantity\n            merchandise {\n              ... on ProductVariant {\n                id\n                title\n                price {\n                  amount\n                  currencyCode\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n**Variables:**\n```json\n{\n  \"cartId\": \"gid://shopify/Cart/abc123\",\n  \"lines\": [\n    {\n      \"merchandiseId\": \"gid://shopify/ProductVariant/123456\",\n      \"quantity\": 2\n    }\n  ]\n}\n```\n\n### Update Cart Line\n\n```graphql\nmutation UpdateCartLine($cartId: ID!, $lines: [CartLineUpdateInput!]!) {\n  cartLinesUpdate(cartId: $cartId, lines: $lines) {\n    cart {\n      id\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n### Remove from Cart\n\n```graphql\nmutation RemoveFromCart($cartId: ID!, $lineIds: [ID!]!) {\n  cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {\n    cart {\n      id\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n### Get Checkout URL\n\n```graphql\nquery GetCart($cartId: ID!) {\n  cart(id: $cartId) {\n    checkoutUrl\n  }\n}\n```\n\n---\n\n## Customer Account API\n\n### Get Current Customer\n\n```graphql\nquery GetCurrentCustomer {\n  customer {\n    id\n    email\n    firstName\n    lastName\n    phone\n    createdAt\n    updatedAt\n    addresses(first: 10) {\n      edges {\n        node {\n          id\n          firstName\n          lastName\n          address1\n          address2\n          city\n          province\n          country\n          zip\n          isDefaultBillingAddress\n          isDefaultShippingAddress\n        }\n      }\n    }\n    orders(first: 10) {\n      edges {\n        node {\n          id\n          orderNumber\n          processedAt\n          totalPrice {\n            amount\n            currencyCode\n          }\n          financialStatus\n          fulfillmentStatus\n          lineItems(first: 10) {\n            edges {\n              node {\n                title\n                quantity\n                price {\n                  amount\n                  currencyCode\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Update Customer\n\n```graphql\nmutation UpdateCustomer($customer: CustomerInput!) {\n  customerUpdate(customer: $customer) {\n    customer {\n      id\n      email\n      firstName\n      lastName\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n### Create Address\n\n```graphql\nmutation CreateAddress($address: MailingAddressInput!) {\n  customerAddressCreate(address: $address) {\n    customerAddress {\n      id\n      address1\n      city\n      country\n      province\n      zip\n    }\n    userErrors {\n      field\n      message\n    }\n  }\n}\n```\n\n---\n\n## Market Directives (Multi-Region)\n\nMarkets enable locale-specific product data, pricing, and availability.\n\n### Query with Market Context\n\n```graphql\nquery GetProductByMarket($handle: String!, $country: CountryCode!, $language: LanguageCode) @inContext(country: $country, language: $language) {\n  product(handle: $handle) {\n    id\n    title\n    priceRange {\n      minVariantPrice {\n        amount\n        currencyCode\n      }\n    }\n    variants(first: 10) {\n      edges {\n        node {\n          id\n          availableForSale\n          quantityAvailable\n        }\n      }\n    }\n  }\n}\n```\n\n**Variables (for Canadian French market):**\n```json\n{\n  \"handle\": \"wireless-headphones\",\n  \"country\": \"CA\",\n  \"language\": \"FR\"\n}\n```\n\n### Supported Markets\n\n```typescript\nenum CountryCode {\n  US = \"US\",\n  CA = \"CA\",\n  GB = \"GB\",\n  AU = \"AU\",\n  JP = \"JP\",\n  DE = \"DE\",\n  FR = \"FR\",\n  IT = \"IT\",\n  // ... and 150+ more\n}\n\nenum LanguageCode {\n  EN = \"EN\",\n  FR = \"FR\",\n  DE = \"DE\",\n  IT = \"IT\",\n  JA = \"JA\",\n  ES = \"ES\",\n  // ... and 20+ more\n}\n```\n\n---\n\n## TypeScript Client Example\n\n```typescript\ninterface StorefrontConfig {\n  shop: string;\n  token: string;\n  apiVersion: string;\n}\n\ninterface Product {\n  id: string;\n  title: string;\n  handle: string;\n  description: string;\n  priceRange: {\n    minVariantPrice: MoneyV2;\n    maxVariantPrice: MoneyV2;\n  };\n  variants: ProductVariant[];\n}\n\ninterface ProductVariant {\n  id: string;\n  title: string;\n  sku: string;\n  price: MoneyV2;\n  availableForSale: boolean;\n  quantityAvailable: number;\n}\n\ninterface MoneyV2 {\n  amount: string;\n  currencyCode: string;\n}\n\nclass StorefrontClient {\n  private endpoint: string;\n  private headers: Record<string, string>;\n\n  constructor(config: StorefrontConfig) {\n    this.endpoint = `https://${config.shop}/api/${config.apiVersion}/graphql.json`;\n    this.headers = {\n      'Content-Type': 'application/json',\n      'X-Shopify-Storefront-Access-Token': config.token,\n    };\n  }\n\n  async query<T>(query: string, variables?: Record<string, any>): Promise<T> {\n    const response = await fetch(this.endpoint, {\n      method: 'POST',\n      headers: this.headers,\n      body: JSON.stringify({ query, variables }),\n    });\n\n    const data = await response.json();\n\n    if (data.errors) {\n      throw new Error(`GraphQL error: ${data.errors[0].message}`);\n    }\n\n    return data.data;\n  }\n\n  async getProduct(handle: string): Promise<Product> {\n    const query = `\n      query GetProduct($handle: String!) {\n        product(handle: $handle) {\n          id\n          title\n          handle\n          description\n          priceRange {\n            minVariantPrice {\n              amount\n              currencyCode\n            }\n            maxVariantPrice {\n              amount\n              currencyCode\n            }\n          }\n          variants(first: 100) {\n            edges {\n              node {\n                id\n                title\n                sku\n                price {\n                  amount\n                  currencyCode\n                }\n                availableForSale\n              }\n            }\n          }\n        }\n      }\n    `;\n\n    const result = await this.query(query, { handle });\n    return result.product;\n  }\n\n  async createCart(variantId: string, quantity: number = 1) {\n    const mutation = `\n      mutation CreateCart($input: CartInput!) {\n        cartCreate(input: $input) {\n          cart {\n            id\n            checkoutUrl\n            lines(first: 10) {\n              edges {\n                node {\n                  id\n                  quantity\n                }\n              }\n            }\n          }\n        }\n      }\n    `;\n\n    const input = {\n      lines: [\n        {\n          merchandiseId: variantId,\n          quantity,\n        },\n      ],\n    };\n\n    return this.query(mutation, { input });\n  }\n}\n\n// Usage\nconst client = new StorefrontClient({\n  shop: 'mystore.myshopify.com',\n  token: 'public_token_here',\n  apiVersion: '2026-01',\n});\n\nconst product = await client.getProduct('wireless-headphones');\nconsole.log(`Product: ${product.title} - $${product.priceRange.minVariantPrice.amount}`);\n\nconst cart = await client.createCart('gid://shopify/ProductVariant/123456', 2);\nconsole.log(`Cart created: ${cart.cart.checkoutUrl}`);\n```\n\n---\n\n## Rate Limiting\n\n**Public token rate limits:**\n- 2 requests per second per IP address\n- 4 requests per second per user token (if customer logged in)\n\n**Private token rate limits:**\n- No rate limiting (server-side access)\n\n**Handling rate limits:**\n```javascript\nasync function executeWithRetry(query, variables, maxRetries = 3) {\n  for (let attempt = 1; attempt <= maxRetries; attempt++) {\n    const response = await fetch(endpoint, {\n      method: 'POST',\n      headers,\n      body: JSON.stringify({ query, variables }),\n    });\n\n    if (response.status === 429) {\n      const waitTime = Math.pow(2, attempt - 1) * 1000;\n      await new Promise(resolve => setTimeout(resolve, waitTime));\n      continue;\n    }\n\n    return response;\n  }\n}\n```\n\n---\n\n## Common Errors & Solutions\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| **Unauthorized** | Invalid or expired token | Verify token in X-Shopify-Storefront-Access-Token header |\n| **Field not available** | Token doesn't have scope | Use private token for full access, or request scope |\n| **Product not found** | Wrong product handle | Verify handle exists; check product published to sales channel |\n| **Cart checkoutUrl null** | Cart hasn't been created properly | Ensure cartCreate mutation completed successfully |\n| **Customer is null** | User not logged in | Authenticate customer first (requires customer token) |\n| **Variant not available** | Out of stock | Check availableForSale and quantityAvailable fields |\n\n---\n\n## Reference URLs\n\n- [Shopify Storefront API Docs](https://shopify.dev/api/storefront/2026-01)\n- [Storefront GraphQL Queries](https://shopify.dev/api/storefront/2026-01/queries)\n- [Storefront GraphQL Mutations](https://shopify.dev/api/storefront/2026-01/mutations)\n- [Customer Account API](https://shopify.dev/api/customer/2026-01)\n- [Market Directives Guide](https://shopify.dev/api/storefront/2026-01/guide-markets)\n- [Cart Operations Guide](https://shopify.dev/api/storefront/2026-01/guide-cart)\n- [Authentication Flows](https://shopify.dev/api/storefront/2026-01/guide-authentication)\n- [Rate Limiting Documentation](https://shopify.dev/api/storefront/2026-01#rate-limits)\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}