shopify-app-react-router

SKILL开发
开发 未标注权限 许可证不明确
安装这个技能

构建 Shopify 嵌入式应用(Embedded App)的开发技能:基于 @shopify/shopify-app-react-router + React Router v7 + Admin API 2026-04。覆盖 shopifyApp 配置、authenticate.admin 的 GraphQL/REST 调用、Webhook 校验、Session 存储、订阅计费、App Bridge 导航、部署,并新增:适用场景判断、安全最佳实践、错误处理与容错、最佳实践、常见问题 FAQ、高级场景。Guides building Shopify embedded apps with the shopify-app-react-router package, React Router v7, and Shopify Admin API 2026-04.

一句话选型结论

按资料字段整理

shopify-app-react-router是开发类的 Agent Skill,安装方式未标注,权限级别未标注,许可证不明确,有可直接复制的安装命令。技能本质是给模型读的说明书,效果取决于模型与客户端;装完先用一个小任务验证,再放进正式流程。

分类开发 安装方式未标注 权限级别未标注权限 许可证许可证不明确
累计安装 未标注 本周 未标注
GitHub 星标 未标注 仓库 未标注
下载量 未标注 技能市场下载数
权限级别 未标注权限 安装前请确认风险范围
许可证 许可证不明确 未说明

安装

未标注
在支持 Skills 的客户端里执行,装完问一句「你有哪些技能」验证
skillhub install user_4fdb9f62/shopify-api-2026-04
  1. 1
    确认客户端支持

    确认你的客户端支持 Skills(Claude、Codex、Cursor 等),并升级到较新的版本。

  2. 2
    拿到技能文件

    执行上面的命令安装;也可以把仓库里的 SKILL.md 手动放进客户端的 skills 目录。

  3. 3
    刷新技能列表

    重新打开客户端,或刷新技能列表,让新放进来的技能被加载。

  4. 4
    验证是否生效

    问一句「你有哪些技能」,确认列表里出现它,再拿一个小任务试跑。

技能档案

开发
字段来自技能仓库与公开索引,中文说明由本站标注
作者 / 维护方user_4fdb9f62仓库 未标注 · 版本 未标注
宿主客户端未标注技能可被这些客户端加载
安装方式未标注安装包 community
权限级别未标注权限安装前请确认风险范围
许可证未说明许可证不明确,商用前请自行确认条款
仓库可用性available技能仓库当前是否可访问
最近核验未标注上游最后核验时间 · 数据更新 1791729067
正文体量30.2 KBSKILL.md 原文大小,越大说明说明书写得越细

典型使用场景

3 类
按分类整理的常见用法,具体以 SKILL.md 为准
写代码与改 bug

按需求补代码、定位并修掉报错。

读仓库与生成改动

让模型读懂项目结构,再给出可落地的改动。

跑测试与排查故障

按现象排查问题,补测试用例并复跑。

选择建议

装之前先看这几条
按条目字段给出的判断,不替你决定用不用

值得选的地方

  • 有可直接复制的安装命令,不用自己翻文档找入口。

先想清楚的地方

  • 没有标注权限级别,实际能做什么要装好后确认。
  • 许可证不明确,商用或二次分发前先到仓库确认条款。
  • 没有做站内核验,内容以作者仓库为准。
  • 技能只是说明书,实际效果取决于模型与客户端,装完先用小任务试一遍。

更适合

  • 需要在开发场景里给模型补一套做法的人。
  • 愿意先读一遍 SKILL.md、确认它会让模型做什么的人。

不太适合

  • 对许可证要求很严、必须明确可商用的人。
  • 只想装完就自动变强、不打算看说明书的人。

同类对比

6 个同类
同分类里挑最接近的几条,字段以各自详情页为准
技能权限级别安装方式累计安装宿主客户端安装命令
shopify-app-react-router当前 未标注权限 未标注 未标注 未标注 有
技能审查员 浏览器控制 ClawHub 市场 877.9 万 OpenClaw 有
Vercel React 最佳实践 浏览器控制 GitHub 仓库 849.1 万 Codex、Claude、Cursor 有
GitHub 代码协作 联网访问 ClawHub 市场 763.3 万 OpenClaw 有
网页设计指南 联网访问 GitHub 仓库 668.6 万 Codex、Claude、Cursor 有
本体论 只读 ClawHub 市场 663.7 万 OpenClaw 有

