shopify-app-dev

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shopify App Development

Shopify应用开发

Expert guidance for building custom Shopify apps using Shopify CLI, modern frameworks, and best practices.
使用Shopify CLI、现代框架及最佳实践构建自定义Shopify应用的专业指南。

When to Use This Skill

何时使用该技能

Invoke this skill when:
  • Creating custom Shopify apps with Shopify CLI
  • Setting up app development environment
  • Implementing OAuth authentication for apps
  • Building app extensions (admin blocks, theme app extensions)
  • Creating admin UI components and pages
  • Working with Hydrogen or Remix for headless storefronts
  • Deploying apps to Cloudflare Workers or other platforms
  • Integrating third-party APIs with Shopify
  • Creating app proxies for custom functionality
  • Implementing app billing and subscription plans
  • Building public or custom apps
在以下场景中调用该技能:
  • 使用Shopify CLI创建自定义Shopify应用
  • 搭建应用开发环境
  • 为应用实现OAuth认证
  • 构建应用扩展(管理后台区块、主题应用扩展)
  • 创建管理后台UI组件及页面
  • 使用Hydrogen或Remix开发无头店铺前端
  • 将应用部署至Cloudflare Workers或其他平台
  • 将第三方API与Shopify集成
  • 创建用于自定义功能的应用代理
  • 实现应用计费与订阅计划
  • 构建公开应用或自定义应用

Core Capabilities

核心能力

1. Shopify CLI Setup

1. Shopify CLI配置

Install and configure Shopify CLI for app development.
Install Shopify CLI:
bash
undefined
安装并配置Shopify CLI以进行应用开发。
安装Shopify CLI:
bash
undefined

Using npm

Using npm

npm install -g @shopify/cli @shopify/app
npm install -g @shopify/cli @shopify/app

Using Homebrew (macOS)

Using Homebrew (macOS)

brew tap shopify/shopify brew install shopify-cli
brew tap shopify/shopify brew install shopify-cli

Verify installation

Verify installation

shopify version

**Create New App:**
```bash
shopify version

**创建新应用:**
```bash

Create app with Node.js/React

Create app with Node.js/React

shopify app init
shopify app init

Choose template:

Choose template:

- Remix (recommended)

- Remix (recommended)

- Node.js + React

- Node.js + React

- PHP

- PHP

- Ruby

- Ruby

App structure created:

App structure created:

my-app/ ├── app/ # Remix app routes ├── extensions/ # App extensions ├── shopify.app.toml # App configuration ├── package.json └── README.md

**App Configuration (shopify.app.toml):**
```toml
my-app/ ├── app/ # Remix app routes ├── extensions/ # App extensions ├── shopify.app.toml # App configuration ├── package.json └── README.md

**应用配置(shopify.app.toml):**
```toml

This file stores app configuration

This file stores app configuration

name = "my-app" client_id = "your-client-id" application_url = "https://your-app.com" embedded = true
[access_scopes]
name = "my-app" client_id = "your-client-id" application_url = "https://your-app.com" embedded = true
[access_scopes]

API access scopes

API access scopes

scopes = "write_products,read_orders,read_customers"
[webhooks] api_version = "2025-10"
[[webhooks.subscriptions]] topics = ["products/create", "products/update"] uri = "/webhooks"
undefined
scopes = "write_products,read_orders,read_customers"
[webhooks] api_version = "2025-10"
[[webhooks.subscriptions]] topics = ["products/create", "products/update"] uri = "/webhooks"
undefined

2. Development Workflow

2. 开发工作流

Start Development Server:
bash
undefined
启动开发服务器:
bash
undefined

Start dev server with tunneling

Start dev server with tunneling

shopify app dev
shopify app dev

Server starts with:

Server starts with:

- Local development URL: http://localhost:3000

- Local development URL: http://localhost:3000

- App installed in development store

- App installed in development store


**Deploy App:**
```bash

**部署应用:**
```bash

Deploy to production

Deploy to production

shopify app deploy
shopify app deploy

Generate app version and deploy extensions

Generate app version and deploy extensions


