mcpField
S
shopify-admin-graphql

shopify-admin-graphql

github.com/tamiror6开发38106 次安装38 周前更新
代码仓库

关于 shopify-admin-graphql

shopify-admin-graphql 是面向通过 GraphQL 调用 Shopify Admin API 的代码开发技能,适用于应用和后台任务。内容涵盖身份验证、常见查询、分页、限流和错误处理。

主要功能

  1. 01通过 Remix 身份验证访问 API
  2. 02使用在线和离线管理会话
  3. 03查询店铺、产品、客户和订单
  4. 04为大型数据集实现分页
  5. 05处理限流和 GraphQL 错误
  6. 06遵循 GraphQL 查询最佳实践

适用场景

  1. 01在 Remix loaders 中编写 Shopify 数据查询
  2. 02通过 GraphQL mutation 更新 Shopify 记录
  3. 03在无请求上下文时处理 webhook
  4. 04通过分页和重试获取大型列表

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tamiror6/shopify-app-skills --skill shopify-admin-graphql

安装后在 Claude Code 中输入/shopify-admin-graphql确认已加载。

详细介绍

Shopify Admin GraphQL

在添加或修改通过 GraphQL 调用 Shopify Admin API 的代码时使用此技能。

使用场景

  • 查询 Shopify 数据(商店、客户、订单、产品、库存)
  • 修改 Shopify 数据(创建或更新客户、订单、产品)
  • 为大型数据集实现分页
  • 处理 API 节流和速率限制
  • 使用 metafield 或其他 Shopify 资源

获取 GraphQL 客户端

在 Remix 加载器/操作中(推荐)

使用 @shopify/shopify-app-remix 中的 authenticate.admin():

import { authenticate } from "../shopify.server";

export const loader = async ({ request }: LoaderFunctionArgs) => {
  const { admin } = await authenticate.admin(request);
  
  const response = await admin.graphql(`
    query GetShopDetails {
      shop {
        id
        name
        myshopifyDomain
        plan {
          displayName
        }
      }
    }
  `);
  
  const { data } = await response.json();
  return json({ shop: data.shop });
};
使用变量
export const action = async ({ request }: ActionFunctionArgs) => {
  const { admin } = await authenticate.admin(request);
  const formData = await request.formData();
  const email = formData.get("email") as string;
  
  // Validate email format to prevent query manipulation
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  if (!email || !emailRegex.test(email)) {
    return json({ error: "Invalid email format" }, { status: 400 });
  }
  
  // Escape quotes and wrap in quotes to treat as literal value
  const sanitizedEmail = email.replace(/"/g, '\\"');
  
  const response = await admin.graphql(`
    query FindCustomerByEmail($query: String!) {
      customers(first: 1, query: $query) {
        edges {
          node {
            id
            email
            phone
            firstName
            lastName
          }
        }
      }
    }
  `, {
    variables: { query: `email:"${sanitizedEmail}"` }
  });
  
  const { data } = await response.json();
  return json({ customer: data.customers.edges[0]?.node });
};
后台任务 / Webhook(离线访问)

没有请求上下文时,请使用离线会话令牌:

import { unauthenticated } from "../shopify.server";

export async function processWebhook(shop: string) {
  const { admin } = await unauthenticated.admin(shop);
  
  const response = await admin.graphql(`
    query GetShop {
      shop {
        name
      }
    }
  `);
  
  const { data } = await response.json();
  return data.shop;
}

常见查询模式

商店详情
query GetShopDetails {
  shop {
    id
    name
    email
    myshopifyDomain
    primaryDomain {
      url
    }
    plan {
      displayName
    }
    currencyCode
    timezoneAbbreviation
  }
}
带分页的产品查询
query GetProducts($first: Int!, $after: String) {
  products(first: $first, after: $after) {
    pageInfo {
      hasNextPage
      endCursor
    }
    edges {
      node {
        id
        title
        handle
        status
        variants(first: 10) {
          edges {
            node {
              id
              title
              price
              sku
            }
          }
        }
      }
    }
  }
}
客户查找
query FindCustomer($query: String!) {
  customers(first: 1, query: $query) {
    edges {
      node {
        id
        email
        phone
        firstName
        lastName
        ordersCount
        totalSpent
      }
    }
  }
}
按 ID 查询订单
query GetOrder($id: ID!) {
  order(id: $id) {
    id
    name
    email
    phone
    totalPriceSet {
      shopMoney {
        amount
        currencyCode
      }
    }
    lineItems(first: 50) {
      edges {
        node {
          title
          quantity
          variant {
            id
            sku
          }
        }
      }
    }
    shippingAddress {
      address1
      city
      country
    }
  }
}

处理节流

Shopify 使用漏桶算法限制速率。对于批量操作或后台任务,请实现重试逻辑:

interface RetryOptions {
  maxRetries?: number;
  initialDelay?: number;
  maxDelay?: number;
}

async function executeWithRetry<T>(
  admin: AdminApiContext,
  query: string,
  variables?: Record<string, unknown>,
  options: RetryOptions = {}
): Promise<T> {
  const { maxRetries = 3, initialDelay = 1000, maxDelay = 10000 } = options;
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      const response = await admin.graphql(query, { variables });
      const result = await response.json();
      
      if (result.errors?.some((e: any) => e.extensions?.code === "THROTTLED")) {
        throw new Error("THROTTLED");
      }
      
      return result.data as T;
    } catch (error) {
      if (attempt === maxRetries) throw error;
      
      const delay = Math.min(initialDelay * Math.pow(2, attempt), maxDelay);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw new Error("Max retries exceeded");
}

错误处理

const response = await admin.graphql(query, { variables });
const { data, errors } = await response.json();

if (errors) {
  // Handle GraphQL errors
  console.error("GraphQL errors:", errors);
  
  // Check for specific error types
  const throttled = errors.some((e: any) => 
    e.extensions?.code === "THROTTLED"
  );
  
  const notFound = errors.some((e: any) => 
    e.message?.includes("not found")
  );
  
  if (throttled) {
    // Retry with backoff
  }
}

最佳实践

  1. 使用操作名称,便于调试:query GetShopDetails { ... }
  2. 仅请求所需字段,以减小响应体并提升性能
  3. 对列表使用分页——不要请求无界数据
  4. 妥善处理错误——同时检查 errors 数组和 HTTP 错误
  5. 为后台任务实现带指数退避的重试
  6. 对多个查询中重复的字段选择使用片段
  7. 对于包含关联关系的复杂查询,优先使用 GraphQL 而非 REST

参考资料

评论

支持 Markdown