SKILL.md 全文

30.2 KB
模型实际读到的说明书原文,来自技能仓库

Shopify App React Router — Development Skill

Overview

This skill provides end-to-end guidance for building Shopify embedded apps using:

  • @shopify/shopify-app-react-router v1.x (latest)
  • React Router v7 (framework mode)
  • Shopify Admin API 2026-04
  • TypeScript

The package wraps authentication (OAuth token exchange, session management), GraphQL Admin API access, webhook verification, billing, and App Bridge integration for React Router apps.


适用场景与快速判断(When to Use This Skill)

> 本技能用于构建 Shopify 嵌入式应用(Embedded App)。动手前先确认是否真的需要它。

✅ 适合使用(Use this when…)

  • 你要开发 嵌入在 Shopify Admin 后台 运行的应用(左侧菜单 + App Bridge 导航)。
  • 技术栈为 React Router v7(framework mode)+ TypeScript + Node.js。
  • 需要服务端处理 OAuth 登录、Session 管理、Admin GraphQL/REST API 调用、Webhook 校验、订阅计费。
  • 需要官方 authenticate.admin / webhook / public 鉴权封装,而非自己手写 OAuth。

❌ 不适合使用(Do NOT use when…)

  • 只是想 改店铺主题/页面(用 Liquid)——请用 shopify-liquid / shopify-theme-pro 相关技能。
  • 要做 自定义店铺前台(Headless Storefront)——请用 shopify-hydrogen / shopify-storefront-graphql。
  • 只想 抓取/分析已有店铺数据(无应用开发)——直接用 Admin API 客户端即可,不需要本套 React Router 脚手架。
  • 纯静态/营销页面——普通前端框架即可,无需 Shopify 鉴权。

🔀 替代方案速查(Alternatives)

目标 推荐技能 / 方案
嵌入式后台应用(本文) shopify-app-react-router
主题 / Liquid 定制 shopify-liquid、shopify-theme-pro
无头店铺前台 shopify-hydrogen、shopify-storefront-graphql
纯数据查询/运营 shopify-admin、shopify-admin-api

🚦 触发信号(何时查阅本技能)

当用户/智能体出现以下意图时,应调用本技能(而不是泛泛去搜 Shopify 文档):
- 「我要做一个 Shopify 后台应用」/「开发嵌入式 App」/「App 要出现在店铺后台左侧菜单」
- 「用 React Router v7 搭 Shopify 应用」/「shopify-app-react-router 怎么用」
- 「怎么接 Shopify Admin API(GraphQL/REST)」/「OAuth 登录 / Session 怎么存」
- 「怎么处理 Webhook 校验」/「怎么做 订阅计费 billing」/「App Bridge 导航怎么配」
- 「应用 部署上线 到 Fly/Railway/Vercel 等」
> 若意图是「改主题/Liquid」「做无头店铺前台」「只查数据」,按上文「❌ 不适合」转对应技能。


快速上手(Quick Start)

前置条件:Node ≥ 18、已创建 Shopify Partner 应用(拿到 API Key / Secret)、一个测试店铺。

# 1) 用官方脚手架创建(含 shopify.server.ts、Prisma、routes 模板)
npm create @shopify/app@latest

# 2) 安装依赖
npm install

# 3) 填写环境变量(.env)
# SHOPIFY_API_KEY=xxx
# SHOPIFY_API_SECRET=xxx
# SHOPIFY_APP_URL=https://<your-tunnel>/  # 开发用 cloudflare tunnel / ngrok

# 4) 本地启动(自带隧道)
npm run dev

# 5) 在 Partner Dashboard 配置 App URL 与 Redirect URL,安装到测试店

最小可运行骨架(核心 3 文件):
- app/shopify.server.ts —— shopifyApp() 配置(见第 1 节)。
- app/routes/app.tsx —— 后台布局 + AppProvider + authenticate.admin(见第 7 节)。
- app/routes/webhooks.tsx —— Webhook 校验(见第 5 节)。

> 完成这 3 步即可在店铺后台看到应用菜单并调用 Admin API。后续按需在 loader/action 中调用 admin.graphql(...)。


Package Architecture

@shopify/shopify-app-react-router
├── /server          → shopifyApp(), authenticate, unauthenticated, boundary, ApiVersion, LogSeverity
├── /react           → AppProvider (client component for embedded admin UI)
└── /test-helpers    → testConfig() for integration tests

