Shopify Storefront API in Java: Complete Implementation Guide

Introduction to Shopify Storefront API

The Shopify Storefront API provides a GraphQL-based interface for building customer-facing experiences like custom storefronts, mobile apps, and more. This guide covers building a comprehensive Java client for interacting with the Shopify Storefront API.

Key Features

  • GraphQL Query Execution with type-safe responses
  • Product Management including variants, images, and collections
  • Cart Operations for managing customer carts
  • Customer Authentication with customer access tokens
  • Checkout Process handling
  • Error Handling and retry mechanisms

Dependencies and Setup

Maven Configuration

<properties>
<okhttp.version>4.12.0</okhttp.version>
<jackson.version>2.15.2</jackson.version>
<graphql.java.version>20.4</graphql.java.version>
</properties>
<dependencies>
<!-- HTTP Client -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>${okhttp.version}</version>
</dependency>
<!-- JSON Processing -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
<!-- GraphQL Java (optional, for query validation) -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>${graphql.java.version}</version>
<optional>true</optional>
</dependency>
<!-- Logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>2.0.7</version>
</dependency>
<!-- Testing -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.9.3</version>
<scope>test</scope>
</dependency>
</dependencies>

Core Shopify Storefront Client

Main Storefront Client

package com.shopify.client;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class ShopifyStorefrontClient {
private static final Logger logger = LoggerFactory.getLogger(ShopifyStorefrontClient.class);
private final String storeDomain;
private final String storefrontAccessToken;
private final String apiVersion;
private final OkHttpClient httpClient;
private final ObjectMapper objectMapper;
private final String graphqlEndpoint;
public ShopifyStorefrontClient(String storeDomain, String storefrontAccessToken) {
this(storeDomain, storefrontAccessToken, "2023-10");
}
public ShopifyStorefrontClient(String storeDomain, String storefrontAccessToken, String apiVersion) {
this.storeDomain = storeDomain;
this.storefrontAccessToken = storefrontAccessToken;
this.apiVersion = apiVersion;
this.objectMapper = new ObjectMapper();
this.httpClient = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build();
this.graphqlEndpoint = String.format("https://%s/api/%s/graphql.json", 
storeDomain, apiVersion);
}
/**
* Execute GraphQL query
*/
public GraphQLResponse executeQuery(String query) throws ShopifyException {
return executeQuery(query, null);
}
/**
* Execute GraphQL query with variables
*/
public GraphQLResponse executeQuery(String query, Map<String, Object> variables) throws ShopifyException {
try {
// Build request body
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("query", query);
if (variables != null && !variables.isEmpty()) {
requestBody.put("variables", variables);
}
String requestBodyJson = objectMapper.writeValueAsString(requestBody);
Request request = new Request.Builder()
.url(graphqlEndpoint)
.post(RequestBody.create(requestBodyJson, MediaType.parse("application/json")))
.addHeader("Content-Type", "application/json")
.addHeader("X-Shopify-Storefront-Access-Token", storefrontAccessToken)
.addHeader("Accept", "application/json")
.build();
try (Response response = httpClient.newCall(request).execute()) {
return parseGraphQLResponse(response);
}
} catch (IOException e) {
throw new ShopifyException("GraphQL query execution failed", e);
}
}
/**
* Execute GraphQL query and parse to specific type
*/
public <T> T executeQuery(String query, Map<String, Object> variables, Class<T> responseType) throws ShopifyException {
GraphQLResponse response = executeQuery(query, variables);
if (response.hasErrors()) {
throw new ShopifyException("GraphQL query contained errors: " + response.getErrors());
}
try {
return objectMapper.convertValue(response.getData(), responseType);
} catch (Exception e) {
throw new ShopifyException("Failed to parse response to type: " + responseType.getSimpleName(), e);
}
}
/**
* Execute mutation
*/
public GraphQLResponse executeMutation(String mutation) throws ShopifyException {
return executeMutation(mutation, null);
}
/**
* Execute mutation with variables
*/
public GraphQLResponse executeMutation(String mutation, Map<String, Object> variables) throws ShopifyException {
return executeQuery(mutation, variables);
}
/**
* Execute mutation and parse to specific type
*/
public <T> T executeMutation(String mutation, Map<String, Object> variables, Class<T> responseType) throws ShopifyException {
return executeQuery(mutation, variables, responseType);
}
/**
* Parse GraphQL response
*/
private GraphQLResponse parseGraphQLResponse(Response response) throws IOException {
if (!response.isSuccessful()) {
throw new ShopifyException("HTTP request failed: " + response.code() + " - " + response.message());
}
String responseBody = response.body().string();
JsonNode rootNode = objectMapper.readTree(responseBody);
GraphQLResponse graphQLResponse = new GraphQLResponse();
// Parse data
if (rootNode.has("data")) {
graphQLResponse.setData(rootNode.get("data"));
}
// Parse errors
if (rootNode.has("errors")) {
GraphQLError[] errors = objectMapper.treeToValue(rootNode.get("errors"), GraphQLError[].class);
graphQLResponse.setErrors(errors);
}
// Parse extensions
if (rootNode.has("extensions")) {
graphQLResponse.setExtensions(rootNode.get("extensions"));
}
return graphQLResponse;
}
/**
* Get store information
*/
public Store getStore() throws ShopifyException {
String query = """
query {
shop {
name
description
primaryDomain {
url
host
}
paymentSettings {
currencyCode
acceptedCardBrands
}
shipsToCountries
}
}
""";
GraphQLResponse response = executeQuery(query);
JsonNode shopNode = response.getData().get("shop");
return objectMapper.convertValue(shopNode, Store.class);
}
// Custom exception
public static class ShopifyException extends Exception {
public ShopifyException(String message) {
super(message);
}
public ShopifyException(String message, Throwable cause) {
super(message, cause);
}
}
}

Data Models

GraphQL Response Classes

