Nuxt Shopify

repository·main·Indexed 41 days ago

https://github.com/nuxt-modules/shopify

A Nuxt integration for Shopify's Storefront, Customer Account, and Admin APIs. It provides typed fetch clients, automatic GraphQL type generation, fragment deduplication, and built-in support for proxying, mocking, and headless analytics. The module includes composables like useStorefrontData and useCustomerAccount, as well as Nitro server utilities for secure API access and webhook subscription management.

Tokens
54.7K
Snippets
153
Records
174
Agent score
48%

What's inside Nuxt Shopify

  1. Overview of Nuxt Shopify

    main
    Nuxt Shopify is a Nuxt module designed to integrate Shopify's e-commerce capabilities into Nuxt applications. It provides seamless access to Shopify's APIs while automating much of the developer experience, such as TypeScript type generation from GraphQL operations, hot-reloading of types, and automatic GraphQL fragment injection. It supports both server-side and client-side execution, often using a Nitro server-side proxy to handle API requests securely.
  2. Use GraphQL fragments with automatic injection

    main

    The module automatically scans your project for GraphQL fragment definitions and injects them into any operation that references them. This eliminates the need to manually import fragments into your queries or mutations.

    Organizing Fragments

    It is recommended to store fragments in a graphql directory at your project root. By default, files in this directory are scanned for code generation and auto-imported.

    API-Specific File Organization

    To ensure fragments are associated with the correct API, follow these naming or directory conventions:

    • Storefront API: Evaluated by default anywhere in the codebase.
    • Admin API: Place in an admin subdirectory or use the *.admin.{gql,graphql,ts,js} suffix.
    • Customer Account API: Place in customer-account, customer, or account subdirectories, or use suffixes like *.customer-account.{gql,graphql,ts,js,vue}, *.customer.{gql,graphql,ts,js,vue}, or *.account.{gql,graphql,ts,js,vue}.

    Configuration

    • graphql.injectFragments: Set to false to disable build-time fragment injection.
    • graphql.normalize: Set to false to disable stripping of duplicate and unused fragment definitions before requests.
    export const PRODUCT_FRAGMENT = `#graphql
      fragment ProductFields on Product {
        id
        title
        description
      }
    `
    export default defineEventHandler(async () => {
      const admin = useAdmin()
    
      const { data } = await admin.request(`#graphql
        query GetMarkets {
          markets(first: 3) {
            nodes {
              ...MarketFields
            }
          }
        }
      `)
    
      return flattenConnection(data?.markets)
    })
  3. Fetch product and resolve variants using selectedOrFirstAvailableVariant

    main

    Instead of downloading all possible variants and matching them client-side, use the selectedOrFirstAvailableVariant GraphQL field. This field accepts a selectedOptions argument of type [SelectedOptionInput!], which is a list of { name, value } pairs.

    This approach allows Shopify to return the exact variant the customer is looking for (or the first available one if no match is found) in a single request.

    GraphQL Query Pattern

    product(handle: $handle) {
      title
      description
      featuredImage {
        ...ImageFields
      }
      options {
        ...ProductOptionFields
      }
      selectedOrFirstAvailableVariant(selectedOptions: $selectedOptions) {
        ...ProductVariantFields
      }
    }
    product(handle: $handle) {
      title
      description
    
      featuredImage {
        ...ImageFields
      }
    
      options {
        ...ProductOptionFields
      }
    
      selectedOrFirstAvailableVariant(selectedOptions: $selectedOptions) {
        ...ProductVariantFields
      }
    }
  4. Configure GraphQL type generation

    main

    The module automatically generates GraphQL types and schema files in the .nuxt directory:

    • .nuxt/shopify/<client>: Type definitions
    • .nuxt/shopify/schema: GraphQL schema files

    To enable IDE support, the module generates a .nuxt/graphql.config.mjs file. You can export this in your project root to point your editor's GraphQL plugin to it:

    // ~/graphql.config.mjs
    export { default } from "./.nuxt/graphql.config.mjs"

    You can disable this generation by setting graphql.generateConfig: false in the module config.

  5. Configure API Versions for Clients

    main

    Every API client (Storefront, Customer Account, or Admin) can have a specific apiVersion pinned. If left unset, the module automatically uses the current stable version available at build time. Pinning a version ensures that your codebase only upgrades when you explicitly change the configuration during a rebuild.

    export default defineNuxtConfig({
      shopify: {
        name: 'nuxt-module-store',
        clients: {
          storefront: {
            apiVersion: '2026-04',
            publicAccessToken: '...',
          },
        },
      },
    })
  6. Route GraphQL operations to specific APIs (Admin vs Storefront)

    main

    The module determines which client (Storefront, Admin, or Customer Account) an operation belongs to based on file location and naming conventions defined by the documents glob patterns.

    Default Routing Rules:

    • Storefront: The default. Any GraphQL found in the app is treated as a Storefront operation unless scoped otherwise.
    • Admin: Matches files in an admin/ directory or files named *.admin.{ts,js,gql,graphql}.
    • Customer Account: Matches files in customer-account/, customer/, or account/ directories, or files named *.customer-account.*, *.customer.*, or *.account.*.

    To keep shared documents organized and correctly routed, it is recommended to place them under graphql/<client>/ in your project root.

  7. How GraphQL Codegen works in Nuxt Shopify

    main

    The module runs a pipeline during nuxt prepare, nuxt dev, and nuxt build that triggers whenever a file containing GraphQL changes. The pipeline consists of four steps:

    1. Introspect the schema: The module introspects your store's API and writes a minified schema to .nuxt/shopify/schema/<client>.<apiVersion>.schema.json.
    2. Generate base types: It generates <client>.types.d.ts containing all API objects, inputs, enums, and scalars.
    3. Scan operations: It scans files matched by the documents glob patterns and generates <client>.operations.d.ts, creating precise types for every query, mutation, and fragment (e.g., GetProductQuery, ProductFieldsFragment).
    4. Wire up request(): The operations file registers each operation into the client's type map. When you call client.request() with a #graphql string, TypeScript performs a lookup to provide the correct variable and return types.

    Generated types are available globally if codegen.autoImport is enabled (default for storefront) or via #shopify/<client> aliases.

  8. Access resolved Shopify configuration via runtimeConfig

    main

    The module parses your configuration on startup and provides the resolved results through Nuxt's useRuntimeConfig(). There are three ways to access the configuration depending on your needs and environment:

    • useRuntimeConfig()._shopify: The full resolved configuration. This is typed as ShopifyConfig and contains sensitive credentials. Use this only on the server side; it must never reach the browser.
    • useRuntimeConfig().public._shopify: A filtered copy of the resolved configuration that is safe to use in the browser. It is typed as PublicShopifyConfig.
    • useRuntimeConfig().shopify: The raw, unresolved configuration input provided in your config files.
  9. Manage applied filters via URL query parameters

    main

    A common pattern for collection filters is to sync the active filters with the browser's URL query parameters. This allows users to share filtered views and use the browser's back/forward buttons.

    Implementation Pattern

    1. State Management: Use a computed property with a get and set to manage the applied filters.
    2. Serialization: Since URL parameters are strings, use JSON.stringify() to store the ProductFilter[] array in a single query parameter (e.g., ?filters=[...]) and JSON.parse() to retrieve it.
    3. Reactivity: Use useStorefrontData with a watch on the route query to trigger new API requests whenever the URL changes.
    import type { ProductFilter } from '#shopify/storefront'
    
    const route = useRoute()
    const router = useRouter()
    
    // Sync filters with URL
    const applied = computed<ProductFilter[]>({
      get: () => route.query.filters ? JSON.parse(route.query.filters as string) : [],
      set: filters => router.push({ query: filters.length ? { filters: JSON.stringify(filters) } : {} }),
    })
    
    // Fetch data reacting to URL changes
    const { data: collection } = await useStorefrontData(`collection-${route.params.handle}`, `...query...`, {
      variables: computed(() => ({
        handle: route.params.handle,
        first: 12,
        filters: applied.value,
      })),
      transform: data => data.collection,
      watch: [() => route.query.filters],
    })
    const applied = computed<ProductFilter[]>({
      get: () => route.query.filters ? JSON.parse(route.query.filters as string) : [],
      set: filters => router.push({ query: filters.length ? { filters: JSON.stringify(filters) } : {} }),
    })
  10. Manage Shopify Cart state and mutations

    main

    A Shopify cart is server-side state identified by a client-side handle (Cart ID). To maintain a persistent cart across page reloads, you should store this ID in a cookie.

    In this implementation, a useCart composable is used to manage the cart lifecycle. It provides reactive state for cart lines, total quantity, total cost, and the checkout URL, along with methods to perform mutations. Every mutation returns the updated cart object, ensuring the client-side state stays in sync with Shopify.

    const { 
      lines, 
      quantity, 
      total, 
      checkoutUrl, 
      loading, 
      get, 
      add, 
      update, 
      remove 
    } = useCart()
  11. Core API Capabilities of Nuxt Shopify

    main

    The module provides specialized support for three primary Shopify APIs:

    • Storefront API: Used to query your Shopify store for products, collections, and other public data.
    • Customer Account API: Used to manage customer-specific data and authentication.
    • Admin API: Used for backend management of your Shopify store.

    Additionally, the module supports advanced features like Webhooks (with automatic validation), Analytics (with built-in consent handling via the Customer Privacy API), and Caching for both client and server-side responses.

  12. Localize cart operations with @inContext

    main

    When serving multiple markets, cart prices must follow the buyer's context. To ensure correct pricing, add the @inContext directive to your GraphQL mutations (like cartLinesAdd or cartLinesUpdate) using the language and country variables.

    Warning: A cart is created in a specific context and retains it. If a user changes their country, the existing cart lines will not re-price automatically. You should create a new cart when the shopper changes their country.

    mutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!, $language: LanguageCode, $country: CountryCode)
    @inContext(language: $language, country: $country) {
      cartLinesAdd(cartId: $cartId, lines: $lines) {
        cart {
          ...CartFields
        }
      }
    }