Getting Started

Usage

Using the Nuxt Shopify module

To quickly get started building your Shopify-powered Nuxt application, you can use the useStorefront, useCustomerAccount and useAdmin composables provided by the module. On the client side, you can also use the useStorefrontData and useCustomerAccountData composables for direct integration with Nuxt's async data fetching.

Depending on the module configuration, you will have access to the Storefront API, the Customer Account API, the Admin API, or any combination of them. The composables are automatically provided when configured correctly.

See the module configuration reference for more details on how to configure the module.

Storefront API

First, ensure you have configured the module to use the Storefront API client in your nuxt.config.ts file:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/shopify'],

  shopify: {
    name: 'nuxt-module-store',

    clients: {
      storefront: {
        apiVersion: '2026-04',
        publicAccessToken: '...',
      },
    },
  },
})

Adding the publicAccessToken will enable the Storefront API client on the server and client side. If you only need access through Nitro you can set the privateAccessToken instead. When setting both, the publicAccessToken will be used on the client side and the privateAccessToken on the server side. When using the mock option, the client will be available on both server and client side with mock data.

See the Storefront API guide for more details on how to configure and use the Storefront API client.

Client side

Once configured, you can use the useStorefront and useStorefrontData composables to interact with the Storefront API.

~/app/pages/product.vue
<script setup lang="ts">
const storefront = useStorefront()

const { data } = await storefront.request(`#graphql
  query GetProduct($handle: String!) {
    product(handle: $handle) {
      id
      title
      description
    }
  }
`, {
  variables: {
    handle: 'high-top-sneakers',
  }
})
</script>

<template>
  <div v-if="data">
    <h1>{{ data.product.title }}</h1>
    <p>{{ data.product.description }}</p>
  </div>
</template>

Server side

On the server side, you can use the useStorefront composable in server routes, server middleware, or API routes.

~/server/api/product/[handle].ts
export default defineEventHandler(async (event) => {
  const { handle } = getQuery(event)

  const storefront = useStorefront()

  const { data } = await storefront.request(`#graphql
    query GetProduct($handle: String!) {
      product(handle: $handle) {
        id
        title
        description
      }
    }
  `, {
    variables: {
      handle,
    }
  })

  return data?.product
})

Customer Account API

The Customer Account API provides functionality for managing customer accounts, including authentication, registration, and account management. To use the Customer Account API, you need to configure the module with the appropriate credentials in your nuxt.config.ts file:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/shopify'],

  shopify: {
    name: 'nuxt-module-store',

    clients: {
      customerAccount: {
        apiVersion: '2026-04',
        clientId: '...',
      },
    },
  },
})
Both public clients and confidential clients (by also providing a clientSecret) are supported for the Customer Account API. See the Customer Account API guide for more details on how to configure and use the Customer Account API client.

Client side

Once configured, you can use the useCustomerAccount composable to interact with the Customer Account API.

~/app/pages/account.vue
<script setup lang="ts">
const customerAccount = useCustomerAccount()

const { data } = await customerAccount.request(`#graphql
  query GetCustomer {
    customer {
      id
      firstName
      lastName
    }
  }
`)
</script>

<template>
  <div v-if="data?.customer">
    <h1>Welcome, {{ data.customer.firstName }} {{ data.customer.lastName }}</h1>
  </div>
</template>

Server side

On the server side, you can use the useCustomerAccount composable in server routes, server middleware, or API routes.

~/server/api/account.ts
export default defineEventHandler(async (event) => {
  const customerAccount = useCustomerAccount(event)

  const { data } = await customerAccount.request(`#graphql
    query GetCustomer {
      customer {
        id
        firstName
        lastName
      }
    }
  `)

  return data?.customer
})

Admin API

First, ensure you have configured the module to use the Admin API client in your nuxt.config.ts file:

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/shopify'],

  shopify: {
    name: 'nuxt-module-store',

    clients: {
      admin: {
        apiVersion: '2026-04',
        clientId: '...',
        clientSecret: '...',
      },
    },
  },
})

Adding clientId and clientSecret will enable the Admin API client on the server side only. Using the Admin API on the client side is not supported due to security reasons. If you want to use admin functionality on the client side, you need to create server endpoints that use the Admin API client and call them from the client side.

See the Admin API guide for more details on how to configure and use the Admin API client.

Server side

Once configured, you can use the useAdmin composable within your Nitro endpoints.

~/server/api/products.ts
export default defineEventHandler(async (event) => {
  const admin = useAdmin()

  const { data: product } = await admin.request(`#graphql
    query GetProduct($handle: String!) {
      product(handle: $handle) {
        id
        title
        description
      }
    }
  `, {
    variables: {
      handle: 'high-top-sneakers',
    }
  })

  return product
})

Outside of Nuxt

The composables above resolve their configuration from the runtime config, so they only work inside a Nuxt app. For Node scripts that run outside the Nuxt runtime the module also exports the client factories directly:

scripts/export-products.ts
import { createAdminClient } from '@nuxtjs/shopify'

const admin = createAdminClient({
  name: 'nuxt-module-store',
  clients: {
    admin: {
      clientId: process.env.SHOPIFY_CLIENT_ID!,
      clientSecret: process.env.SHOPIFY_CLIENT_SECRET!,
    },
  },
})

const { data } = await admin.request(`#graphql
  query GetProducts {
    products(first: 10) {
      nodes {
        id
        title
      }
    }
  }
`)

console.log(flattenConnection(data?.products))

