api-graphql

📁 dragnoir/shopify-agent-skills 📅 2 days ago
1
总安装量
1
周安装量
#52629
全站排名
安装命令
npx skills add https://github.com/dragnoir/shopify-agent-skills --skill api-graphql

Agent 安装分布

amp 1
openclaw 1
opencode 1
kimi-cli 1
codex 1
github-copilot 1

Skill 文档

Shopify 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

  1. Request only needed fields – Reduces cost and response size
  2. Use fragments – Reuse common field selections
  3. Handle errors – Check for userErrors in mutations
  4. Implement pagination – Don’t request all records at once
  5. Monitor rate limits – Use throttle status in responses
  6. Use bulk operations – For large data exports

Resources

For app integration, see the app-development skill.