**Environment Variables (.env):**
```bash
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SCOPES=write_products,read_orders
HOST=your-app-domain.com
SHOPIFY_APP_URL=https://your-app.com
DATABASE_URL=postgresql://...

**环境变量(.env):**
```bash
SHOPIFY_API_KEY=your_api_key
SHOPIFY_API_SECRET=your_api_secret
SCOPES=write_products,read_orders
HOST=your-app-domain.com
SHOPIFY_APP_URL=https://your-app.com
DATABASE_URL=postgresql://...

3. App Architecture (Remix)

3. 应用架构(Remix)

Modern Shopify app using Remix framework.
app/routes/app._index.jsx (Home Page):
javascript
import { useLoaderData } from "@remix-run/react";
import { authenticate } from "../shopify.server";
import {
  Page,
  Layout,
  Card,
  DataTable,
  Button,
} from "@shopify/polaris";

export async function loader({ request }) {
  const { admin, session } = await authenticate.admin(request);

  // Fetch products using GraphQL
  const response = await admin.graphql(`
    query {
      products(first: 10) {
        edges {
          node {
            id
            title
            handle
            status
          }
        }
      }
    }
  `);

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

  return {
    products: data.products.edges.map(e => e.node),
    shop: session.shop,
  };
}

export default function Index() {
  const { products, shop } = useLoaderData();

  const rows = products.map((product) => [
    product.title,
    product.handle,
    product.status,
  ]);

  return (
    <Page title="Products">
      <Layout>
        <Layout.Section>
          <Card>
            <DataTable
              columnContentTypes={["text", "text", "text"]}
              headings={["Title", "Handle", "Status"]}
              rows={rows}
            />
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}
app/routes/app.product.$id.jsx (Product Detail):
javascript
import { json } from "@remix-run/node";
import { useLoaderData, useSubmit } from "@remix-run/react";
import { authenticate } from "../shopify.server";
import {
  Page,
  Layout,
  Card,
  Form,
  FormLayout,
  TextField,
  Button,
} from "@shopify/polaris";
import { useState } from "react";

export async function loader({ request, params }) {
  const { admin } = await authenticate.admin(request);

  const response = await admin.graphql(`
    query GetProduct($id: ID!) {
      product(id: $id) {
        id
        title
        description
        status
        vendor
      }
    }
  `, {
    variables: { id: `gid://shopify/Product/${params.id}` },
  });

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

  return json({ product: data.product });
}

export async function action({ request, params }) {
  const { admin } = await authenticate.admin(request);

  const formData = await request.formData();
  const title = formData.get("title");
  const description = formData.get("description");

  const response = await admin.graphql(`
    mutation UpdateProduct($input: ProductInput!) {
      productUpdate(input: $input) {
        product {
          id
          title
        }
        userErrors {
          field
          message
        }
      }
    }
  `, {
    variables: {
      input: {
        id: `gid://shopify/Product/${params.id}`,
        title,
        description,
      },
    },
  });

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

  if (data.productUpdate.userErrors.length > 0) {
    return json({ errors: data.productUpdate.userErrors }, { status: 400 });
  }

  return json({ success: true });
}

export default function ProductDetail() {
  const { product } = useLoaderData();
  const submit = useSubmit();

  const [title, setTitle] = useState(product.title);
  const [description, setDescription] = useState(product.description);

  const handleSubmit = () => {
    const formData = new FormData();
    formData.append("title", title);
    formData.append("description", description);

    submit(formData, { method: "post" });
  };

  return (
    <Page title="Edit Product" backAction={{ url: "/app" }}>
      <Layout>
        <Layout.Section>
          <Card>
            <FormLayout>
              <TextField
                label="Title"
                value={title}
                onChange={setTitle}
                autoComplete="off"
              />
              <TextField
                label="Description"
                value={description}
                onChange={setDescription}
                multiline={4}
                autoComplete="off"
              />
              <Button primary onClick={handleSubmit}>
                Save
              </Button>
            </FormLayout>
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}
使用Remix框架构建的现代Shopify应用。
app/routes/app._index.jsx(首页):
javascript
import { useLoaderData } from "@remix-run/react";
import { authenticate } from "../shopify.server";
import {
  Page,
  Layout,
  Card,
  DataTable,
  Button,
} from "@shopify/polaris";

export async function loader({ request }) {
  const { admin, session } = await authenticate.admin(request);

  // Fetch products using GraphQL
  const response = await admin.graphql(`
    query {
      products(first: 10) {
        edges {
          node {
            id
            title
            handle
            status
          }
        }
      }
    }
  `);

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

  return {
    products: data.products.edges.map(e => e.node),
    shop: session.shop,
  };
}

export default function Index() {
  const { products, shop } = useLoaderData();

  const rows = products.map((product) => [
    product.title,
    product.handle,
    product.status,
  ]);

  return (
    <Page title="Products">
      <Layout>
        <Layout.Section>
          <Card>
            <DataTable
              columnContentTypes={["text", "text", "text"]}
              headings={["Title", "Handle", "Status"]}
              rows={rows}
            />
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}
app/routes/app.product.$id.jsx(产品详情页):
javascript
import { json } from "@remix-run/node";
import { useLoaderData, useSubmit } from "@remix-run/react";
import { authenticate } from "../shopify.server";
import {
  Page,
  Layout,
  Card,
  Form,
  FormLayout,
  TextField,
  Button,
} from "@shopify/polaris";
import { useState } from "react";

export async function loader({ request, params }) {
  const { admin } = await authenticate.admin(request);

  const response = await admin.graphql(`
    query GetProduct($id: ID!) {
      product(id: $id) {
        id
        title
        description
        status
        vendor
      }
    }
  `, {
    variables: { id: `gid://shopify/Product/${params.id}` },
  });

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

  return json({ product: data.product });
}

export async function action({ request, params }) {
  const { admin } = await authenticate.admin(request);

  const formData = await request.formData();
  const title = formData.get("title");
  const description = formData.get("description");

  const response = await admin.graphql(`
    mutation UpdateProduct($input: ProductInput!) {
      productUpdate(input: $input) {
        product {
          id
          title
        }
        userErrors {
          field
          message
        }
      }
    }
  `, {
    variables: {
      input: {
        id: `gid://shopify/Product/${params.id}`,
        title,
        description,
      },
    },
  });

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

  if (data.productUpdate.userErrors.length > 0) {
    return json({ errors: data.productUpdate.userErrors }, { status: 400 });
  }

  return json({ success: true });
}

export default function ProductDetail() {
  const { product } = useLoaderData();
  const submit = useSubmit();

  const [title, setTitle] = useState(product.title);
  const [description, setDescription] = useState(product.description);

  const handleSubmit = () => {
    const formData = new FormData();
    formData.append("title", title);
    formData.append("description", description);

    submit(formData, { method: "post" });
  };

  return (
    <Page title="Edit Product" backAction={{ url: "/app" }}>
      <Layout>
        <Layout.Section>
          <Card>
            <FormLayout>
              <TextField
                label="Title"
                value={title}
                onChange={setTitle}
                autoComplete="off"
              />
              <TextField
                label="Description"
                value={description}
                onChange={setDescription}
                multiline={4}
                autoComplete="off"
              />
              <Button primary onClick={handleSubmit}>
                Save
              </Button>
            </FormLayout>
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}

4. App Extensions

4. 应用扩展

Extend Shopify functionality with various extension types.
Admin Action Extension:
Create button in admin product page:
bash
shopify app generate extension
通过各类扩展类型增强Shopify功能。
管理后台操作扩展:
在管理后台产品页面创建按钮:
bash
shopify app generate extension

Choose: Admin action

Choose: Admin action

Name: Export Product

Name: Export Product


**extensions/export-product/src/index.jsx:**
```javascript
import { extend, AdminAction } from "@shopify/admin-ui-extensions";

extend("Admin::Product::SubscriptionAction", (root, { data }) => {
  const { id, title } = data.selected[0];

  const button = root.createComponent(AdminAction, {
    title: "Export Product",
    onPress: async () => {
      // Call your app API
      const response = await fetch("/api/export", {
        method: "POST",
        body: JSON.stringify({ productId: id }),
        headers: { "Content-Type": "application/json" },
      });

      if (response.ok) {
        root.toast.show("Product exported successfully!");
      } else {
        root.toast.show("Export failed", { isError: true });
      }
    },
  });

  root.append(button);
});
Theme App Extension:
Add app block to themes:
bash
shopify app generate extension

**extensions/export-product/src/index.jsx:**
```javascript
import { extend, AdminAction } from "@shopify/admin-ui-extensions";

extend("Admin::Product::SubscriptionAction", (root, { data }) => {
  const { id, title } = data.selected[0];

  const button = root.createComponent(AdminAction, {
    title: "Export Product",
    onPress: async () => {
      // Call your app API
      const response = await fetch("/api/export", {
        method: "POST",
        body: JSON.stringify({ productId: id }),
        headers: { "Content-Type": "application/json" },
      });

      if (response.ok) {
        root.toast.show("Product exported successfully!");
      } else {
        root.toast.show("Export failed", { isError: true });
      }
    },
  });

  root.append(button);
});
主题应用扩展:
向主题中添加应用区块:
bash
shopify app generate extension

Choose: Theme app extension

Choose: Theme app extension

Name: Product Reviews

Name: Product Reviews


**extensions/product-reviews/blocks/reviews.liquid:**
```liquid
{% schema %}
{
  "name": "Product Reviews",
  "target": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Customer Reviews"
    },
    {
      "type": "range",
      "id": "reviews_to_show",
      "label": "Reviews to Show",
      "min": 1,
      "max": 10,
      "default": 5
    }
  ]
}
{% endschema %}

<div class="product-reviews">
  <h2>{{ block.settings.heading }}</h2>

  {% comment %}
    Fetch reviews from your app API
  {% endcomment %}

  <div id="reviews-container" data-product-id="{{ product.id }}"></div>
</div>

<script>
  // Fetch and render reviews
  fetch(`/apps/reviews/api/reviews?product_id={{ product.id }}&limit={{ block.settings.reviews_to_show }}`)
    .then(r => r.json())
    .then(reviews => {
      const container = document.getElementById('reviews-container');
      container.innerHTML = reviews.map(review => `
        <div class="review">
          <div class="rating">${'⭐'.repeat(review.rating)}</div>
          <h3>${review.title}</h3>
          <p>${review.content}</p>
          <p class="author">- ${review.author}</p>
        </div>
      `).join('');
    });
</script>

{% stylesheet %}
  .product-reviews {
    padding: 2rem;
  }

  .review {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid #eee;
  }

  .rating {
    color: #ffa500;
    margin-bottom: 0.5rem;
  }
{% endstylesheet %}

**extensions/product-reviews/blocks/reviews.liquid:**
```liquid
{% schema %}
{
  "name": "Product Reviews",
  "target": "section",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "Heading",
      "default": "Customer Reviews"
    },
    {
      "type": "range",
      "id": "reviews_to_show",
      "label": "Reviews to Show",
      "min": 1,
      "max": 10,
      "default": 5
    }
  ]
}
{% endschema %}

<div class="product-reviews">
  <h2>{{ block.settings.heading }}</h2>

  {% comment %}
    Fetch reviews from your app API
  {% endcomment %}

  <div id="reviews-container" data-product-id="{{ product.id }}"></div>
</div>

<script>
  // Fetch and render reviews
  fetch(`/apps/reviews/api/reviews?product_id={{ product.id }}&limit={{ block.settings.reviews_to_show }}`)
    .then(r => r.json())
    .then(reviews => {
      const container = document.getElementById('reviews-container');
      container.innerHTML = reviews.map(review => `
        <div class="review">
          <div class="rating">${'⭐'.repeat(review.rating)}</div>
          <h3>${review.title}</h3>
          <p>${review.content}</p>
          <p class="author">- ${review.author}</p>
        </div>
      `).join('');
    });
</script>

{% stylesheet %}
  .product-reviews {
    padding: 2rem;
  }

  .review {
    margin-bottom: 1.5rem;
    padding-bottom: 1.5rem;
    border-bottom: 1px solid #eee;
  }

  .rating {
    color: #ffa500;
    margin-bottom: 0.5rem;
  }
{% endstylesheet %}

5. Webhooks in Apps

5. 应用中的Webhook

Handle Shopify events in your app.
app/routes/webhooks.jsx:
javascript
import { authenticate } from "../shopify.server";
import db from "../db.server";

export async function action({ request }) {
  const { topic, shop, session, admin, payload } = await authenticate.webhook(request);

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

  switch (topic) {
    case "APP_UNINSTALLED":
      // Clean up app data
      await db.session.deleteMany({ where: { shop } });
      break;

    case "PRODUCTS_CREATE":
      // Handle new product
      console.log("New product created:", payload.id, payload.title);
      await handleProductCreated(payload);
      break;

    case "PRODUCTS_UPDATE":
      // Handle product update
      console.log("Product updated:", payload.id);
      await handleProductUpdated(payload);
      break;

    case "ORDERS_CREATE":
      // Handle new order
      console.log("New order:", payload.id, payload.email);
      await handleOrderCreated(payload);
      break;

    case "CUSTOMERS_CREATE":
      // Handle new customer
      await handleCustomerCreated(payload);
      break;

    default:
      console.log("Unhandled webhook topic:", topic);
  }

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

async function handleProductCreated(product) {
  // Process new product
  await db.product.create({
    data: {
      shopifyId: product.id,
      title: product.title,
      handle: product.handle,
    },
  });
}

async function handleOrderCreated(order) {
  // Send email notification, update inventory, etc.
  console.log(`Order ${order.id} received for ${order.email}`);
}
Register Webhooks (app/shopify.server.js):
javascript
import "@shopify/shopify-app-remix/adapters/node";
import {
  ApiVersion,
  AppDistribution,
  shopifyApp,
  DeliveryMethod,
} from "@shopify/shopify-app-remix/server";

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET,
  scopes: process.env.SCOPES?.split(","),
  appUrl: process.env.SHOPIFY_APP_URL,
  authPathPrefix: "/auth",
  sessionStorage: new SQLiteSessionStorage(),
  distribution: AppDistribution.AppStore,
  apiVersion: ApiVersion.October25,

  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    PRODUCTS_CREATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    PRODUCTS_UPDATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    ORDERS_CREATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
  },
});