Key Exports (from @shopify/shopify-app-react-router/server)

Export Purpose
shopifyApp(config) Initialize the Shopify app with credentials and options
authenticate.admin(request) Authenticate admin-embedded requests, returns AdminContext
authenticate.webhook(request) Verify webhook HMAC signatures
authenticate.public(request) Authenticate public (unauthenticated) app proxy/checkout requests
unauthenticated.admin(shop) Background/admin API access without a request context
boundary.error(error) Error boundary compatible with Shopify CSP headers
boundary.headers(args) Headers function for CSP compliance
ApiVersion Enum of all API versions (use ApiVersion.April26)
LogSeverity Log level: Error, Warning, Info, Debug

Key Exports (from @shopify/shopify-app-react-router/react)

Export Purpose
AppProvider Wraps admin routes with App Bridge context (embedded, apiKey)

1. shopifyApp Configuration

Create /app/shopify.server.ts as the central configuration file:

import {
  ApiVersion,
  LogSeverity,
  shopifyApp,
} from "@shopify/shopify-app-react-router/server";

const shopify = shopifyApp({
  // Required
  apiKey: process.env.SHOPIFY_API_KEY!,
  apiSecretKey: process.env.SHOPIFY_API_SECRET!,
  appUrl: process.env.SHOPIFY_APP_URL!,
  apiVersion: ApiVersion.April26, // 2026-04

  // Optional
  scopes: ["read_products", "write_products", "read_orders"],
  logger: { level: LogSeverity.Debug },
  isEmbeddedApp: true,           // default: true
  distribution: "AppDistribution.AppStore", // or ShopifyAdmin for custom apps

  // Session storage — use Prisma, SQLite, PostgreSQL, MongoDB, Redis, etc.
  sessionStorage: new PrismaSessionStorage(prisma),

  // Billing — optional, for apps with charges
  billing: undefined,

  // Future flags
  future: { v3_authenticatePublic: true },
});

export default shopify;
export const authenticate = shopify.authenticate;
export const unauthenticated = shopify.unauthenticated;
export const sessionStorage = shopify.sessionStorage;

ApiVersion Enum (relevant for 2026-04)

ApiVersion.April26  // "2026-04"
ApiVersion.January26 // "2026-01"
ApiVersion.October25 // "2025-10"
// ... etc

2. Authenticate Admin Requests

Basic Pattern (Loader / Action)

import { authenticate } from "~/shopify.server";
import type { LoaderFunctionArgs, ActionFunctionArgs } from "react-router";

// In a loader
export const loader = async ({ request }: LoaderFunctionArgs) => {
  const { admin, session, billing, cors, redirect } =
    await authenticate.admin(request);

  // admin  → { graphql, rest (via shopify-api) }
  // session → { id, shop, accessToken, isOnline, scope, expires }
  // billing → { require, check, cancel, request }
  // cors    → function to wrap cross-origin responses
  // redirect → { adminPath, embedApp }

  return null;
};

// In an action
export const action = async ({ request }: ActionFunctionArgs) => {
  const { admin } = await authenticate.admin(request);
  // ...
};

AdminContext Return Type

After authenticate.admin(request), the returned AdminContext contains:

Property Type Description
admin AdminApiContext Authenticated API client (graphql + rest)
session Session Active session: id, shop, accessToken, isOnline, scope, expires
billing BillingContext Billing helpers (require, check, cancel, request)
cors (data) => Response Wrap responses with CORS headers for cross-origin extensions
redirect AdminRedirect Redirect helpers (adminPath, embedApp)
botOrigin string Bot protection origin URL

3. GraphQL Admin API (primary API)

export const action = async ({ request }: ActionFunctionArgs) => {
  const { admin } = await authenticate.admin(request);

  // admin.graphql(query, options) → Response
  const response = await admin.graphql(
    `#graphql
    query GetProducts($first: Int!) {
      products(first: $first) {
        nodes {
          id
          title
          variants(first: 5) {
            nodes { id price sku inventoryQuantity }
          }
        }
      }
    }`,
    {
      variables: { first: 10 },
      // Optional: Retry configuration
      // retries: 2,
    }
  );

  const { data, errors } = await response.json();

  if (errors) {
    console.error("GraphQL errors:", errors);
    return { error: "Query failed" };
  }

  return { products: data.products.nodes };
};

