google-site-verification: google61fe8ba583a51912.html
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