export default shopify;
export const authenticate = shopify.authenticate;
在应用中处理Shopify事件。
app/routes/webhooks.jsx:
javascript
import { authenticate } from "../shopify.server";
import db from "../db.server";

export async function action({ request }) {
  const { topic, shop, session, admin, payload } = await authenticate.webhook(request);

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

  switch (topic) {
    case "APP_UNINSTALLED":
      // Clean up app data
      await db.session.deleteMany({ where: { shop } });
      break;

    case "PRODUCTS_CREATE":
      // Handle new product
      console.log("New product created:", payload.id, payload.title);
      await handleProductCreated(payload);
      break;

    case "PRODUCTS_UPDATE":
      // Handle product update
      console.log("Product updated:", payload.id);
      await handleProductUpdated(payload);
      break;

    case "ORDERS_CREATE":
      // Handle new order
      console.log("New order:", payload.id, payload.email);
      await handleOrderCreated(payload);
      break;

    case "CUSTOMERS_CREATE":
      // Handle new customer
      await handleCustomerCreated(payload);
      break;

    default:
      console.log("Unhandled webhook topic:", topic);
  }

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

async function handleProductCreated(product) {
  // Process new product
  await db.product.create({
    data: {
      shopifyId: product.id,
      title: product.title,
      handle: product.handle,
    },
  });
}

async function handleOrderCreated(order) {
  // Send email notification, update inventory, etc.
  console.log(`Order ${order.id} received for ${order.email}`);
}
注册Webhook(app/shopify.server.js):
javascript
import "@shopify/shopify-app-remix/adapters/node";
import {
  ApiVersion,
  AppDistribution,
  shopifyApp,
  DeliveryMethod,
} from "@shopify/shopify-app-remix/server";

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET,
  scopes: process.env.SCOPES?.split(","),
  appUrl: process.env.SHOPIFY_APP_URL,
  authPathPrefix: "/auth",
  sessionStorage: new SQLiteSessionStorage(),
  distribution: AppDistribution.AppStore,
  apiVersion: ApiVersion.October25,

  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    PRODUCTS_CREATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    PRODUCTS_UPDATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
    ORDERS_CREATE: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: "/webhooks",
    },
  },
});

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

