AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Api Graphql

skill-dragnoir-shopify-agent-skills-api-graphql · by dragnoir

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.

No reviews yet
0 installs
38 views
0.0% view→install

Install

$ agentstack add skill-dragnoir-shopify-agent-skills-api-graphql

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-dragnoir-shopify-agent-skills-api-graphql)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
7mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Api Graphql? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

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": "Product description",
    "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  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](../app-development/SKILL.md) skill.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.