GraphQL Client Details

  • URL: https://{shop}/admin/api/2026-04/graphql.json
  • Auth: Automatically injected X-Shopify-Access-Token from session
  • Return: Standard Response object — call .json() to parse
  • #graphql tag: Optional comment for IDE GraphQL syntax highlighting
  • Retries: Optional { retries: 2 } for automatic retry on rate limits
  • Typed responses: Use @shopify/api-codegen-preset for TypeScript types

Common GraphQL Operations (2026-04)

# Fetch products with variants and inventory
query {
  products(first: 50) {
    nodes {
      id
      title
      handle
      status
      vendor
      productType
      tags
      totalInventory
      variants(first: 50) {
        nodes { id title price sku inventoryQuantity }
      }
    }
  }
}

# Fetch orders with line items
query {
  orders(first: 50, reverse: true) {
    nodes {
      id
      name
      createdAt
      totalPriceSet { shopMoney { amount currencyCode } }
      displayFinancialStatus
      lineItems(first: 20) {
        nodes { title quantity originalUnitPriceSet { shopMoney { amount } } }
      }
    }
  }
}

# Bulk product update
mutation {
  productUpdate(input: { id: "gid://shopify/Product/123", title: "New Title" }) {
    product { id title }
  }
}

4. REST Admin API (via direct fetch)

The package does NOT expose admin.rest() natively in v1. For REST endpoints, use direct fetch with the session access token:

export const action = async ({ request }: ActionFunctionArgs) => {
  const { session } = await authenticate.admin(request);

  // Direct REST call
  const response = await fetch(
    `https://${session.shop}/admin/api/2026-04/orders.json?status=any&limit=250`,
    {
      headers: {
        "X-Shopify-Access-Token": session.accessToken!,
        "Content-Type": "application/json",
      },
    }
  );

  const data = await response.json();
  return { orders: data.orders };
};

REST rate limits (2026-04)

  • REST: 2 requests/second (leaky bucket, 40 burst)
  • GraphQL: 50 points/second (1 point per query cost)

5. Webhook Handling

// /app/routes/webhooks.tsx (NOT nested under admin layout)
export const action = async ({ request }: ActionFunctionArgs) => {
  const { topic, shop, payload, admin } =
    await authenticate.webhook(request);

  console.log(`Webhook: ${topic} from ${shop}`);

  // Access the webhook body directly
  // payload contains the parsed webhook body

  // Optionally use admin API in the handler
  if (topic === "PRODUCTS_CREATE") {
    // Do something with the product
  }

  return new Response(null, { status: 200 });
};

Common webhook topics (2026-04)

Topic Trigger
PRODUCTS_CREATE Product created
PRODUCTS_UPDATE Product updated
PRODUCTS_DELETE Product deleted
ORDERS_CREATE Order created
ORDERS_UPDATED Order updated
ORDERS_CANCELLED Order cancelled
ORDERS_FULFILLED Order fulfilled
CUSTOMERS_CREATE Customer created
APP_UNINSTALLED App uninstalled (clean up session)
GDPR_REQUEST GDPR data request

6. Background Jobs (unauthenticated.admin)

For background tasks (cron jobs, queue workers, 3rd-party webhooks):

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

async function processBackgroundJob(shop: string) {
  const { admin } = await unauthenticated.admin(shop);

  const response = await admin.graphql(
    `#graphql
    query { products(first: 10) { nodes { id title } } }
    `,
  );

  // Process data...
}

7. Admin Layout & Route Structure

app/
├── shopify.server.ts       # shopifyApp() config → default export
├── routes/
│   ├── app.tsx             # Admin layout route (auth + AppBridge)
│   ├── app._index.tsx      # Admin home page
│   ├── app.products.tsx    # Product management page
│   ├── app.orders.tsx      # Order management page
│   └── webhooks.tsx        # Webhook handler (outside admin layout)

Admin Layout Route (app/routes/app.tsx)

import { Link, Outlet, useLoaderData, useRouteError } from "react-router";
import { boundary } from "@shopify/shopify-app-react-router/server";
import { AppProvider } from "@shopify/shopify-app-react-router/react";
import { NavMenu } from "@shopify/app-bridge-react";
import { authenticate } from "~/shopify.server";

export const loader = async ({ request }) => {
  await authenticate.admin(request);
  return { apiKey: process.env.SHOPIFY_API_KEY || "" };
};