package com.shopify.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
import java.util.List;
// GraphQL Response
public class GraphQLResponse {
private JsonNode data;
private GraphQLError[] errors;
private JsonNode extensions;
public boolean hasErrors() {
return errors != null && errors.length > 0;
}
public String getErrors() {
if (!hasErrors()) return null;
StringBuilder sb = new StringBuilder();
for (GraphQLError error : errors) {
sb.append(error.getMessage()).append("; ");
}
return sb.toString();
}
// Getters and setters
public JsonNode getData() { return data; }
public void setData(JsonNode data) { this.data = data; }
public GraphQLError[] getErrors() { return errors; }
public void setErrors(GraphQLError[] errors) { this.errors = errors; }
public JsonNode getExtensions() { return extensions; }
public void setExtensions(JsonNode extensions) { this.extensions = extensions; }
}
// GraphQL Error
@JsonIgnoreProperties(ignoreUnknown = true)
class GraphQLError {
private String message;
private List<Location> locations;
private String[] path;
private JsonNode extensions;
// Getters and setters
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public List<Location> getLocations() { return locations; }
public void setLocations(List<Location> locations) { this.locations = locations; }
public String[] getPath() { return path; }
public void setPath(String[] path) { this.path = path; }
public JsonNode getExtensions() { return extensions; }
public void setExtensions(JsonNode extensions) { this.extensions = extensions; }
}
// Error Location
@JsonIgnoreProperties(ignoreUnknown = true)
class Location {
private int line;
private int column;
// Getters and setters
public int getLine() { return line; }
public void setLine(int line) { this.line = line; }
public int getColumn() { return column; }
public void setColumn(int column) { this.column = column; }
}
// Store Information
@JsonIgnoreProperties(ignoreUnknown = true)
class Store {
private String name;
private String description;
private PrimaryDomain primaryDomain;
private PaymentSettings paymentSettings;
private String[] shipsToCountries;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public PrimaryDomain getPrimaryDomain() { return primaryDomain; }
public void setPrimaryDomain(PrimaryDomain primaryDomain) { this.primaryDomain = primaryDomain; }
public PaymentSettings getPaymentSettings() { return paymentSettings; }
public void setPaymentSettings(PaymentSettings paymentSettings) { this.paymentSettings = paymentSettings; }
public String[] getShipsToCountries() { return shipsToCountries; }
public void setShipsToCountries(String[] shipsToCountries) { this.shipsToCountries = shipsToCountries; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PrimaryDomain {
private String url;
private String host;
// Getters and setters
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getHost() { return host; }
public void setHost(String host) { this.host = host; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PaymentSettings {
private String currencyCode;
private String[] acceptedCardBrands;
// Getters and setters
public String getCurrencyCode() { return currencyCode; }
public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; }
public String[] getAcceptedCardBrands() { return acceptedCardBrands; }
public void setAcceptedCardBrands(String[] acceptedCardBrands) { this.acceptedCardBrands = acceptedCardBrands; }
}

Product Service

Product Management Service

package com.shopify.service;
import com.shopify.client.ShopifyStorefrontClient;
import com.shopify.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class ProductService {
private static final Logger logger = LoggerFactory.getLogger(ProductService.class);
private final ShopifyStorefrontClient client;
public ProductService(ShopifyStorefrontClient client) {
this.client = client;
}
/**
* Get all products with pagination
*/
public ProductConnection getProducts(int first, String after) throws ShopifyException {
String query = """
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
title
description
descriptionHtml
handle
availableForSale
productType
tags
vendor
createdAt
updatedAt
featuredImage {
id
url
altText
width
height
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
variants(first: 10) {
edges {
node {
id
title
availableForSale
sku
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
selectedOptions {
name
value
}
image {
id
url
altText
width
height
}
}
}
}
}
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("first", first);
if (after != null) {
variables.put("after", after);
}
GraphQLResponse response = client.executeQuery(query, variables);
JsonNode productsNode = response.getData().get("products");
return convertToProductConnection(productsNode);
}
/**
* Get product by handle
*/
public Product getProductByHandle(String handle) throws ShopifyException {
String query = """
query GetProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
title
description
descriptionHtml
handle
availableForSale
productType
tags
vendor
createdAt
updatedAt
featuredImage {
id
url
altText
width
height
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
variants(first: 20) {
edges {
node {
id
title
availableForSale
sku
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
selectedOptions {
name
value
}
image {
id
url
altText
width
height
}
}
}
}
images(first: 10) {
edges {
node {
id
url
altText
width
height
}
}
}
options {
id
name
values
}
}
}
""";
Map<String, Object> variables = Collections.singletonMap("handle", handle);
GraphQLResponse response = client.executeQuery(query, variables);
JsonNode productNode = response.getData().get("productByHandle");
if (productNode == null || productNode.isNull()) {
return null;
}
return convertToProduct(productNode);
}
/**
* Get product by ID
*/
public Product getProductById(String id) throws ShopifyException {
String query = """
query GetProductById($id: ID!) {
product(id: $id) {
id
title
description
descriptionHtml
handle
availableForSale
productType
tags
vendor
createdAt
updatedAt
featuredImage {
id
url
altText
width
height
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
variants(first: 20) {
edges {
node {
id
title
availableForSale
sku
price {
amount
currencyCode
}
compareAtPrice {
amount
currencyCode
}
selectedOptions {
name
value
}
image {
id
url
altText
width
height
}
}
}
}
images(first: 10) {
edges {
node {
id
url
altText
width
height
}
}
}
options {
id
name
values
}
}
}
""";
Map<String, Object> variables = Collections.singletonMap("id", id);
GraphQLResponse response = client.executeQuery(query, variables);
JsonNode productNode = response.getData().get("product");
if (productNode == null || productNode.isNull()) {
return null;
}
return convertToProduct(productNode);
}
/**
* Search products by query
*/
public ProductConnection searchProducts(String query, int first, String after) throws ShopifyException {
String graphqlQuery = """
query SearchProducts($query: String!, $first: Int!, $after: String) {
products(first: $first, after: $after, query: $query) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
title
description
handle
availableForSale
productType
vendor
featuredImage {
id
url
altText
width
height
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("query", query);
variables.put("first", first);
if (after != null) {
variables.put("after", after);
}
GraphQLResponse response = client.executeQuery(graphqlQuery, variables);
JsonNode productsNode = response.getData().get("products");
return convertToProductConnection(productsNode);
}
/**
* Get products by collection
*/
public ProductConnection getProductsByCollection(String collectionHandle, int first, String after) throws ShopifyException {
String query = """
query GetProductsByCollection($handle: String!, $first: Int!, $after: String) {
collectionByHandle(handle: $handle) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
}
edges {
cursor
node {
id
title
description
handle
availableForSale
productType
vendor
featuredImage {
id
url
altText
width
height
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("handle", collectionHandle);
variables.put("first", first);
if (after != null) {
variables.put("after", after);
}
GraphQLResponse response = client.executeQuery(query, variables);
JsonNode collectionNode = response.getData().get("collectionByHandle");
if (collectionNode == null || collectionNode.isNull()) {
return new ProductConnection();
}
JsonNode productsNode = collectionNode.get("products");
return convertToProductConnection(productsNode);
}
// Helper methods for conversion
private ProductConnection convertToProductConnection(JsonNode productsNode) {
if (productsNode == null) {
return new ProductConnection();
}
ProductConnection connection = new ProductConnection();
// Parse page info
JsonNode pageInfoNode = productsNode.get("pageInfo");
if (pageInfoNode != null) {
PageInfo pageInfo = new PageInfo();
pageInfo.setHasNextPage(pageInfoNode.get("hasNextPage").asBoolean());
pageInfo.setHasPreviousPage(pageInfoNode.get("hasPreviousPage").asBoolean());
pageInfo.setStartCursor(getStringOrNull(pageInfoNode, "startCursor"));
pageInfo.setEndCursor(getStringOrNull(pageInfoNode, "endCursor"));
connection.setPageInfo(pageInfo);
}
// Parse edges
List<ProductEdge> edges = new ArrayList<>();
JsonNode edgesNode = productsNode.get("edges");
if (edgesNode != null && edgesNode.isArray()) {
for (JsonNode edgeNode : edgesNode) {
ProductEdge edge = new ProductEdge();
edge.setCursor(getStringOrNull(edgeNode, "cursor"));
JsonNode productNode = edgeNode.get("node");
if (productNode != null) {
edge.setNode(convertToProduct(productNode));
}
edges.add(edge);
}
}
connection.setEdges(edges);
return connection;
}
private Product convertToProduct(JsonNode productNode) {
Product product = new Product();
product.setId(getStringOrNull(productNode, "id"));
product.setTitle(getStringOrNull(productNode, "title"));
product.setDescription(getStringOrNull(productNode, "description"));
product.setDescriptionHtml(getStringOrNull(productNode, "descriptionHtml"));
product.setHandle(getStringOrNull(productNode, "handle"));
product.setAvailableForSale(getBooleanOrFalse(productNode, "availableForSale"));
product.setProductType(getStringOrNull(productNode, "productType"));
product.setVendor(getStringOrNull(productNode, "vendor"));
product.setCreatedAt(getStringOrNull(productNode, "createdAt"));
product.setUpdatedAt(getStringOrNull(productNode, "updatedAt"));
// Parse tags
JsonNode tagsNode = productNode.get("tags");
if (tagsNode != null && tagsNode.isArray()) {
List<String> tags = new ArrayList<>();
for (JsonNode tagNode : tagsNode) {
tags.add(tagNode.asText());
}
product.setTags(tags);
}
// Parse featured image
JsonNode featuredImageNode = productNode.get("featuredImage");
if (featuredImageNode != null && !featuredImageNode.isNull()) {
product.setFeaturedImage(convertToImage(featuredImageNode));
}
// Parse price range
JsonNode priceRangeNode = productNode.get("priceRange");
if (priceRangeNode != null) {
product.setPriceRange(convertToPriceRange(priceRangeNode));
}
// Parse variants
JsonNode variantsNode = productNode.get("variants");
if (variantsNode != null) {
product.setVariants(convertToVariantConnection(variantsNode));
}
// Parse images
JsonNode imagesNode = productNode.get("images");
if (imagesNode != null) {
product.setImages(convertToImageConnection(imagesNode));
}
// Parse options
JsonNode optionsNode = productNode.get("options");
if (optionsNode != null && optionsNode.isArray()) {
List<ProductOption> options = new ArrayList<>();
for (JsonNode optionNode : optionsNode) {
options.add(convertToProductOption(optionNode));
}
product.setOptions(options);
}
return product;
}
private Image convertToImage(JsonNode imageNode) {
Image image = new Image();
image.setId(getStringOrNull(imageNode, "id"));
image.setUrl(getStringOrNull(imageNode, "url"));
image.setAltText(getStringOrNull(imageNode, "altText"));
image.setWidth(getIntOrZero(imageNode, "width"));
image.setHeight(getIntOrZero(imageNode, "height"));
return image;
}
private PriceRange convertToPriceRange(JsonNode priceRangeNode) {
PriceRange priceRange = new PriceRange();
JsonNode minPriceNode = priceRangeNode.get("minVariantPrice");
if (minPriceNode != null) {
priceRange.setMinVariantPrice(convertToMoney(minPriceNode));
}
JsonNode maxPriceNode = priceRangeNode.get("maxVariantPrice");
if (maxPriceNode != null) {
priceRange.setMaxVariantPrice(convertToMoney(maxPriceNode));
}
return priceRange;
}
private Money convertToMoney(JsonNode moneyNode) {
Money money = new Money();
money.setAmount(getStringOrNull(moneyNode, "amount"));
money.setCurrencyCode(getStringOrNull(moneyNode, "currencyCode"));
return money;
}
private ProductVariantConnection convertToVariantConnection(JsonNode variantsNode) {
ProductVariantConnection connection = new ProductVariantConnection();
List<ProductVariantEdge> edges = new ArrayList<>();
JsonNode edgesNode = variantsNode.get("edges");
if (edgesNode != null && edgesNode.isArray()) {
for (JsonNode edgeNode : edgesNode) {
ProductVariantEdge edge = new ProductVariantEdge();
edge.setCursor(getStringOrNull(edgeNode, "cursor"));
JsonNode variantNode = edgeNode.get("node");
if (variantNode != null) {
edge.setNode(convertToProductVariant(variantNode));
}
edges.add(edge);
}
}
connection.setEdges(edges);
return connection;
}
private ProductVariant convertToProductVariant(JsonNode variantNode) {
ProductVariant variant = new ProductVariant();
variant.setId(getStringOrNull(variantNode, "id"));
variant.setTitle(getStringOrNull(variantNode, "title"));
variant.setAvailableForSale(getBooleanOrFalse(variantNode, "availableForSale"));
variant.setSku(getStringOrNull(variantNode, "sku"));
// Parse price
JsonNode priceNode = variantNode.get("price");
if (priceNode != null && !priceNode.isNull()) {
variant.setPrice(convertToMoney(priceNode));
}
// Parse compare at price
JsonNode compareAtPriceNode = variantNode.get("compareAtPrice");
if (compareAtPriceNode != null && !compareAtPriceNode.isNull()) {
variant.setCompareAtPrice(convertToMoney(compareAtPriceNode));
}
// Parse selected options
JsonNode selectedOptionsNode = variantNode.get("selectedOptions");
if (selectedOptionsNode != null && selectedOptionsNode.isArray()) {
List<SelectedOption> selectedOptions = new ArrayList<>();
for (JsonNode optionNode : selectedOptionsNode) {
SelectedOption option = new SelectedOption();
option.setName(getStringOrNull(optionNode, "name"));
option.setValue(getStringOrNull(optionNode, "value"));
selectedOptions.add(option);
}
variant.setSelectedOptions(selectedOptions);
}
// Parse image
JsonNode imageNode = variantNode.get("image");
if (imageNode != null && !imageNode.isNull()) {
variant.setImage(convertToImage(imageNode));
}
return variant;
}
private ImageConnection convertToImageConnection(JsonNode imagesNode) {
ImageConnection connection = new ImageConnection();
List<ImageEdge> edges = new ArrayList<>();
JsonNode edgesNode = imagesNode.get("edges");
if (edgesNode != null && edgesNode.isArray()) {
for (JsonNode edgeNode : edgesNode) {
ImageEdge edge = new ImageEdge();
edge.setCursor(getStringOrNull(edgeNode, "cursor"));
JsonNode imageNode = edgeNode.get("node");
if (imageNode != null) {
edge.setNode(convertToImage(imageNode));
}
edges.add(edge);
}
}
connection.setEdges(edges);
return connection;
}
private ProductOption convertToProductOption(JsonNode optionNode) {
ProductOption option = new ProductOption();
option.setId(getStringOrNull(optionNode, "id"));
option.setName(getStringOrNull(optionNode, "name"));
JsonNode valuesNode = optionNode.get("values");
if (valuesNode != null && valuesNode.isArray()) {
List<String> values = new ArrayList<>();
for (JsonNode valueNode : valuesNode) {
values.add(valueNode.asText());
}
option.setValues(values);
}
return option;
}
// Utility methods
private String getStringOrNull(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
return (fieldNode != null && !fieldNode.isNull()) ? fieldNode.asText() : null;
}
private boolean getBooleanOrFalse(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
return (fieldNode != null && !fieldNode.isNull()) ? fieldNode.asBoolean() : false;
}
private int getIntOrZero(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
return (fieldNode != null && !fieldNode.isNull()) ? fieldNode.asInt() : 0;
}
}

Cart Service

Cart Management Service

package com.shopify.service;
import com.shopify.client.ShopifyStorefrontClient;
import com.shopify.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
public class CartService {
private static final Logger logger = LoggerFactory.getLogger(CartService.class);
private final ShopifyStorefrontClient client;
public CartService(ShopifyStorefrontClient client) {
this.client = client;
}
/**
* Create a new cart
*/
public Cart createCart(List<CartLineInput> lines) throws ShopifyException {
String mutation = """
mutation CreateCart($lines: [CartLineInput!]) {
cartCreate(input: { lines: $lines }) {
cart {
id
checkoutUrl
totalQuantity
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
product {
title
handle
}
price {
amount
currencyCode
}
image {
url
altText
width
height
}
}
}
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
}
}
}
}
}
userErrors {
field
message
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("lines", lines);
GraphQLResponse response = client.executeMutation(mutation, variables);
JsonNode cartCreateNode = response.getData().get("cartCreate");
// Check for user errors
JsonNode userErrorsNode = cartCreateNode.get("userErrors");
if (userErrorsNode != null && userErrorsNode.isArray() && userErrorsNode.size() > 0) {
throw new ShopifyException("Cart creation failed: " + userErrorsNode.toString());
}
JsonNode cartNode = cartCreateNode.get("cart");
return convertToCart(cartNode);
}
/**
* Get cart by ID
*/
public Cart getCart(String cartId) throws ShopifyException {
String query = """
query GetCart($cartId: ID!) {
cart(id: $cartId) {
id
checkoutUrl
totalQuantity
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
product {
title
handle
}
price {
amount
currencyCode
}
image {
url
altText
width
height
}
}
}
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
}
}
}
}
}
}
""";
Map<String, Object> variables = Collections.singletonMap("cartId", cartId);
GraphQLResponse response = client.executeQuery(query, variables);
JsonNode cartNode = response.getData().get("cart");
if (cartNode == null || cartNode.isNull()) {
return null;
}
return convertToCart(cartNode);
}
/**
* Add lines to cart
*/
public Cart addLinesToCart(String cartId, List<CartLineInput> lines) throws ShopifyException {
String mutation = """
mutation CartLinesAdd($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
checkoutUrl
totalQuantity
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
product {
title
handle
}
price {
amount
currencyCode
}
image {
url
altText
width
height
}
}
}
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
}
}
}
}
}
userErrors {
field
message
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("cartId", cartId);
variables.put("lines", lines);
GraphQLResponse response = client.executeMutation(mutation, variables);
JsonNode cartLinesAddNode = response.getData().get("cartLinesAdd");
// Check for user errors
JsonNode userErrorsNode = cartLinesAddNode.get("userErrors");
if (userErrorsNode != null && userErrorsNode.isArray() && userErrorsNode.size() > 0) {
throw new ShopifyException("Add lines failed: " + userErrorsNode.toString());
}
JsonNode cartNode = cartLinesAddNode.get("cart");
return convertToCart(cartNode);
}
/**
* Update cart lines
*/
public Cart updateCartLines(String cartId, List<CartLineUpdateInput> lines) throws ShopifyException {
String mutation = """
mutation CartLinesUpdate($cartId: ID!, $lines: [CartLineUpdateInput!]!) {
cartLinesUpdate(cartId: $cartId, lines: $lines) {
cart {
id
checkoutUrl
totalQuantity
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
product {
title
handle
}
price {
amount
currencyCode
}
image {
url
altText
width
height
}
}
}
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
}
}
}
}
}
userErrors {
field
message
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("cartId", cartId);
variables.put("lines", lines);
GraphQLResponse response = client.executeMutation(mutation, variables);
JsonNode cartLinesUpdateNode = response.getData().get("cartLinesUpdate");
// Check for user errors
JsonNode userErrorsNode = cartLinesUpdateNode.get("userErrors");
if (userErrorsNode != null && userErrorsNode.isArray() && userErrorsNode.size() > 0) {
throw new ShopifyException("Update lines failed: " + userErrorsNode.toString());
}
JsonNode cartNode = cartLinesUpdateNode.get("cart");
return convertToCart(cartNode);
}
/**
* Remove lines from cart
*/
public Cart removeLinesFromCart(String cartId, List<String> lineIds) throws ShopifyException {
String mutation = """
mutation CartLinesRemove($cartId: ID!, $lineIds: [ID!]!) {
cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
cart {
id
checkoutUrl
totalQuantity
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
totalTaxAmount {
amount
currencyCode
}
totalDutyAmount {
amount
currencyCode
}
}
lines(first: 10) {
edges {
node {
id
quantity
merchandise {
... on ProductVariant {
id
title
product {
title
handle
}
price {
amount
currencyCode
}
image {
url
altText
width
height
}
}
}
cost {
totalAmount {
amount
currencyCode
}
subtotalAmount {
amount
currencyCode
}
}
}
}
}
}
userErrors {
field
message
}
}
}
""";
Map<String, Object> variables = new HashMap<>();
variables.put("cartId", cartId);
variables.put("lineIds", lineIds);
GraphQLResponse response = client.executeMutation(mutation, variables);
JsonNode cartLinesRemoveNode = response.getData().get("cartLinesRemove");
// Check for user errors
JsonNode userErrorsNode = cartLinesRemoveNode.get("userErrors");
if (userErrorsNode != null && userErrorsNode.isArray() && userErrorsNode.size() > 0) {
throw new ShopifyException("Remove lines failed: " + userErrorsNode.toString());
}
JsonNode cartNode = cartLinesRemoveNode.get("cart");
return convertToCart(cartNode);
}
// Helper method to convert JSON to Cart
private Cart convertToCart(JsonNode cartNode) {
Cart cart = new Cart();
cart.setId(getStringOrNull(cartNode, "id"));
cart.setCheckoutUrl(getStringOrNull(cartNode, "checkoutUrl"));
cart.setTotalQuantity(getIntOrZero(cartNode, "totalQuantity"));
// Parse cost
JsonNode costNode = cartNode.get("cost");
if (costNode != null) {
CartCost cost = new CartCost();
JsonNode totalAmountNode = costNode.get("totalAmount");
if (totalAmountNode != null) {
cost.setTotalAmount(convertToMoney(totalAmountNode));
}
JsonNode subtotalAmountNode = costNode.get("subtotalAmount");
if (subtotalAmountNode != null) {
cost.setSubtotalAmount(convertToMoney(subtotalAmountNode));
}
JsonNode totalTaxAmountNode = costNode.get("totalTaxAmount");
if (totalTaxAmountNode != null && !totalTaxAmountNode.isNull()) {
cost.setTotalTaxAmount(convertToMoney(totalTaxAmountNode));
}
JsonNode totalDutyAmountNode = costNode.get("totalDutyAmount");
if (totalDutyAmountNode != null && !totalDutyAmountNode.isNull()) {
cost.setTotalDutyAmount(convertToMoney(totalDutyAmountNode));
}
cart.setCost(cost);
}
// Parse lines
JsonNode linesNode = cartNode.get("lines");
if (linesNode != null) {
cart.setLines(convertToCartLineConnection(linesNode));
}
return cart;
}
private CartLineConnection convertToCartLineConnection(JsonNode linesNode) {
CartLineConnection connection = new CartLineConnection();
List<CartLineEdge> edges = new ArrayList<>();
JsonNode edgesNode = linesNode.get("edges");
if (edgesNode != null && edgesNode.isArray()) {
for (JsonNode edgeNode : edgesNode) {
CartLineEdge edge = new CartLineEdge();
edge.setCursor(getStringOrNull(edgeNode, "cursor"));
JsonNode lineNode = edgeNode.get("node");
if (lineNode != null) {
edge.setNode(convertToCartLine(lineNode));
}
edges.add(edge);
}
}
connection.setEdges(edges);
return connection;
}
private CartLine convertToCartLine(JsonNode lineNode) {
CartLine line = new CartLine();
line.setId(getStringOrNull(lineNode, "id"));
line.setQuantity(getIntOrZero(lineNode, "quantity"));
// Parse merchandise
JsonNode merchandiseNode = lineNode.get("merchandise");
if (merchandiseNode != null) {
// This would need to handle different types of merchandise
// For now, assume it's a ProductVariant
line.setMerchandise(convertToProductVariant(merchandiseNode));
}
// Parse cost
JsonNode costNode = lineNode.get("cost");
if (costNode != null) {
CartLineCost cost = new CartLineCost();
JsonNode totalAmountNode = costNode.get("totalAmount");
if (totalAmountNode != null) {
cost.setTotalAmount(convertToMoney(totalAmountNode));
}
JsonNode subtotalAmountNode = costNode.get("subtotalAmount");
if (subtotalAmountNode != null) {
cost.setSubtotalAmount(convertToMoney(subtotalAmountNode));
}
line.setCost(cost);
}
return line;
}
// Reuse conversion methods from ProductService
private Money convertToMoney(JsonNode moneyNode) {
Money money = new Money();
money.setAmount(getStringOrNull(moneyNode, "amount"));
money.setCurrencyCode(getStringOrNull(moneyNode, "currencyCode"));
return money;
}
private ProductVariant convertToProductVariant(JsonNode variantNode) {
ProductVariant variant = new ProductVariant();
variant.setId(getStringOrNull(variantNode, "id"));
variant.setTitle(getStringOrNull(variantNode, "title"));
// Parse price
JsonNode priceNode = variantNode.get("price");
if (priceNode != null && !priceNode.isNull()) {
variant.setPrice(convertToMoney(priceNode));
}
// Parse product
JsonNode productNode = variantNode.get("product");
if (productNode != null) {
Product product = new Product();
product.setTitle(getStringOrNull(productNode, "title"));
product.setHandle(getStringOrNull(productNode, "handle"));
variant.setProduct(product);
}
// Parse image
JsonNode imageNode = variantNode.get("image");
if (imageNode != null && !imageNode.isNull()) {
variant.setImage(convertToImage(imageNode));
}
return variant;
}
private Image convertToImage(JsonNode imageNode) {
Image image = new Image();
image.setUrl(getStringOrNull(imageNode, "url"));
image.setAltText(getStringOrNull(imageNode, "altText"));
image.setWidth(getIntOrZero(imageNode, "width"));
image.setHeight(getIntOrZero(imageNode, "height"));
return image;
}
// Utility methods
private String getStringOrNull(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
return (fieldNode != null && !fieldNode.isNull()) ? fieldNode.asText() : null;
}
private int getIntOrZero(JsonNode node, String fieldName) {
JsonNode fieldNode = node.get(fieldName);
return (fieldNode != null && !fieldNode.isNull()) ? fieldNode.asInt() : 0;
}
}