createStorefrontClient, createCustomerAccountClient and createShopifyClient are exported the same way. Access tokens are resolved exactly as they are inside Nuxt, so an admin client configured with a client ID and secret performs the OAuth token exchange for you.

These factories are meant for usage inside Node. Within your app, keep using the auto-imported composables, as they wire up proxying, caching, tracking, hooks and more.

Fragments

When using the Shopify GraphQL APIs, it's common to reuse fragments across multiple queries and mutations. The module scans your project for fragment definitions and injects every fragment an operation references directly into the operation, so you never have to import a fragment into a query yourself. Injection follows nested spreads, and a fragment the operation already defines is never added twice.

It is recommended to organize your fragments in a graphql directory in your project root. fragments.dirs is resolved against the project root. Files within this directory will be scanned for code generation and auto-imported by default.

Anywhere in the codebase, including the graphql directory, files for the Storefront API will be evaluated by default. Files for the Admin API need to be placed in an admin subdirectory or use the file ending *.admin.{gql,graphql,ts,js}. Files for the Customer Account API need to be placed in a customer-account, customer or account subdirectory or use the file ending *.customer-account.{gql,graphql,ts,js,vue}, *.customer.{gql,graphql,ts,js,vue}, or *.account.{gql,graphql,ts,js,vue}.

Fragments are injected at build time, which you can disable with graphql.injectFragments. Before a request is sent, duplicate and unused fragment definitions are stripped from the operation, which you can disable with graphql.normalize. See the configuration reference.
app/pages/product.vue
<script setup lang="ts">
const props = defineProps<{
  handle: string
}>()

const key = computed(() => `product-${props.handle}`)

const { data: products } = await useStorefrontData(key, `#graphql
  query GetProduct($handle: String!) {
    product(handle: $handle) {
      ...ProductFields
    }
  }
`, {
  variables: props,
})
</script>

<template>
  <div
    v-for="product in products"
    :key="product.id"
  >
    <h2>{{ product.title }}</h2>
    <p>{{ product.description }}</p>
  </div>
</template>

Generated Types

The code generation process will automatically create TypeScript types for your GraphQL queries, mutations and fragments. These types will be based on the schema defined in your Shopify store and will be available for use throughout your application.

Generated types will be placed in the ./.nuxt/shopify/storefront, ./.nuxt/shopify/customer-account, and ./.nuxt/shopify/admin directories, and are reachable through the #shopify/storefront, #shopify/customer-account and #shopify/admin aliases. When for example using the useStorefrontData composable, the types will be inferred automatically. When you want to be able to pass objects from a client response to another component or function, use the generated type by name, for the Storefront API these are available globally (see Auto-Imports):

app/components/ProductCard.vue
<script setup lang="ts">
defineProps<{
  product: ProductFieldsFragment
}>()
</script>

<template>
  <div>
    <h2>{{ product.title }}</h2>
    <p>{{ product.description }}</p>
  </div>
</template>

Auto-Imports

Types generated from your own operations, their type representations of queries, mutations and fragments are declared globally, so you can use them by name without an import:

app/components/ProductCard.vue
<script setup lang="ts">
defineProps<{
  product: ProductFieldsFragment
}>()
</script>

A query named GetProduct gives you GetProductQuery and GetProductQueryVariables, a fragment named ProductFields gives you ProductFieldsFragment. This is on by default for the Storefront API and opt-in for the others:

nuxt.config.ts
export default defineNuxtConfig({
  shopify: {
    clients: {
      storefront: {
        codegen: { autoImport: true }, // default: true
      },
      customerAccount: {
        codegen: { autoImport: true }, // default: false
      },
      admin: {
        codegen: { autoImport: true }, // default: false
      },
    },
  },
})

Schema types are not global. A Shopify schema declares several thousand type names, including Image, Location, Order and Page, which would shadow built-in DOM types and your own. Import those from the client alias instead:

import type { Product, MoneyV2 } from '#shopify/storefront'

The aliases #shopify/storefront, #shopify/customer-account and #shopify/admin also export every operation type, so you can always import a name explicitly even when auto-import is enabled.

Enabling autoImport for more than one client puts all of their operation types in the same global namespace. If two clients declare an operation with the same name, import it from the alias instead.

Separately, the contents of your fragment directories are auto-imported, so a fragment constant can be used in any query without importing it:

nuxt.config.ts
export default defineNuxtConfig({
  shopify: {
    fragments: {
      dirs: ['graphql'], // resolved from the project root
      autoImport: true, // default: true
    },
  },
})

The helpers flattenConnection and parseGid are auto-imported on both the client and the server.

Utilities

Alongside the clients, the module auto-imports a few helpers for working with Shopify responses. They are available on both the client and the server.

flattenConnection

Shopify returns paginated data as a connection with edges and nodes. flattenConnection turns either shape into a plain array, so you do not have to map over edges yourself:

app/pages/products.vue
<script setup lang="ts">
const { data } = await useStorefrontData('products', `#graphql
  query GetProducts {
    products(first: 10) {
      nodes {
        id
        title
      }
    }
  }
`)

const products = computed(() => flattenConnection(data.value?.products))
</script>

It returns an empty array when the connection is null or undefined, so the result is always safe to iterate.

parseGid

Shopify identifies every record with a global ID such as gid://shopify/Product/1234567890. parseGid extracts the numeric ID from it, which is useful for URLs, analytics payloads, or third-party systems:

const id = parseGid('gid://shopify/Product/1234567890') // '1234567890'
parseGid throws when the value is not a valid global ID. Guard the call if the ID may be missing or user-supplied.
Published under the MIT License