export default function App() {
  const { apiKey } = useLoaderData();

  return (
    <AppProvider embedded apiKey={apiKey}>
      <NavMenu>
        <Link to="/app" rel="home">Home</Link>
        <Link to="/app/products">Products</Link>
      </NavMenu>
      <Outlet />
    </AppProvider>
  );
}

export function ErrorBoundary() {
  return boundary.error(useRouteError());
}

export const headers = (args) => boundary.headers(args);

8. Session Storage

Prisma + SQLite (default template)

import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import prisma from "./db.server";

const shopify = shopifyApp({
  // ...
  sessionStorage: new PrismaSessionStorage(prisma),
});

Other adapters

Package Storage
@shopify/shopify-app-session-storage-prisma Prisma (any DB)
@shopify/shopify-app-session-storage-postgresql PostgreSQL
@shopify/shopify-app-session-storage-mysql MySQL
@shopify/shopify-app-session-storage-mongodb MongoDB
@shopify/shopify-app-session-storage-redis Redis
@shopify/shopify-app-session-storage-dynamodb DynamoDB
@shopify/shopify-app-session-storage-sqlite SQLite

9. Billing (Subscription Charges)

import { BillingInterval } from "@shopify/shopify-app-react-router/server";

// In shopifyApp config:
const shopify = shopifyApp({
  billing: {
    ["My Plan"]: {
      amount: 9.99,
      currencyCode: "USD",
      interval: BillingInterval.Monthly,
      trialDays: 7,
    },
  },
  // ...
});

// In a loader/action:
export const loader = async ({ request }) => {
  const { billing, session } = await authenticate.admin(request);

  // Check if shop has active subscription
  const hasPayment = await billing.check({
    plans: ["My Plan"],
    isTest: true, // During development
  });

  if (!hasPayment) {
    // Redirect to billing page
    const billingUrl = await billing.request({
      plan: "My Plan",
      isTest: true,
    });
    return redirect(billingUrl);
  }

  return { hasPayment };
};

10. Testing

import { testConfig } from "@shopify/shopify-app-react-router/test-helpers";

// In shopify.server.ts:
let config = { apiKey: "...", apiSecretKey: "...", appUrl: "...", apiVersion: ApiVersion.April26 };

if (process.env.SHOPIFY_TESTING) {
  Object.assign(config, testConfig());
}

// For e2e testing with real session storage:
if (process.env.SHOPIFY_TESTING === "e2e") {
  Object.assign(config, testConfig({ sessionStorage }));
}

11. Deployment

  • Supported platforms: Any Node.js host (Fly.io, Railway, Render, Heroku, AWS, Cloudflare Workers with nodejs compat)
  • Required env vars: SHOPIFY_API_KEY, SHOPIFY_API_SECRET, SHOPIFY_APP_URL
  • Database requirement: Session storage needs a persistent database
  • Scopes: Configure in shopify.app.toml or Partner Dashboard
  • Webhooks: Register in shopify.app.toml or via Partner Dashboard
  • GDPR: Implement mandatory webhooks: customers/data_request, customers/redact, shop/redact

12. Top Shopify Admin API 2026-04 Endpoints

Refer to references/admin-api-2026-04.md for the complete reference of commonly used GraphQL queries, mutations, and REST endpoints for the Shopify Admin API 2026-04 version, including product management, order processing, inventory control, customer data, and store configuration.


13. Security Best Practices(安全最佳实践)

> 嵌入式应用直接持有店铺访问令牌,安全是底线。下列为必做项。

13.1 密钥与凭证管理

  • SHOPIFY_API_SECRET、accessToken 只能存在于服务端环境变量,绝对不要写进前端代码或通过 API 返回给浏览器。
  • 使用 .env(已 gitignore),禁止提交 .env 到仓库;CI/CD 用平台密钥注入。
  • 轮换 Secret 时同步更新环境变量与 Partner Dashboard。

13.2 Webhook HMAC 校验(必须)

  • 永远走 authenticate.webhook(request),它会校验 X-Shopify-Hmac-SHA256 头。
  • 切勿在 webhook 路由里手动 request.json() 后直接处理——未经校验的请求可能来自伪造源。
  • 处理失败返回 401/500 让 Shopify 重试,不要用 200 吞掉错误。

