Selling Products with Shopify
Shopify's Storefront API exposes your entire product catalog through GraphQL which makes it easy to fetch your products at build time, generate your store pages statically, and let Shopify host the checkout. No backend required.
Manage your products, inventory, and pricing entirely in Shopify. Every time your site builds, ZeroPoint fetches the latest data and regenerates your store. Customers check out on Shopify's fully hosted, PCI-compliant checkout — no payment data touches your ZeroPoint site.
How it works
- Create products in your Shopify store
- At build time, ZeroPoint fetches your product catalog from the Storefront API
- Your store pages are generated as static HTML — no runtime server needed
- Customers click "Buy Now" and are taken directly to Shopify's hosted checkout
Steps
-
Create your products in Shopify
In your Shopify Admin, go to Products and create a product for each item you want to sell. Give each product a title, description, images, and price. Shopify automatically generates a URL-safe handle from the product title — this becomes the slug for your product pages.
-
Get a Storefront API access token
The Storefront API uses a public access token — it's designed to be used in front-end code and only exposes data your store makes publicly available.
- In your Shopify Admin, go to Apps → Develop apps
- Click Create an app, give it a name (e.g. "ZeroPoint")
- Click Configure Storefront API scopes and enable
unauthenticated_read_product_listingsandunauthenticated_read_product_inventory - Click Install app, then copy the Storefront API access token
Store both your token and your store domain as environment variables in your deployment platform:
- Netlify: Site configuration → Environment variables → Add variable
- Cloudflare Pages: Settings → Environment variables → Add variable
- GitHub Actions: Repository settings → Secrets and variables → Actions → New repository secret
For local development, add both to a
.envfile at the root of your project (ensure.envis in your.gitignore):SHOPIFY_STORE_DOMAIN=your-store.myshopify.com SHOPIFY_STOREFRONT_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX -
Create a Shopify data file
No npm package required — the Storefront API is a standard GraphQL endpoint over HTTPS, so native
fetchis all you need. Createsrc/data/shopify.jsto fetch your products at build time:src/data/shopify.jsexport default async function () { const query = `{ products(first: 50, sortKey: TITLE) { edges { node { title handle description priceRange { minVariantPrice { amount currencyCode } } featuredImage { url altText } variants(first: 1) { edges { node { id } } } } } } }`; const response = await fetch( `https://${process.env.SHOPIFY_STORE_DOMAIN}/api/2025-01/graphql.json`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Shopify-Storefront-Access-Token': process.env.SHOPIFY_STOREFRONT_TOKEN, }, body: JSON.stringify({ query }), } ); const { data } = await response.json(); return data.products.edges.map(({ node }) => { // Variant IDs from Shopify are global IDs like "gid://shopify/ProductVariant/123" // We need just the numeric portion for the checkout URL const variantGid = node.variants.edges[0]?.node?.id ?? ''; const variantId = variantGid.split('/').pop(); const amount = parseFloat(node.priceRange.minVariantPrice.amount); const currency = node.priceRange.minVariantPrice.currencyCode; return { name: node.title, slug: node.handle, // Shopify handles are already URL-safe slugs description: node.description, image: node.featuredImage?.url ?? null, imageAlt: node.featuredImage?.altText ?? node.title, price: new Intl.NumberFormat('en-US', { style: 'currency', currency, }).format(amount), // Direct single-product checkout URL — no backend or cart needed checkoutUrl: `https://${process.env.SHOPIFY_STORE_DOMAIN}/cart/${variantId}:1/checkout`, }; }); }The
shopifydata is now available to all your ZeroPoint templates. Note thatSHOPIFY_STORE_DOMAINis used at build time only — it does not appear in the generated HTML (the checkout URL is baked in as a plain string). -
Create the store listing page
Create
src/content/pages/store.njkto render a grid of product cards, each linking to its own detail page:src/content/pages/store.njk--- title: Store permalink: /store/ --- <h1>Store</h1> <div class="product-grid"> {% for product in shopify %} <article class="product-card"> {% if product.image %} <a href="/store/{{ product.slug }}/"> <img src="{{ product.image }}" alt="{{ product.imageAlt }}"> </a> {% endif %} <h2><a href="/store/{{ product.slug }}/">{{ product.name }}</a></h2> <p class="product-price">{{ product.price }}</p> <a href="/store/{{ product.slug }}/" class="button button-secondary">View Product</a> </article> {% endfor %} </div> -
Generate individual product pages
Pagination can generate one page per item in a dataset. Create
src/content/pages/store-product.njk— ZeroPoint will output a page at/store/your-product-handle/for every product in your Shopify catalog:src/content/pages/store-product.njk--- pagination: data: shopify size: 1 alias: product permalink: /store/{{ product.slug }}/ eleventyExcludeFromCollections: true --- <nav class="breadcrumb"> <a href="/store/">← Back to Store</a> </nav> <article class="product-single"> {% if product.image %} <div class="product-image"> <img src="{{ product.image }}" alt="{{ product.imageAlt }}"> </div> {% endif %} <div class="product-details"> <h1>{{ product.name }}</h1> {% if product.description %} <div class="product-description"> {{ product.description | markdown | safe }} </div> {% endif %} <p class="product-price">{{ product.price }}</p> <a href="{{ product.checkoutUrl }}" class="button button-primary"> Buy Now → </a> </div> </article>Add a product in Shopify, trigger a build, and it gets a card on
/store/and its own page at/store/its-handle/. Archive a product in Shopify and both disappear on the next build.
Going Further
- 🛒 Cart API — use the Storefront API's cart mutations client-side to build a multi-product cart that still checks out on Shopify. Requires JavaScript but no backend.
- 🏷️ Shopify Tax — automatic tax calculation handled entirely by Shopify at checkout. No configuration on your side.
- 🌍 Shopify Markets — sell internationally with local currencies, languages, and pricing. Configured in the Shopify Dashboard.
- 🔔 Webhooks — receive events after a purchase to trigger fulfillment or notifications. Handle them with a Netlify or Cloudflare Pages Function.
- 👤 Customer Accounts — Shopify's hosted account portal for order history and saved addresses. No code required.