Knowledge Hub
For manager
Headless CMSFor developer
Web FrameworksStatic Site GeneratorsServerless DatabasesMonoreposHostingPIMLearn
GuidesTutorialsAdopt
Case studiesThe Shopify Storefront API allows your custom frontend to talk with Shopify’s backend. You can make API calls to Shopify and access product data and other services like the checkout page.
Reading this article, you will learn how to build a Shopify storefront using the Storefront API and Next.js. By the end, you should be able to send GraphQL requests to Shopify and use the response data in your front end.
Let’s get started!
Navigate to Shopify Partners and create your Shopify Partner account (if you don't have one already). The account will allow you to create a store for development purposes at no cost.
Click Stores on the Shopify Partners dashboard to add a store. Ensure you select Development Store as the store type, then fill in the store details.
Once you create the store, you are ready to create a private app.
Create a private app by following the steps below:
Once you’ve created the app, follow these steps to configure the Storefront API.
To get the access token:
Copy the access token and the shop URL in the .env file.
The shop URL follows this format.
https://{store_name}.myshopify.com/api/2022-10/graphql.json
The last step of setting up Shopify is to add sample products to the store.
Start by running the following command in the terminal to create a Next.js project.
npx create-next-app@latest
This command should prompt you as follows:
√ What is your project named? ... nextjs-shopify
√ Would you like to use TypeScript with this project? ... No / Yes
√ Would you like to use ESLint with this project? ... No / Yes
Name the project shopify-store and select “No” for TypeScript and “Yes” for EsLint.
Now, run the app using the following command.
npm run dev
To make GraphQL requests, use graphql-request, a minimal client for sending GraphQL requests.
Run the following command on the terminal to install it.
npm install graphql-request
To fetch the products from Shopify and manage the cart, you’ll need queries that do the following:
Let’s create these queries.
In the base folder of your app, create a new folder called utils and add a new file named shopify.js.
Open the utils/shopify.js file in your editor and add the code which imports GraphQLClient and gql from graphql-request.
import { gql, GraphQLClient } from "graphql-request";
Assign the Storefront access token and the shop URL to a storefrontAccessToken and endpoint variable.
const storefrontAccessToken = process.env.STOREFRONTACCESSTOKEN;
const endpoint = process.env.SHOPURL
Add the following code to create a GraphQL client instance. You’ll use it to make requests.
const graphQLClient = new GraphQLClient(endpoint, {
headers: {
"X-Shopify-Storefront-Access-Token": storefrontAccessToken,
},
});
Create a new function called getProducts and define the query for fetching products from Shopify.
export async function getProducts() {
const getAllProductsQuery = gql`
{
products(first: 10) {
edges {
node {
id
title
handle
priceRange {
minVariantPrice {
amount
}
}
featuredImage {
altText
url
}
}
}
}
}
`;
}
Then, use the query to make the API request.
export async function getProducts() {
const getAllProductsQuery = gql`
// Omitted for brevity
`;
try {
return await graphQLClient.request(getAllProductsQuery);
} catch (error) {
throw new Error(error);
}
}
You can now call the getProducts function to fetch products from Shopify. Next, you’ll create a function that fetches a single product by handle.
In utils/shopify.js, add a new function called getProduct that accepts a product ID as an argument.
export const getProduct = async (id) => {
};
Modify the function to include the query for fetching the product.
export const getProduct = async (id) => {
const productQuery = gql`
query getProduct($id: ID!) {
product(id: $id) {
id
handle
title
description
priceRange {
minVariantPrice {
amount
currencyCode
}
}
featuredImage {
url
altText
}
variants(first: 10) {
edges {
node {
id
}
}
}
}
}
`;
};
In getProduct, use the query in the GraphQL request and pass the ID as a variable.
export const getProduct = async (id) => {
const productQuery = gql`
// Omitted for brevity
`;
const variables = {
id,
};
try {
const data = await queryShopify(productQuery, variables);
return data.product;
} catch (error) {
throw new Error(error);
}
};
Next, you’ll create a function that creates a cart and adds items to the cart.
In utils/shopify.js, add a new function named addToCart, that accepts a product ID and quantity as arguments.
Next, define the mutation for creating a cart.
export async function addToCart(itemId, quantity) {
const createCartMutation = gql`
mutation createCart($cartInput: CartInput) {
cartCreate(input: $cartInput) {
cart {
id
}
}
}
`;
}
Use the mutation to create a cart.
export async function addToCart(itemId, quantity) {
const createCartMutation = gql`
// Omitted for brevity
`
const variables = {
cartInput: {
lines: [
{
quantity: parseInt(quantity),
merchandiseId: itemId,
},
],
},
};
try {
return await graphQLClient.request(createCartMutation, variables);
} catch (error) {
throw new Error(error);
}
}
This function creates a new cart and adds a product to it. Next, you’ll create a function that adds a product to an existing cart.
Add a function named updateCart in utils/shopify.js and define the mutation for updating the cart.
export async function updateCart(cartId, itemId, quantity) {
const updateCartMutation = gql`
mutation cartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
}
}
}
`;
}
This mutation is used to find the cart with the specified cart ID and update it with the item.
Modify the updateCart function to make the request as follows:
export async function updateCart(cartId, itemId, quantity) {
const updateCartMutation = gql`
// Omitted for brevity
}
`;
const variables = {
cartId: cartId,
lines: [
{
quantity: parseInt(quantity),
merchandiseId: itemId,
},
];
};
try {
return await graphQLClient.request(updateCartMutation, variables);
} catch (error) {
throw new Error(error);
}
}
After creating or updating the cart, you’ll need to retrieve it
In utils/shopify, add a function named retrieveCart and add a query for retrieving the cart.
export async function retrieveCart(cartId) {
const cartQuery = gql`
query cartQuery($cartId: ID!) {
cart(id: $cartId) {
id
createdAt
updatedAt
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
}
}
}
}
}
estimatedCost {
totalAmount {
amount
}
}
}
}
`;
}
This query uses the cart ID to query for IDs of the products in the cart and the total estimated cost. Add the following to the retrieveCart function to make the request.
export async function retrieveCart(cartId) {
const cartQuery = gql`
// Omitted for brevity
`;
const variables = {
cartId,
};
try {
const data = await graphQLClient.request(cartQuery, variables);
return data.cart;
} catch (error) {
throw new Error(error);
}
}
Next, you’ll create a function that retrieves the checkout URL.
Add a function named retrieveCheckout that receives a cart ID as an argument in utils/shopify.js. Then, add the query for fetching the checkout URL from Shopify.
export const getCheckoutUrl = async (cartId) => {
const getCheckoutUrlQuery = gql`
query checkoutURL($cartId: ID!) {
cart(id: $cartId) {
checkoutUrl
}
}
`;
const variables = {
cartId: cartId,
};
};
Add the following try/catch statement to make the request and return the checkout URL.
export const getCheckoutUrl = async (cartId) => {
const getCheckoutUrlQuery = gql`
query checkoutURL($cartId: ID!) {
cart(id: $cartId) {
checkoutUrl
}
}
`;
const variables = {
cartId: cartId,
};
try {
return await graphQLClient.request(getCheckoutUrlQuery, variables);
} catch (error) {
throw new Error(error);
}
};
You have already created all the functions you’ll use to get products and manage the cart. The next step is to create the frontend, beginning with the products page.
The products page displays all the products and their prices.
To get started, export the getServerSideProps function in pages/index.js and call the getProducts function from utils/shopify.js.
import { getProducts } from "../utils/shopify";
export default function Home({ products }) {
return (
// Display products
);
}
export const getServerSideProps = async () => {
const data = await getProducts();
return {
props: { data },
};
};
};
This function passes the products as props into the page component.
Add the following code to the page component to iterate over and display each product.
import Head from "next/head";
import styles from "../styles/Home.module.css";
import { getProducts } from "../utils/shopify";
import ProductCard from "../components/ProductCard/ProductCard";
export default function Home({ data }) {
const products = data.products.edges;
return (
<>
<Head>
<title>Nextjs Shopify</title>
<meta name="description" content="Generated by create next app" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<div className={styles.products}>
{products.map((product) => {
return <ProductCard key={product.node.id} product={product} />;
})}
</div>
</main>
</>
);
}
Because you’re rendering the ProductCard component for each product, you need to create it.
In the base folder, create a new folder called components and add the ProductCard/ProductCard.js file.
Add the following code that renders the product to this file.
import Image from 'next/image'
import Link from 'next/link'
import styles from "./ProductCard.module.css"
export default function ProductCard({product}) {
return (
<>
<div className={styles.card}>
<div className={styles.image}>
<Image src={product.node.featuredImage.url} alt={product.node.featuredImage.alttext} fill={true} />
</div>
<div className={styles.content}>
<small>
<Link href={`products/${product.node.handle}/?id=${product.node.id}`} className={styles.action}>{product.node.title}</Link>
</small>
<small>{product.node.priceRange.minVariantPrice.amount}</small>
</div>
</div>
</>
)
}
Note that the product title links to a product details page that you’ll create in the next step.
For brevity, all the CSS files are in the GitHub repo.
Create products/[handle].js file in the pages folder. The file will fetch the product details by the ID passed in the URL query.
Start by exporting the getServerSideProps function in products/[id].js file and fetch the product by passing context.query.productid to the getProduct function.
import ProductDetails from "../../components/ProductDetails/ProductDetails";
import { getProduct } from "../../utils/shopify";
export default function Product({ product }) {
return <ProductDetails product={product} />;
}
export const getServerSideProps = async (context) => {
const { productid} = context.query;
const product = await getProduct(productid);
return {
props: {
product,
},
};
};
The Product page uses a ProductDetails component, so create components/ProductDetails/ProductDetails.js file and add the following:
import Image from "next/image";
import Link from "next/link";
import { useState } from "react";
import styles from "./ProductDetails.module.css";
export default function ProductDetails({ product }) {
const [quantity, setQuantity] = useState(0);
const updateQuantity = (e) => {
setQuantity(e.target.value);
if (quantity == 0) setCheckout(false);
};
return (
<>
<div className={styles.container}>
<div className={styles.image}>
<Image
src={product.featuredImage.url}
alt={product.featuredImage.altText}
fill={true}
/>
</div>
<div className={styles.content}>
<span>
<h2>{product.title}</h2>
<h3>{product.priceRange.minVariantPrice.amount}</h3>
</span>
<input
value={quantity}
onChange={updateQuantity}
type="number"
min={0}
/>
<button className={styles.cartbtn} onClick={handleAddToCart}>
Add to Cart
</button>
</div>
</div>
</>
);
}
This component displays the product image, its title, and price. It also allows a user to specify the number of items they want to buy.
The “Add to Cart” button calls a function called handleAddToCart when clicked. Implement it in the ProductDetails component as follows.
const handleAddToCart = async () => {
let cartId = sessionStorage.getItem("cartId");
if (quantity > 0) {
if (cartId) {
await updateCart(cartId, product.variants.edges[0].node.id, quantity);
} else {
let data = await addToCart(product.variants.edges[0].node.id, quantity);
cartId = data.cartCreate.cart.id;
sessionStorage.setItem("cartId", cartId);
}
}
};
If the quantity of items is greater than 0 but there is no cart in the session, this function should create a cart and add the item to it. If a cart does exist, it should update the cart with the item.
In the next section, you will list the cart items and the total then create a checkout button.
In the pages folder, create a file named cart.js and export a getServerSideProps function that calls the retrieveCart function from utils/shopify.js.
import { retrieveCart, getCheckoutUrl } from "../utils/shopify";
export default function Cart({ products, checkoutUrl}) {
return (
<div>
// Display cart items
</div>
);
}
export const getServerSideProps = async (context) => {
const { cartid } = context.query;
const cart = await retrieveCart(cartid);
const data = await getCheckoutUrl(cartid);
const { checkoutUrl } = data.cart;
return {
props: {
cart,
checkoutUrl,
},
};
};
Call the getCheckoutUrl function to get the checkout URL of the cart.
In the Cart component, iterate over the cart items and render them. Additionally, display the total amount and the checkout button.
import Image from "next/image";
import { getCheckoutUrl, retrieveCart } from "../utils/shopify";
import styles from "../styles/Cart.module.css";
export default function Cart({ cart, checkoutUrl }) {
return (
<div className={styles.container}>
<ul role="list-item">
{cart.lines.edges.map((item) => {
return (
<li key={item.node.id}>
<div>
<Image
src={item.node.merchandise.product.featuredImage.url}
alt={item.node.merchandise.product.featuredImage.altText}
width={200}
height={240}
/>
</div>
<div className={styles.content}>
<h2>{item.node.merchandise.product.title}</h2>
<p>
{
item.node.merchandise.product.priceRange.minVariantPrice
.amount
}
</p>
<p>Quantity: {item.node.quantity}</p>
</div>
</li>
);
})}
</ul>
<h2>Total - {cart.estimatedCost.totalAmount.amount}</h2>
<a href={checkoutUrl}>
<button>Checkout</button>
</a>
</div>
);
}
You now have a working cart!
So far, you have fetched products from Shopify, created a cart for users to add products to, and enabled checkout.
You can now add more functionalities to your store. For example, you may:
Find the queries and mutations to implement the above functionality and more in the Shopify GraphQL API documentation.
The sample code for the application created in this tutorial can be found in GitHub repository.