
关于
Shopify 应用开发的专家模式,包括 Remix/React 前端、GraphQL API 和应用扩展。
name: shopify-apps description: Shopify 应用开发的专家模式,包括 Remix/React Router 应用、带 App Bridge 的嵌入式应用、webhook 处理、GraphQL Admin API、Polaris 组件、计费和应用扩展。 risk: safe source: vibeship-spawner-skills (Apache 2.0) date_added: 2026-02-27
Shopify 应用
Shopify 应用开发的专家模式,包括 Remix/React Router 应用、带 App Bridge 的嵌入式应用、webhook 处理、GraphQL Admin API、Polaris 组件、计费和应用扩展。
模式
React Router 应用设置
使用 React Router 的现代 Shopify 应用模板
何时使用:启动新的 Shopify 应用
模板
# Create new Shopify app with CLI
npm init @shopify/app@latest my-shopify-app
# Project structure
# my-shopify-app/
# ├── app/
# │ ├── routes/
# │ │ ├── app._index.tsx # Main app page
# │ │ ├── app.tsx # App layout with providers
# │ │ ├── auth.$.tsx # Auth callback
# │ │ └── webhooks.tsx # Webhook handler
# │ ├── shopify.server.ts # Server configuration
# │ └── root.tsx # Root layout
# ├── extensions/ # App extensions
# ├── shopify.app.toml # App configuration
# └── package.json
// shopify.app.toml
name = "my-shopify-app"
client_id = "your-client-id"
application_url = "https://your-app.example.com"
[access_scopes]
scopes = "read_products,write_products,read_orders"
[webhooks]
api_version = "2024-10"
[webhooks.subscriptions]
topics = ["orders/create", "products/update"]
uri = "/webhooks"
[auth]
redirect_urls = ["https://your-app.example.com/auth/callback"]
// app/shopify.server.ts
import "@shopify/shopify-app-remix/adapters/node";
import {
LATEST_API_VERSION,
shopifyApp,
DeliveryMethod,
} from "@shopify/shopify-app-remix/server";
import { PrismaSessionStorage } from "@shopify/shopify-app-session-storage-prisma";
import prisma from "./db.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 PrismaSessionStorage(prisma),
distribution: AppDistribution.AppStore,
future: {
unstable_newEmbeddedAuthStrategy: true,
},
...(process.env.SHOP_CUSTOM_DOMAIN
? { customShopDomains: [process.env.SHOP_CUSTOM_DOMAIN] }
: {}),
});
export default shopify;
export const apiVersion = LATEST_API_VERSION;
export const authenticate = shopify.authenticate;
export const sessionStorage = shopify.sessionStorage;
注意事项
- React Router 在 2024 年底取代 Remix 成为推荐模板
- unstable_newEmbeddedAuthStrategy 默认为新应用启用
- Webhooks 在 shopify.app.toml 中配置,而非代码中
- 运行 'shopify app deploy' 以应用配置更改
带 App Bridge 的嵌入式应用
在 Shopify 管理后台中渲染嵌入式应用
何时使用:构建嵌入式管理应用
模板
// app/routes/app.tsx - App layout with providers
import { Link, Outlet, useLoaderData, useRouteError } from "@remix-run/react";
import { AppProvider } from "@shopify/shopify-app-remix/react";
import polarisStyles from "@shopify/polaris/build/esm/styles.css?url";
export const links = () => [{ rel: "stylesheet", href: polarisStyles }];
export async function loader({ request }: LoaderFunctionArgs) {
await authenticate.admin(request);
return json({ apiKey: process.env.SHOPIFY_API_KEY! });
}
export default function App() {
const { apiKey } = useLoaderData<typeof loader>();
return (
<AppProvider isEmbeddedApp apiKey={apiKey}>
<ui-nav-menu>
<Link to="/app" rel="home">Home</Link>
<Link to="/app/products">Products</Link>
<Link to="/app/settings">Settings</Link>
</ui-nav-menu>
<Outlet />
</AppProvider>
);
}
export function ErrorBoundary() {
const error = useRouteError();
return (
<AppProvider isEmbeddedApp>
<Page>
<Card>
<Text as="p" variant="bodyMd">
Something went wrong. Please try again.
</Text>
</Card>
</Page>
</AppProvider>
);
}
// app/routes/app._index.tsx - Main app page
import {
Page,
Layout,
Card,
Text,
BlockStack,
Button,
} from "@shopify/polaris";
import { TitleBar } from "@shopify/app-bridge-react";
export async function loader({ request }: LoaderFunctionArgs) {
const { admin } = await authenticate.admin(request);
// GraphQL query
const response = await admin.graphql(\`
query {
shop {
name
email
}
}
\`);
const { data } = await response.json();
return json({ shop: data.shop });
}
export default function Index() {
const { shop } = useLoaderData<typeof loader>();
return (
<Page>
<TitleBar title="My Shopify App" />
<Layout>
<Layout.Section>
<Card>
<BlockStack gap="200">
</BlockStack>
</Card>
</Layout.Section>
</Layout>
</Page>
);
}
兼容工具
Claude CodeCursor
标签
电商
