All Skills 
api-graphql
Platform APIs & Operations
Work with Shopify GraphQL APIs including Admin API and Storefront API. Use this skill for querying and mutating Shopify data, managing products, orders, customers, handling pagination, working with metafields, and understanding rate limits. Covers authentication, queries, mutations, and webhooks.
Rapi-graphql
$
npx skills add dragnoir/Shopify-agent-skills --skill api-graphqlShopify GraphQL APIs
When to use this skill
Use this skill when:
- Querying Shopify data (products, orders, customers)
- Creating or updating resources via mutations
- Building integrations with Shopify stores
- Working with metafields and metaobjects
- Handling pagination in API responses
- Managing API authentication and rate limits
API Types
Admin API
- Full store access (backend only)
- Requires authentication (OAuth or API key)
- Used by apps and integrations
Storefront API
- Public storefront access
- Uses public access token
- Safe for frontend/client-side
Getting Started
Admin API Authentication
// Using @shopify/shopify-api
import { shopifyApi, LATEST_API_VERSION } from "@shopify/shopify-api";
const shopify = shopifyApi({
apiKey: process.env.SHOPIFY_API_KEY,
apiSecretKey: process.env.SHOPIFY_API_SECRET,
scopes: ["read_products", "write_products"],
hostName: "your-app.com",
apiVersion: LATEST_API_VERSION,
});
Making Requests
// In a Remix app route
import { authenticate } from "../shopify.server";
export async function loader({ request }) {
const { admin } = await authenticate.admin(request);
const response = await admin.graphql(`
query {
products(first: 10) {
nodes {
id
title
}
}
}
`);
return response.json();
}
Common Queries
Products
# Get products with variants
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
handle
description
status
vendor
productType
tags
featuredImage {
url
altText
}
variants(first: 10) {
nodes {
id
title
sku
price
compareAtPrice
inventoryQuantity
selectedOptions {
name
value
}
}
}
priceRange {
minVariantPrice {
amount
currencyCode
}
maxVariantPrice {
amount
currencyCode
}
}
}
}
}
# Get single product by ID
query GetProduct($id: ID!) {
product(id: $id) {
id
title
description
variants(first: 100) {
nodes {
id
title
price
}
}
}
}
# Get product by handle
query GetProductByHandle($handle: String!) {
productByHandle(handle: $handle) {
id
title
}
}
Orders
query GetOrders($first: Int!, $query: String) {
orders(first: $first, query: $query) {
nodes {
id
name
email
createdAt
displayFinancialStatus
displayFulfillmentStatus
totalPriceSet {
shopMoney {
amount
currencyCode
}
}
customer {
id
firstName
lastName
email
}
lineItems(first: 50) {
nodes {
id
title
quantity
variant {
id
sku
}
originalTotalSet {
shopMoney {
amount
}
}
}
}
shippingAddress {
address1
city
province
country
zip
}
}
}
}
Customers
query GetCustomers($first: Int!, $query: String) {
customers(first: $first, query: $query) {
nodes {
id
firstName
lastName
email
phone
ordersCount
totalSpent {
amount
currencyCode
}
addresses(first: 5) {
address1
city
province
country
zip
}
tags
createdAt
}
}
}
Collections
query GetCollection($handle: String!) {
collectionByHandle(handle: $handle) {
id
title
description
products(first: 20) {
nodes {
id
title
featuredImage {
url
}
}
}
}
}
Inventory
query GetInventory($locationId: ID!) {
location(id: $locationId) {
inventoryLevels(first: 100) {
nodes {
id
available
item {
id
variant {
id
displayName
sku
}
}
}
}
}
}
Common Mutations
Create Product
mutation CreateProduct($input: ProductInput!) {
productCreate(input: $input) {
product {
id
title
handle
}
userErrors {
field
message
}
}
}
Variables:
{
"input": {
"title": "New Product",
"descriptionHtml": "<p>Product description</p>",
"vendor": "My Store",
"productType": "Apparel",
"tags": ["new", "featured"],
"variants": [
{
"price": "29.99",
"sku": "NEW-001",
"inventoryManagement": "SHOPIFY",
"inventoryPolicy": "DENY"
}
]
}
}
Update Product
mutation UpdateProduct($input: ProductInput!) {
productUpdate(input: $input) {
product {
id
title
}
userErrors {
field
message
}
}
}
Variables:
{
"input": {
"id": "gid://shopify/Product/123456",
"title": "Updated Title",
"tags": ["sale", "featured"]
}
}
Create Order
mutation CreateDraftOrder($input: DraftOrderInput!) {
draftOrderCreate(input: $input) {
draftOrder {
id
invoiceUrl
}
userErrors {
field
message
}
}
}
Update Inventory
mutation AdjustInventory($input: InventoryAdjustQuantityInput!) {
inventoryAdjustQuantity(input: $input) {
inventoryLevel {
available
}
userErrors {
field
message
}
}
}
Variables:
{
"input": {
"inventoryLevelId": "gid://shopify/InventoryLevel/123456",
"availableDelta": 10
}
}
Metafields
Read Metafields
# Product metafields
query GetProductMetafields($id: ID!) {
product(id: $id) {
metafields(first: 20) {
nodes {
id
namespace
key
value
type
}
}
# Specific metafield
careInstructions: metafield(namespace: "custom", key: "care_instructions") {
value
}
}
}
Write Metafields
mutation SetMetafields($metafields: [MetafieldsSetInput!]!) {
metafieldsSet(metafields: $metafields) {
metafields {
id
key
value
}
userErrors {
field
message
}
}
}
Variables:
{
"metafields": [
{
"ownerId": "gid://shopify/Product/123456",
"namespace": "custom",
"key": "care_instructions",
"value": "Machine wash cold",
"type": "single_line_text_field"
},
{
"ownerId": "gid://shopify/Product/123456",
"namespace": "custom",
"key": "dimensions",
"value": "{\"width\": 10, \"height\": 20, \"depth\": 5}",
"type": "json"
}
]
}
Metafield Types
| Type | Description | Example Value |
|---|---|---|
single_line_text_field | Short text | "Hello" |
multi_line_text_field | Long text | "Line 1\nLine 2" |
number_integer | Integer | "42" |
number_decimal | Decimal | "19.99" |
boolean | True/False | "true" |
date | Date | "2025-01-15" |
json | JSON object | "{\"key\": \"value\"}" |
url | URL | "https://example.com" |
color | Color hex | "#FF0000" |
Pagination
Cursor-Based Pagination
async function getAllProducts(admin) {
let products = [];
let hasNextPage = true;
let cursor = null;
while (hasNextPage) {
const response = await admin.graphql(
`
query GetProducts($first: Int!, $after: String) {
products(first: $first, after: $after) {
pageInfo {
hasNextPage
endCursor
}
nodes {
id
title
}
}
}
`,
{
variables: { first: 50, after: cursor },
},
);
const data = await response.json();
products = [...products, ...data.data.products.nodes];
hasNextPage = data.data.products.pageInfo.hasNextPage;
cursor = data.data.products.pageInfo.endCursor;
}
return products;
}
Rate Limits
Understanding Cost
query {
products(first: 100) {
nodes {
id
title
}
}
}
Response includes:
{
"extensions": {
"cost": {
"requestedQueryCost": 102,
"actualQueryCost": 52,
"throttleStatus": {
"maximumAvailable": 2000,
"currentlyAvailable": 1948,
"restoreRate": 100
}
}
}
}
Handling Rate Limits
async function queryWithRetry(admin, query, variables, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await admin.graphql(query, { variables });
const data = await response.json();
if (data.errors?.some((e) => e.extensions?.code === "THROTTLED")) {
const waitTime = Math.pow(2, attempt) * 1000;
await new Promise((resolve) => setTimeout(resolve, waitTime));
continue;
}
return data;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
}
}
}
Storefront API
Authentication
const storefrontClient = new StorefrontApiClient({
privateAccessToken: process.env.STOREFRONT_ACCESS_TOKEN,
storeDomain: "your-store.myshopify.com",
apiVersion: "2025-01",
});
Product Query (Storefront)
query GetProduct($handle: String!) {
product(handle: $handle) {
id
title
description
images(first: 5) {
nodes {
url
altText
}
}
variants(first: 100) {
nodes {
id
title
price {
amount
currencyCode
}
availableForSale
}
}
}
}
Cart Operations (Storefront)
# Create cart
mutation CreateCart($lines: [CartLineInput!]!) {
cartCreate(input: { lines: $lines }) {
cart {
id
checkoutUrl
lines(first: 10) {
nodes {
id
quantity
merchandise {
... on ProductVariant {
title
}
}
}
}
}
}
}
# Add to cart
mutation AddToCart($cartId: ID!, $lines: [CartLineInput!]!) {
cartLinesAdd(cartId: $cartId, lines: $lines) {
cart {
id
lines(first: 10) {
nodes {
id
quantity
}
}
}
}
}
Webhooks
Subscribe to Webhooks
mutation WebhookSubscriptionCreate(
$topic: WebhookSubscriptionTopic!
$webhookSubscription: WebhookSubscriptionInput!
) {
webhookSubscriptionCreate(
topic: $topic
webhookSubscription: $webhookSubscription
) {
webhookSubscription {
id
topic
endpoint {
... on WebhookHttpEndpoint {
callbackUrl
}
}
}
userErrors {
field
message
}
}
}
Variables:
{
"topic": "PRODUCTS_CREATE",
"webhookSubscription": {
"callbackUrl": "https://your-app.com/webhooks",
"format": "JSON"
}
}
Webhook Topics
| Topic | Description |
|---|---|
PRODUCTS_CREATE | Product created |
PRODUCTS_UPDATE | Product updated |
PRODUCTS_DELETE | Product deleted |
ORDERS_CREATE | Order placed |
ORDERS_UPDATED | Order modified |
ORDERS_PAID | Order paid |
CUSTOMERS_CREATE | Customer created |
INVENTORY_LEVELS_UPDATE | Inventory changed |
Bulk Operations
Bulk Query
mutation BulkProducts {
bulkOperationRunQuery(
query: """
{
products {
edges {
node {
id
title
variants {
edges {
node {
id
sku
price
}
}
}
}
}
}
}
"""
) {
bulkOperation {
id
status
}
userErrors {
field
message
}
}
}
Poll Bulk Operation
query BulkOperationStatus {
currentBulkOperation {
id
status
objectCount
url
}
}
Best Practices
- Request only needed fields - Reduces cost and response size
- Use fragments - Reuse common field selections
- Handle errors - Check for userErrors in mutations
- Implement pagination - Don't request all records at once
- Monitor rate limits - Use throttle status in responses
- Use bulk operations - For large data exports
Resources
- Admin API Reference
- Storefront API Reference
- GraphQL Basics
- Rate Limits
- Webhooks Reference
- GraphiQL Explorer
For app integration, see the app-development skill.