13.3 Session 与令牌安全

  • Session Storage 必须持久化且加密传输;生产环境用 PostgreSQL/Redis,不要用内存存储(多实例会丢 session)。
  • isOnline session 含用户级令牌,避免长期留存;离线任务用 unauthenticated.admin(shop)。
  • APP_UNINSTALLED webhook 中必须删除该 shop 的所有 session,否则存在令牌永久泄露风险。

13.4 最小权限(Scope)

  • 只申请业务必需的 scope(只读就别要 write_*),缩小令牌泄露面。
  • 上线前在 Partner Dashboard 复核 scope,避免越权被审核拒绝。

13.5 客户端安全

  • 浏览器端只拿到 apiKey(公开信息),绝不下发 accessToken。
  • 用 boundary.headers() 维持 Shopify 要求的 CSP 头,防止 iframe 被劫持。
  • 所有对外接口强制 HTTPS;本地开发隧道也用 HTTPS。

13.6 防滥用

  • 对自己暴露的 action/loader 做基础限流与参数校验,避免被恶意调用耗尽 Shopify API 配额。

14. Error Handling & Resilience(错误处理与容错)

> 评测指出原文档在「网络波动 / 限流 / 参数错误」时缺少清晰提示。本节给出可复制的容错模式。

14.1 通用包装器(Loader / Action)

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

// 统一错误处理包装:区分鉴权失败 / 参数错误 / 限流 / 未知
async function withAdmin<T>(
  request: Request,
  fn: (admin: any, session: any) => Promise<T>
): Promise<Response> {
  try {
    const { admin, session } = await authenticate.admin(request);
    const result = await fn(admin, session);
    return Response.json({ ok: true, data: result });
  } catch (err) {
    const status = (err as any)?.response?.status ?? 500;
    console.error("[shopify-app] request failed:", err);
    return Response.json(
      { ok: false, error: (err as Error).message, code: status },
      { status: status >= 400 && status < 500 ? status : 500 }
    );
  }
}

14.2 GraphQL 错误解析

admin.graphql() 即便业务出错也常返回 HTTP 200,错误在 errors 数组里:

const res = await admin.graphql(query, { variables });
const { data, errors } = await res.json();
if (errors?.length) {
  // errors: [{ message, path, extensions: { code } }]
  // 常见 code: USER_ERROR / THROTTLED / GRAPHQL_VALIDATION_ERROR
  console.error("GraphQL errors:", errors);
  const msg = errors.map((e: any) => e.message).join("; ");
  return Response.json({ ok: false, error: msg }, { status: 422 });
}

14.3 REST 错误处理

const res = await fetch(url, { headers });
if (!res.ok) {
  // 429 = 限流, 401 = token 失效, 422 = 参数/业务错误, 404 = 资源不存在
  const body = await res.text();
  console.error(`REST ${res.status}:`, body);
  return Response.json({ ok: false, error: body, code: res.status }, { status: res.status });
}

14.4 限流(Rate Limit)应对

  • GraphQL:50 点/秒。超出返回 extensions.code === "THROTTLED"。
  • REST:2 次/秒(漏桶,40 突发)。超量返回 HTTP 429,响应头 Retry-After 给出等待秒数。
  • 策略:捕获 429 → 读 Retry-After → 指数退避重试;批量任务用游标分页 + 限速;admin.graphql(query, { retries: 2 }) 开启自动重试。

14.5 网络重试与超时

  • 海外 API 偶发抖动:对 fetch 包一层超时(AbortController,建议 10s)与最多 3 次指数退避。
  • 开发环境访问 *.myshopify.com / admin/api 需稳定外网;国内网络不稳时优先部署到海外节点或加代理。

14.6 Webhook 失败

  • 处理超时或抛错时返回非 200,让 Shopify 自动重试(约 19 次,指数退避)。
  • 保证处理 幂等(同一事件可能多次投递)——用 payload.id 去重。

14.7 错误边界

  • 后台路由导出 ErrorBoundary 调用 boundary.error(useRouteError()),保证异常页符合 Shopify CSP,不会白屏。