6. App Proxy

6. 应用代理

Create custom storefront routes that access your app.
Setup in Partner Dashboard:
Subpath prefix: apps
Subpath: reviews
Proxy URL: https://your-app.com/api/proxy
Result:
https://store.com/apps/reviews → proxies to → https://your-app.com/api/proxy
Handle Proxy Requests (app/routes/api.proxy.jsx):
javascript
import { json } from "@remix-run/node";

export async function loader({ request }) {
  const url = new URL(request.url);

  // Verify proxy request
  const signature = url.searchParams.get("signature");
  const shop = url.searchParams.get("shop");

  if (!verifyProxySignature(signature, request)) {
    return json({ error: "Invalid signature" }, { status: 401 });
  }

  // Handle different paths
  const path = url.searchParams.get("path_prefix");

  if (path === "/apps/reviews/product") {
    const productId = url.searchParams.get("product_id");
    const reviews = await getProductReviews(productId);

    return json({ reviews });
  }

  return json({ message: "App Proxy" });
}

function verifyProxySignature(signature, request) {
  // Verify HMAC signature
  // Implementation depends on your setup
  return true;
}
创建可访问应用的自定义店铺前端路由。
在合作伙伴后台设置:
Subpath prefix: apps
Subpath: reviews
Proxy URL: https://your-app.com/api/proxy
设置结果:
https://store.com/apps/reviews → 代理至 → https://your-app.com/api/proxy
处理代理请求(app/routes/api.proxy.jsx):
javascript
import { json } from "@remix-run/node";

