api-graphql
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
- 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.