14.8 错误诊断决策树(从哪里查起)

  1. 请求直接抛异常 / 重定向登录循环 → 先查 §16「Failed to validate session」:确认 sessionStorage 持久化、appUrl 与回调一致。
  2. 返回 HTTP 4xx → REST 看状态码(401 令牌失效 / 422 参数 / 404 资源);GraphQL 看 errors[].extensions.code(USER_ERROR=参数 / THROTTLED=限流 / VALIDATION=语法)。
  3. 返回 429 → 限流,跳 §14.4:读 Retry-After 退避,开启 retries,降频。
  4. 偶发超时 / 连不上 → 网络层,跳 §14.5 + §18 国内适配:检查外网/隧道,部署到海外节点或加代理,加 AbortController 超时。
  5. Webhook 收不到 / 401 → 跳 §16「Webhook 返回 401」:只能用 authenticate.webhook,不要手动 parse。
  6. 页面白屏 / 无法嵌入 → 跳 §16「应用无法嵌入」:确认 boundary.headers() 与 isEmbeddedApp: true。

15. Best Practices(最佳实践)

15.1 项目结构约定

  • 集中 shopify.server.ts 导出 authenticate / unauthenticated / sessionStorage。
  • 路由按业务域拆分(app.products.tsx、app.orders.tsx);Webhook 路由 不嵌套 在 admin 布局下。

15.2 游标分页(避免 offset)

query Products($cursor: String) {
  products(first: 50, after: $cursor) {
    pageInfo { hasNextPage endCursor }
    nodes { id title }
  }
}
  • 用 pageInfo.endCursor 循环,直到 hasNextPage=false,不要用偏移分页。

15.3 批量操作

  • 大批量写入用 Bulk mutation / bulkOperationRunMutation;导入导出用 productImport / CSV。
  • 单次 products(first: N) 控制 N,避免单次请求过大触发超时。

15.4 避免过度请求

  • 用 GraphQL 精确选择字段,避免 ... on Product { * } 全量拉取。
  • 客户端列表缓存 + 服务端按需请求。

15.5 Webhook 幂等

  • 所有 webhook handler 用事件 id 做幂等键,防止重复处理导致数据错乱。

15.6 开启 future flags

  • future: { v3_authenticatePublic: true } 等,提前适配破坏性变更,减少升级成本。

15.7 测试策略

  • 单元测试用 testConfig() 注入假 session;e2e 用 testConfig({ sessionStorage }) 接真库。
  • 对 authenticate、graphql、webhook 校验分别写用例。

16. Troubleshooting / 常见问题(FAQ)

症状 原因 解决
422 Unprocessable Entity 调 Admin API 库存/必填字段缺失(如新建变体未设 inventoryItem) 设置 inventory_policy=continue、inventory_management=shopify,并确保 location 级库存已建
GraphQL error: Field 'X' doesn't exist API 版本字段差异 确认用 ApiVersion.April26(2026-04),字段以 references 文档为准
Missing required argument: <arg> mutation 参数缺失 检查 input 对象是否补全必填字段,参考 references 的 mutation 签名
Failed to validate session / 登录循环 session 存储失效或在线 session 过期 确认 sessionStorage 持久化;线上用 Redis/PG;检查 appUrl 与回调一致
Webhook 返回 401 HMAC 校验失败 只能用 authenticate.webhook(request),不要手动 parse body 后再校验
CORS 报错(Embedded 扩展) 跨域响应缺头 用 cors(data) 包裹返回:return cors({ ... })
应用无法嵌入 / 白屏 CSP / App Bridge 配置 路由导出 headers = (a) => boundary.headers(a);确认 isEmbeddedApp: true
429 Too Many Requests 触限流 读 Retry-After 退避;开启 retries;降低请求频率
开发时连不上 Shopify 国内网络/隧道不稳 用稳定 HTTPS 隧道;或把服务部署到海外节点
Scope 被拒 / 权限不足 申请的 scope 超出业务 收窄 scope 并在 Partner Dashboard 复核

16.1 反模式(常见错误与根因)

反模式 为什么会出问题 正确做法
在前端代码/API 返回里暴露 accessToken 令牌泄露 = 店铺被完全接管 只下发 apiKey 到浏览器;令牌仅留服务端 env
用内存 sessionStorage 上线 多实例/重启后 session 丢失,全员重新登录 生产用 Redis/PostgreSQL 等持久化存储
Webhook 路由手动 request.json() 后直接处理 跳过 HMAC 校验,伪造请求可执行 永远走 authenticate.webhook(request)
Webhook 路由 嵌套 在 admin layout 下 admin 布局要求鉴权,webhook 无 session 会 401 webhook 路由独立、不套 admin 布局
用 offset/pageInfo 之外的方式分页大表 大表越翻越慢甚至超时 用 after: $cursor + pageInfo.endCursor 游标分页
忽略 429 直接重试 触发更长时间封禁 读 Retry-After 做指数退避;开启 retries
把 SHOPIFY_API_SECRET 提交进仓库 密钥泄露 .env gitignore + CI 用平台密钥注入
不实现 GDPR webhook 上架审核被拒 / 合规风险 必做 customers/data_request、customers/redact、shop/redact
申请过多 write_* scope 审核被拒、令牌面过大 只申请业务必需的最小 scope