Complete Data Models

package com.shopify.model;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
// Product and related models
@JsonIgnoreProperties(ignoreUnknown = true)
class Product {
private String id;
private String title;
private String description;
private String descriptionHtml;
private String handle;
private boolean availableForSale;
private String productType;
private List<String> tags;
private String vendor;
private String createdAt;
private String updatedAt;
private Image featuredImage;
private PriceRange priceRange;
private ProductVariantConnection variants;
private ImageConnection images;
private List<ProductOption> options;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public String getDescriptionHtml() { return descriptionHtml; }
public void setDescriptionHtml(String descriptionHtml) { this.descriptionHtml = descriptionHtml; }
public String getHandle() { return handle; }
public void setHandle(String handle) { this.handle = handle; }
public boolean isAvailableForSale() { return availableForSale; }
public void setAvailableForSale(boolean availableForSale) { this.availableForSale = availableForSale; }
public String getProductType() { return productType; }
public void setProductType(String productType) { this.productType = productType; }
public List<String> getTags() { return tags; }
public void setTags(List<String> tags) { this.tags = tags; }
public String getVendor() { return vendor; }
public void setVendor(String vendor) { this.vendor = vendor; }
public String getCreatedAt() { return createdAt; }
public void setCreatedAt(String createdAt) { this.createdAt = createdAt; }
public String getUpdatedAt() { return updatedAt; }
public void setUpdatedAt(String updatedAt) { this.updatedAt = updatedAt; }
public Image getFeaturedImage() { return featuredImage; }
public void setFeaturedImage(Image featuredImage) { this.featuredImage = featuredImage; }
public PriceRange getPriceRange() { return priceRange; }
public void setPriceRange(PriceRange priceRange) { this.priceRange = priceRange; }
public ProductVariantConnection getVariants() { return variants; }
public void setVariants(ProductVariantConnection variants) { this.variants = variants; }
public ImageConnection getImages() { return images; }
public void setImages(ImageConnection images) { this.images = images; }
public List<ProductOption> getOptions() { return options; }
public void setOptions(List<ProductOption> options) { this.options = options; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductConnection {
private PageInfo pageInfo;
private List<ProductEdge> edges;
// Getters and setters
public PageInfo getPageInfo() { return pageInfo; }
public void setPageInfo(PageInfo pageInfo) { this.pageInfo = pageInfo; }
public List<ProductEdge> getEdges() { return edges; }
public void setEdges(List<ProductEdge> edges) { this.edges = edges; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductEdge {
private String cursor;
private Product node;
// Getters and setters
public String getCursor() { return cursor; }
public void setCursor(String cursor) { this.cursor = cursor; }
public Product getNode() { return node; }
public void setNode(Product node) { this.node = node; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PageInfo {
private boolean hasNextPage;
private boolean hasPreviousPage;
private String startCursor;
private String endCursor;
// Getters and setters
public boolean isHasNextPage() { return hasNextPage; }
public void setHasNextPage(boolean hasNextPage) { this.hasNextPage = hasNextPage; }
public boolean isHasPreviousPage() { return hasPreviousPage; }
public void setHasPreviousPage(boolean hasPreviousPage) { this.hasPreviousPage = hasPreviousPage; }
public String getStartCursor() { return startCursor; }
public void setStartCursor(String startCursor) { this.startCursor = startCursor; }
public String getEndCursor() { return endCursor; }
public void setEndCursor(String endCursor) { this.endCursor = endCursor; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class Image {
private String id;
private String url;
private String altText;
private int width;
private int height;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getUrl() { return url; }
public void setUrl(String url) { this.url = url; }
public String getAltText() { return altText; }
public void setAltText(String altText) { this.altText = altText; }
public int getWidth() { return width; }
public void setWidth(int width) { this.width = width; }
public int getHeight() { return height; }
public void setHeight(int height) { this.height = height; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ImageConnection {
private List<ImageEdge> edges;
// Getters and setters
public List<ImageEdge> getEdges() { return edges; }
public void setEdges(List<ImageEdge> edges) { this.edges = edges; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ImageEdge {
private String cursor;
private Image node;
// Getters and setters
public String getCursor() { return cursor; }
public void setCursor(String cursor) { this.cursor = cursor; }
public Image getNode() { return node; }
public void setNode(Image node) { this.node = node; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class PriceRange {
private Money minVariantPrice;
private Money maxVariantPrice;
// Getters and setters
public Money getMinVariantPrice() { return minVariantPrice; }
public void setMinVariantPrice(Money minVariantPrice) { this.minVariantPrice = minVariantPrice; }
public Money getMaxVariantPrice() { return maxVariantPrice; }
public void setMaxVariantPrice(Money maxVariantPrice) { this.maxVariantPrice = maxVariantPrice; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class Money {
private String amount;
private String currencyCode;
// Getters and setters
public String getAmount() { return amount; }
public void setAmount(String amount) { this.amount = amount; }
public String getCurrencyCode() { return currencyCode; }
public void setCurrencyCode(String currencyCode) { this.currencyCode = currencyCode; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductVariant {
private String id;
private String title;
private boolean availableForSale;
private String sku;
private Money price;
private Money compareAtPrice;
private List<SelectedOption> selectedOptions;
private Image image;
private Product product;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public boolean isAvailableForSale() { return availableForSale; }
public void setAvailableForSale(boolean availableForSale) { this.availableForSale = availableForSale; }
public String getSku() { return sku; }
public void setSku(String sku) { this.sku = sku; }
public Money getPrice() { return price; }
public void setPrice(Money price) { this.price = price; }
public Money getCompareAtPrice() { return compareAtPrice; }
public void setCompareAtPrice(Money compareAtPrice) { this.compareAtPrice = compareAtPrice; }
public List<SelectedOption> getSelectedOptions() { return selectedOptions; }
public void setSelectedOptions(List<SelectedOption> selectedOptions) { this.selectedOptions = selectedOptions; }
public Image getImage() { return image; }
public void setImage(Image image) { this.image = image; }
public Product getProduct() { return product; }
public void setProduct(Product product) { this.product = product; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductVariantConnection {
private List<ProductVariantEdge> edges;
// Getters and setters
public List<ProductVariantEdge> getEdges() { return edges; }
public void setEdges(List<ProductVariantEdge> edges) { this.edges = edges; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductVariantEdge {
private String cursor;
private ProductVariant node;
// Getters and setters
public String getCursor() { return cursor; }
public void setCursor(String cursor) { this.cursor = cursor; }
public ProductVariant getNode() { return node; }
public void setNode(ProductVariant node) { this.node = node; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class SelectedOption {
private String name;
private String value;
// Getters and setters
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class ProductOption {
private String id;
private String name;
private List<String> values;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public List<String> getValues() { return values; }
public void setValues(List<String> values) { this.values = values; }
}
// Cart and related models
@JsonIgnoreProperties(ignoreUnknown = true)
class Cart {
private String id;
private String checkoutUrl;
private int totalQuantity;
private CartCost cost;
private CartLineConnection lines;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getCheckoutUrl() { return checkoutUrl; }
public void setCheckoutUrl(String checkoutUrl) { this.checkoutUrl = checkoutUrl; }
public int getTotalQuantity() { return totalQuantity; }
public void setTotalQuantity(int totalQuantity) { this.totalQuantity = totalQuantity; }
public CartCost getCost() { return cost; }
public void setCost(CartCost cost) { this.cost = cost; }
public CartLineConnection getLines() { return lines; }
public void setLines(CartLineConnection lines) { this.lines = lines; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class CartCost {
private Money totalAmount;
private Money subtotalAmount;
private Money totalTaxAmount;
private Money totalDutyAmount;
// Getters and setters
public Money getTotalAmount() { return totalAmount; }
public void setTotalAmount(Money totalAmount) { this.totalAmount = totalAmount; }
public Money getSubtotalAmount() { return subtotalAmount; }
public void setSubtotalAmount(Money subtotalAmount) { this.subtotalAmount = subtotalAmount; }
public Money getTotalTaxAmount() { return totalTaxAmount; }
public void setTotalTaxAmount(Money totalTaxAmount) { this.totalTaxAmount = totalTaxAmount; }
public Money getTotalDutyAmount() { return totalDutyAmount; }
public void setTotalDutyAmount(Money totalDutyAmount) { this.totalDutyAmount = totalDutyAmount; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class CartLineConnection {
private List<CartLineEdge> edges;
// Getters and setters
public List<CartLineEdge> getEdges() { return edges; }
public void setEdges(List<CartLineEdge> edges) { this.edges = edges; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class CartLineEdge {
private String cursor;
private CartLine node;
// Getters and setters
public String getCursor() { return cursor; }
public void setCursor(String cursor) { this.cursor = cursor; }
public CartLine getNode() { return node; }
public void setNode(CartLine node) { this.node = node; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class CartLine {
private String id;
private int quantity;
private ProductVariant merchandise;
private CartLineCost cost;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public ProductVariant getMerchandise() { return merchandise; }
public void setMerchandise(ProductVariant merchandise) { this.merchandise = merchandise; }
public CartLineCost getCost() { return cost; }
public void setCost(CartLineCost cost) { this.cost = cost; }
}
@JsonIgnoreProperties(ignoreUnknown = true)
class CartLineCost {
private Money totalAmount;
private Money subtotalAmount;
// Getters and setters
public Money getTotalAmount() { return totalAmount; }
public void setTotalAmount(Money totalAmount) { this.totalAmount = totalAmount; }
public Money getSubtotalAmount() { return subtotalAmount; }
public void setSubtotalAmount(Money subtotalAmount) { this.subtotalAmount = subtotalAmount; }
}
// Input types for mutations
class CartLineInput {
private String merchandiseId;
private int quantity;
private List<AttributeInput> attributes;
// Getters and setters
public String getMerchandiseId() { return merchandiseId; }
public void setMerchandiseId(String merchandiseId) { this.merchandiseId = merchandiseId; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public List<AttributeInput> getAttributes() { return attributes; }
public void setAttributes(List<AttributeInput> attributes) { this.attributes = attributes; }
}
class CartLineUpdateInput {
private String id;
private int quantity;
private List<AttributeInput> attributes;
// Getters and setters
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public int getQuantity() { return quantity; }
public void setQuantity(int quantity) { this.quantity = quantity; }
public List<AttributeInput> getAttributes() { return attributes; }
public void setAttributes(List<AttributeInput> attributes) { this.attributes = attributes; }
}
class AttributeInput {
private String key;
private String value;
// Getters and setters
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
}

Spring Boot Integration

Configuration Class

package com.shopify.config;
import com.shopify.client.ShopifyStorefrontClient;
import com.shopify.service.ProductService;
import com.shopify.service.CartService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShopifyConfig {
@Value("${shopify.store.domain}")
private String storeDomain;
@Value("${shopify.storefront.access.token}")
private String storefrontAccessToken;
@Value("${shopify.api.version:2023-10}")
private String apiVersion;
@Bean
public ShopifyStorefrontClient shopifyStorefrontClient() {
return new ShopifyStorefrontClient(storeDomain, storefrontAccessToken, apiVersion);
}
@Bean
public ProductService productService() {
return new ProductService(shopifyStorefrontClient());
}
@Bean
public CartService cartService() {
return new CartService(shopifyStorefrontClient());
}
}

REST Controller

package com.shopify.controller;
import com.shopify.model.*;
import com.shopify.service.ProductService;
import com.shopify.service.CartService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/shopify")
public class ShopifyController {
@Autowired
private ProductService productService;
@Autowired
private CartService cartService;
@GetMapping("/products")
public ResponseEntity<Map<String, Object>> getProducts(
@RequestParam(defaultValue = "10") int first,
@RequestParam(required = false) String after) {
Map<String, Object> response = new HashMap<>();
try {
ProductConnection products = productService.getProducts(first, after);
response.put("success", true);
response.put("products", products.getEdges());
response.put("pageInfo", products.getPageInfo());
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/products/{handle}")
public ResponseEntity<Map<String, Object>> getProductByHandle(@PathVariable String handle) {
Map<String, Object> response = new HashMap<>();
try {
Product product = productService.getProductByHandle(handle);
if (product == null) {
response.put("success", false);
response.put("error", "Product not found");
return ResponseEntity.notFound().build();
}
response.put("success", true);
response.put("product", product);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/products/search")
public ResponseEntity<Map<String, Object>> searchProducts(
@RequestParam String q,
@RequestParam(defaultValue = "10") int first,
@RequestParam(required = false) String after) {
Map<String, Object> response = new HashMap<>();
try {
ProductConnection products = productService.searchProducts(q, first, after);
response.put("success", true);
response.put("products", products.getEdges());
response.put("pageInfo", products.getPageInfo());
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/cart")
public ResponseEntity<Map<String, Object>> createCart(@RequestBody List<CartLineInput> lines) {
Map<String, Object> response = new HashMap<>();
try {
Cart cart = cartService.createCart(lines);
response.put("success", true);
response.put("cart", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@GetMapping("/cart/{cartId}")
public ResponseEntity<Map<String, Object>> getCart(@PathVariable String cartId) {
Map<String, Object> response = new HashMap<>();
try {
Cart cart = cartService.getCart(cartId);
if (cart == null) {
response.put("success", false);
response.put("error", "Cart not found");
return ResponseEntity.notFound().build();
}
response.put("success", true);
response.put("cart", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PostMapping("/cart/{cartId}/lines")
public ResponseEntity<Map<String, Object>> addLinesToCart(
@PathVariable String cartId,
@RequestBody List<CartLineInput> lines) {
Map<String, Object> response = new HashMap<>();
try {
Cart cart = cartService.addLinesToCart(cartId, lines);
response.put("success", true);
response.put("cart", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@PutMapping("/cart/{cartId}/lines")
public ResponseEntity<Map<String, Object>> updateCartLines(
@PathVariable String cartId,
@RequestBody List<CartLineUpdateInput> lines) {
Map<String, Object> response = new HashMap<>();
try {
Cart cart = cartService.updateCartLines(cartId, lines);
response.put("success", true);
response.put("cart", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
@DeleteMapping("/cart/{cartId}/lines")
public ResponseEntity<Map<String, Object>> removeLinesFromCart(
@PathVariable String cartId,
@RequestBody List<String> lineIds) {
Map<String, Object> response = new HashMap<>();
try {
Cart cart = cartService.removeLinesFromCart(cartId, lineIds);
response.put("success", true);
response.put("cart", cart);
return ResponseEntity.ok(response);
} catch (Exception e) {
response.put("success", false);
response.put("error", e.getMessage());
return ResponseEntity.badRequest().body(response);
}
}
}

Application Properties

# Shopify Configuration
shopify.store.domain=your-store.myshopify.com
shopify.storefront.access.token=your-storefront-access-token
shopify.api.version=2023-10
# Server Configuration
server.port=8080
# Logging
logging.level.com.shopify=INFO

Usage Examples

Basic Usage

package com.shopify.examples;
import com.shopify.client.ShopifyStorefrontClient;
import com.shopify.model.*;
import com.shopify.service.ProductService;
import com.shopify.service.CartService;
import java.util.List;
public class ShopifyExamples {
public static void main(String[] args) {
// Initialize client
ShopifyStorefrontClient client = new ShopifyStorefrontClient(
"your-store.myshopify.com",
"your-storefront-access-token"
);
ProductService productService = new ProductService(client);
CartService cartService = new CartService(client);
try {
// Example 1: Get products
ProductConnection products = productService.getProducts(10, null);
for (ProductEdge edge : products.getEdges()) {
Product product = edge.getNode();
System.out.println("Product: " + product.getTitle());
}
// Example 2: Get specific product
Product product = productService.getProductByHandle("my-product-handle");
if (product != null) {
System.out.println("Found product: " + product.getTitle());
}
// Example 3: Create cart
CartLineInput line = new CartLineInput();
line.setMerchandiseId("gid://shopify/ProductVariant/123456789");
line.setQuantity(1);
Cart cart = cartService.createCart(List.of(line));
System.out.println("Cart created: " + cart.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
}

Conclusion

This comprehensive Shopify Storefront API Java client provides:

  1. Complete GraphQL integration for queries and mutations
  2. Product management with pagination and search
  3. Cart operations for full e-commerce functionality
  4. Type-safe models for all Shopify entities
  5. Spring Boot integration for easy configuration
  6. Error handling and comprehensive logging

Key features:

  • Product catalog browsing with filters and search
  • Variant management with pricing and availability
  • Cart creation and management with line items
  • Image handling with URLs and transformations
  • Pagination support for large datasets
  • Input validation and error handling

This implementation enables Java applications to seamlessly integrate with Shopify stores, providing robust e-commerce capabilities for custom storefronts, mobile apps, and other customer-facing experiences.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper