mcpField
A
app-shopify-admin-graphql

app-shopify-admin-graphql

github.com/niccos-shopify-workspace开发241 次安装38 周前更新
代码仓库

关于 app-shopify-admin-graphql

面向使用 GraphQL 接入 Shopify Admin API 的开发者技能,涵盖请求认证、分页、限流和错误处理,并支持请求上下文及后台任务。

主要功能

  1. 01使用 Remix 管理端认证
  2. 02查询和修改 Shopify 数据
  3. 03为大型结果集分页
  4. 04通过重试处理限流
  5. 05处理 GraphQL 与 HTTP 错误

适用场景

  1. 01获取店铺、客户、订单或产品数据
  2. 02通过 GraphQL 更新 Shopify 记录
  3. 03在 Webhook 后台任务中调用 API

安装

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

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

安装后在 Claude Code 中输入/app-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