17. Advanced Scenarios(高级场景)

17.1 App Proxy(应用代理)

  • 在店铺前台以 myshopify.com/apps/<your-app> 暴露服务端路由,用 authenticate.public(request) 校验来源,适合主题内嵌区块拉取应用数据。

17.2 后台队列任务

  • 用 cron(node-cron / 平台定时器)调 unauthenticated.admin(shop),处理日报、同步等;注意限流与幂等。

17.3 GDPR Webhook(合规必做)

  • 必须实现 customers/data_request、customers/redact、shop/redact:
  • customers/data_request:导出该客户数据并返回。
  • customers/redact:删除客户 PII。
  • shop/redact:应用卸载后删除店铺全部关联数据(含 session)。

17.4 多店铺 / 多 Session

  • sessionStorage 以 shop 为键隔离;离线任务用 unauthenticated.admin(shop) 按店铺取令牌。

18. 国内适配指南(China Deployment & Network)

> 评测指出「国内适配性」仅 3.8:内容依赖海外服务、国内需访问国外资源。本节给出可落地的适配建议。

18.1 网络与访问

  • Shopify 服务(Partner API、*.myshopify.com、admin/api)均在海外,国内直接访问可能不稳定或被拦截。
  • 开发期:用稳定的 HTTPS 隧道(cloudflared / ngrok)暴露本地,SHOPIFY_APP_URL 必须 HTTPS;隧道域名变化后同步更新 Partner Dashboard 的 App URL / Redirect URL。
  • 生产期:将应用部署到 海外节点(Fly.io / Railway 美区、AWS us-east-1、Cloudflare 等),让服务端到 Shopify 的调用走海外内网,规避国内出口瓶颈。
  • 若必须经由国内出口,可配置出口代理/专线,但需保证令牌传输全程 HTTPS,且代理本身不落盘密钥。

18.2 数据驻留与合规

  • Session / 业务数据建议存储在海外或合规区域,避免跨境数据流动带来的合规问题;GDPR webhook(§17.3)必须实现。
  • 不要把店铺数据回传国内再做处理,除非已评估数据出境合规。

18.3 依赖与安装

  • npm install @shopify/* 需访问 npm 海外源;国内可配置镜像(如 registry.npmmirror.com),但运行时依赖仍走 Shopify 海外 API。
  • 构建产物(Docker 镜像)建议提前在海外 CI 构建并推送到可达的镜像仓库。

18.4 面向中国商家的特殊说明

  • 如果你已在中国开展 Shopify 业务(服务海外店铺),本技能完全适用;瓶颈主要是网络而非代码。
  • 大陆商家直接开店受限,通常服务的是 海外店铺;此时按 18.1–18.3 部署到海外节点即可顺畅运行。

常见问题

5 问
答案由本站依据条目字段整理
shopify-app-react-router怎么安装?

在支持 Skills 的客户端里执行:skillhub install user_4fdb9f62/shopify-api-2026-04。安装方式为未标注。装完刷新技能列表或重开客户端。

shopify-app-react-router支持哪些客户端?

资料没有标注宿主客户端,但只要客户端支持 Skills(Claude、Codex、Cursor 等),一般都能加载。

shopify-app-react-router权限高吗,安全吗?

权限级别为未标注权限。安装前先看它会读取或写入哪些内容。

shopify-app-react-router收费吗?

本站不代收费用,也没有标注具体价格。是否收费取决于技能本身和所用的模型或客户端,正式使用前按作者说明确认。

shopify-app-react-router用什么许可证?

许可证标注为 未说明(许可证不明确)。商用或二次分发前请到仓库确认条款原文。

热门专题

按场景挑技能
全部专题 →

技能内容以作者仓库为准;分类、权限级别与许可证状态的中文说明为中文整理,最近核验 未标注。