export async function loader({ request }) {
  const url = new URL(request.url);

  // Verify proxy request
  const signature = url.searchParams.get("signature");
  const shop = url.searchParams.get("shop");

  if (!verifyProxySignature(signature, request)) {
    return json({ error: "Invalid signature" }, { status: 401 });
  }

  // Handle different paths
  const path = url.searchParams.get("path_prefix");

  if (path === "/apps/reviews/product") {
    const productId = url.searchParams.get("product_id");
    const reviews = await getProductReviews(productId);

    return json({ reviews });
  }

  return json({ message: "App Proxy" });
}

function verifyProxySignature(signature, request) {
  // Verify HMAC signature
  // Implementation depends on your setup
  return true;
}

7. Polaris UI Components

7. Polaris UI组件

Use Shopify's design system for consistent admin UI.
Common Components:
javascript
import {
  Page,
  Layout,
  Card,
  Button,
  TextField,
  Select,
  Checkbox,
  Badge,
  Banner,
  DataTable,
  Modal,
  Toast,
  Frame,
} from "@shopify/polaris";

export default function MyPage() {
  return (
    <Page
      title="Settings"
      primaryAction={{ content: "Save", onAction: handleSave }}
      secondaryActions={[{ content: "Cancel", onAction: handleCancel }]}
    >
      <Layout>
        <Layout.Section>
          <Card title="General Settings" sectioned>
            <TextField
              label="App Name"
              value={name}
              onChange={setName}
            />

            <Select
              label="Status"
              options={[
                { label: "Active", value: "active" },
                { label: "Draft", value: "draft" },
              ]}
              value={status}
              onChange={setStatus}
            />

            <Checkbox
              label="Enable notifications"
              checked={notifications}
              onChange={setNotifications}
            />
          </Card>
        </Layout.Section>

        <Layout.Section secondary>
          <Card title="Status" sectioned>
            <Badge status="success">Active</Badge>
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}
使用Shopify的设计系统构建一致的管理后台UI。
常用组件:
javascript
import {
  Page,
  Layout,
  Card,
  Button,
  TextField,
  Select,
  Checkbox,
  Badge,
  Banner,
  DataTable,
  Modal,
  Toast,
  Frame,
} from "@shopify/polaris";

export default function MyPage() {
  return (
    <Page
      title="Settings"
      primaryAction={{ content: "Save", onAction: handleSave }}
      secondaryActions={[{ content: "Cancel", onAction: handleCancel }]}
    >
      <Layout>
        <Layout.Section>
          <Card title="General Settings" sectioned>
            <TextField
              label="App Name"
              value={name}
              onChange={setName}
            />

            <Select
              label="Status"
              options={[
                { label: "Active", value: "active" },
                { label: "Draft", value: "draft" },
              ]}
              value={status}
              onChange={setStatus}
            />

            <Checkbox
              label="Enable notifications"
              checked={notifications}
              onChange={setNotifications}
            />
          </Card>
        </Layout.Section>

        <Layout.Section secondary>
          <Card title="Status" sectioned>
            <Badge status="success">Active</Badge>
          </Card>
        </Layout.Section>
      </Layout>
    </Page>
  );
}

8. Deployment

8. 部署

Deploy Shopify apps to production.
Deploy to Cloudflare Workers:
wrangler.toml:
toml
name = "shopify-app"
compatibility_date = "2025-11-10"
main = "build/index.js"

[vars]
SHOPIFY_API_KEY = "your_api_key"

[[kv_namespaces]]
binding = "SESSIONS"
id = "your_kv_namespace_id"
Deploy:
bash
undefined
将Shopify应用部署至生产环境。
部署至Cloudflare Workers:
wrangler.toml:
toml
name = "shopify-app"
compatibility_date = "2025-11-10"
main = "build/index.js"

[vars]
SHOPIFY_API_KEY = "your_api_key"

[[kv_namespaces]]
binding = "SESSIONS"
id = "your_kv_namespace_id"
部署步骤:
bash
undefined

Build app

Build app

npm run build
npm run build

Deploy to Cloudflare

Deploy to Cloudflare

wrangler deploy

**Environment Secrets:**
```bash
wrangler deploy

**环境密钥:**
```bash

Add secrets

Add secrets

wrangler secret put SHOPIFY_API_SECRET wrangler secret put DATABASE_URL
undefined
wrangler secret put SHOPIFY_API_SECRET wrangler secret put DATABASE_URL
undefined

Best Practices

最佳实践

  1. Use Shopify CLI for app scaffolding and development
  2. Implement proper OAuth with HMAC verification
  3. Handle webhook events for real-time updates
  4. Use Polaris for consistent admin UI
  5. Test in development store before production
  6. Implement error handling for all API calls
  7. Store session data securely (encrypted database)
  8. Follow Shopify app requirements for listing
  9. Implement app billing for monetization
  10. Use app extensions to enhance merchant experience
  1. 使用Shopify CLI进行应用脚手架搭建与开发
  2. 实现规范的OAuth认证,包含HMAC验证
  3. 处理Webhook事件以获取实时更新
  4. 使用Polaris构建一致的管理后台UI
  5. 在开发店铺中测试后再上线至生产环境
  6. 为所有API调用实现错误处理
  7. 安全存储会话数据(加密数据库)
  8. 遵循Shopify应用上架要求
  9. 实现应用计费功能以实现 monetization
  10. 使用应用扩展提升商家体验

Integration with Other Skills

与其他技能的集成

  • shopify-api - Use when making API calls from your app
  • shopify-liquid - Use when creating theme app extensions
  • shopify-debugging - Use when troubleshooting app issues
  • shopify-performance - Use when optimizing app performance
  • shopify-api - 在应用中调用API时使用
  • shopify-liquid - 创建主题应用扩展时使用
  • shopify-debugging - 排查应用问题时使用
  • shopify-performance - 优化应用性能时使用

Quick Reference

快速参考

bash
undefined
bash
undefined

Create app

创建应用

shopify app init
shopify app init

Start development

启动开发

shopify app dev
shopify app dev

Generate extension

生成扩展

shopify app generate extension
shopify app generate extension

Deploy app

部署应用

shopify app deploy
shopify app deploy

Configure webhooks

配置Webhook

Edit shopify.app.toml

编辑 shopify.app.toml

undefined
undefined