shopify-remix-template

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shopify Remix Template Guide

Shopify Remix Template 指南

This skill provides a guide for building Shopify apps using the official Shopify Remix App Template. This template is the recommended starting point for most new Shopify embedded apps (though React Router is the future direction, Remix is still widely used and supported).
本指南介绍如何使用官方Shopify Remix App Template构建Shopify应用。该模板是大多数新Shopify嵌入式应用的推荐起步方案(尽管React Router是未来方向,但Remix目前仍被广泛使用和支持)。

🚀 Getting Started

🚀 入门

To create a new app using the Remix template, run:
bash
git clone https://github.com/Shopify/shopify-app-template-remix.git
要使用Remix模板创建新应用,运行:
bash
git clone https://github.com/Shopify/shopify-app-template-remix.git

📂 Project Structure

📂 项目结构

A typical Remix app structure:
  • app/
    • routes/
      : File-system based routing.
      • app._index.tsx
        : The main dashboard page.
      • app.tsx
        : The root layout for the authenticated app.
      • webhooks.tsx
        : Webhook handler.
    • shopify.server.ts
      : Critical. Initializes the Shopify API client, authentication, and session storage (Redis).
    • db.server.ts
      : Database connection (Mongoose).
    • models/
      : Mongoose models (e.g.,
      Session.ts
      ,
      Shop.ts
      ).
    • root.tsx
      : The root component for the entire application.
  • shopify.app.toml
    : Main app configuration file.
典型的Remix应用结构如下:
  • app/
    • routes/
      : 基于文件系统的路由
      • app._index.tsx
        : 主仪表盘页面
      • app.tsx
        : 已验证应用的根布局
      • webhooks.tsx
        : Webhook处理器
    • shopify.server.ts
      : 核心文件,用于初始化Shopify API客户端、身份验证和会话存储(Redis)
    • db.server.ts
      : 数据库连接(Mongoose)
    • models/
      : Mongoose模型(例如
      Session.ts
      Shop.ts
    • root.tsx
      : 整个应用的根组件
  • shopify.app.toml
    : 应用主配置文件

🔐 Authentication & Sessions

🔐 身份验证与会话

The template uses
@shopify/shopify-app-remix
to handle authentication automatically.
该模板使用
@shopify/shopify-app-remix
自动处理身份验证。

shopify.server.ts

shopify.server.ts

This file exports an
authenticate
object used in loaders and actions. It is configured to use Redis for session storage.
typescript
import { shopifyApp } from "@shopify/shopify-app-remix/server";
import { RedisSessionStorage } from "@shopify/shopify-app-session-storage-redis";

const sessionDb = new RedisSessionStorage(
  new URL(process.env.REDIS_URL!)
);

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET,
  appUrl: process.env.SHOPIFY_APP_URL,
  scopes: process.env.SCOPES?.split(","),
  apiVersion: "2025-10",
  sessionStorage: sessionDb,
  isEmbeddedApp: true,
});

export const authenticate = shopify.authenticate;
export const apiVersion = "2025-10";
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;
该文件导出一个在loader和action中使用的
authenticate
对象,配置为使用Redis存储会话。
typescript
import { shopifyApp } from "@shopify/shopify-app-remix/server";
import { RedisSessionStorage } from "@shopify/shopify-app-session-storage-redis";

const sessionDb = new RedisSessionStorage(
  new URL(process.env.REDIS_URL!)
);

const shopify = shopifyApp({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET,
  appUrl: process.env.SHOPIFY_APP_URL,
  scopes: process.env.SCOPES?.split(","),
  apiVersion: "2025-10",
  sessionStorage: sessionDb,
  isEmbeddedApp: true,
});

export const authenticate = shopify.authenticate;
export const apiVersion = "2025-10";
export const addDocumentResponseHeaders = shopify.addDocumentResponseHeaders;

Usage in Loaders (Data Fetching)

在Loader中使用(数据获取)

Protect routes and get the session context:
typescript
import { json } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const loader = async ({ request }) => {
  const { admin, session } = await authenticate.admin(request);
  
  // Use admin API
  const response = await admin.graphql(`...`);
  
  return json({ data: response });
};
保护路由并获取会话上下文:
typescript
import { json } from "@remix-run/node";
import { authenticate } from "../shopify.server";

export const loader = async ({ request }) => {
  const { admin, session } = await authenticate.admin(request);
  
  // Use admin API
  const response = await admin.graphql(`...`);
  
  return json({ data: response });
};

📡 Webhooks

📡 Webhooks

Webhooks are handled in
app/routes/webhooks.tsx
(or individual route files). The template automatically registers webhooks defined in
shopify.server.ts
.
To add a webhook:
  1. Add configuration in
    shopify.server.ts
    .
  2. Handle the topic in the
    action
    of
    app/routes/webhooks.tsx
    .
Webhook在
app/routes/webhooks.tsx
(或单独的路由文件)中处理。模板会自动注册在
shopify.server.ts
中定义的Webhook。
添加Webhook的步骤:
  1. shopify.server.ts
    中添加配置
  2. app/routes/webhooks.tsx
    action
    中处理对应的topic

🗄️ Database (Mongoose/MongoDB)

🗄️ 数据库(Mongoose/MongoDB)

Use Mongoose for persistent data storage (Shops, Settings, etc.).
使用Mongoose实现持久化数据存储(店铺信息、配置等)。

app/db.server.ts

app/db.server.ts

Singleton connection to MongoDB.
typescript
import mongoose from "mongoose";

let isConnected = false;

export const connectDb = async () => {
  if (isConnected) return;

  try {
    await mongoose.connect(process.env.MONGODB_URI!);
    isConnected = true;
    console.log("🚀 Connected to MongoDB");
  } catch (error) {
    console.error("❌ MongoDB connection error:", error);
  }
};
MongoDB的单例连接:
typescript
import mongoose from "mongoose";

let isConnected = false;

export const connectDb = async () => {
  if (isConnected) return;

  try {
    await mongoose.connect(process.env.MONGODB_URI!);
    isConnected = true;
    console.log("🚀 Connected to MongoDB");
  } catch (error) {
    console.error("❌ MongoDB connection error:", error);
  }
};

app/models/Shop.ts
(Example)

app/models/Shop.ts
(示例)

typescript
import mongoose from "mongoose";

const ShopSchema = new mongoose.Schema({
  shop: { type: String, required: true, unique: true },
  accessToken: { type: String, required: true },
  isInstalled: { type: Boolean, default: true },
});

export const Shop = mongoose.models.Shop || mongoose.model("Shop", ShopSchema);
typescript
import mongoose from "mongoose";

const ShopSchema = new mongoose.Schema({
  shop: { type: String, required: true, unique: true },
  accessToken: { type: String, required: true },
  isInstalled: { type: Boolean, default: true },
});

export const Shop = mongoose.models.Shop || mongoose.model("Shop", ShopSchema);

Usage in Loaders

在Loader中使用

Connect to the DB before using models.
typescript
import { connectDb } from "../db.server";
import { Shop } from "../models/Shop";

export const loader = async ({ request }) => {
  await connectDb();
  // ...
  const shopData = await Shop.findOne({ shop: session.shop });
  // ...
};
使用模型前先连接数据库:
typescript
import { connectDb } from "../db.server";
import { Shop } from "../models/Shop";

export const loader = async ({ request }) => {
  await connectDb();
  // ...
  const shopData = await Shop.findOne({ shop: session.shop });
  // ...
};

🎨 UI & Design (Polaris)

🎨 UI与设计(Polaris)

The template comes pre-configured with Polaris, Shopify's design system.
  • Wrap your pages in
    <Page>
    components.
  • Use
    <Layout>
    ,
    <Card>
    , and other Polaris components for a native feel.
  • App Bridge is initialized automatically in
    app.tsx
    .
模板预配置了Polaris,即Shopify的设计系统。
  • <Page>
    组件包裹你的页面
  • 使用
    <Layout>
    <Card>
    等Polaris组件获得原生体验
  • App Bridge在
    app.tsx
    中自动初始化

🛠️ Common Tasks

🛠️ 常见任务

1. Adding a Navigation Item

1. 添加导航项

Update
app/routes/app.tsx
:
typescript
<ui-nav-menu>
  <Link to="/app">Home</Link>
  <Link to="/app/settings">Settings</Link>
</ui-nav-menu>
更新
app/routes/app.tsx
typescript
<ui-nav-menu>
  <Link to="/app">Home</Link>
  <Link to="/app/settings">Settings</Link>
</ui-nav-menu>

2. Fetching Data from Shopify

2. 从Shopify获取数据

Use the
admin
object from
authenticate.admin(request)
to make GraphQL calls.
使用
authenticate.admin(request)
返回的
admin
对象发起GraphQL请求。

3. Deploying

3. 部署

  • Hosting: Remix apps can be hosted on Vercel, Fly.io, Heroku, or Cloudflare.
  • Database: Ensure you have a persistent database (e.g., Postgres) for production.
  • Environment Variables: Set
    SHOPIFY_API_KEY
    ,
    SHOPIFY_API_SECRET
    ,
    SCOPES
    ,
    SHOPIFY_APP_URL
    .
  • 托管: Remix应用可以托管在Vercel、Fly.io、Heroku或Cloudflare上
  • 数据库: 生产环境请确保使用持久化数据库(例如Postgres)
  • 环境变量: 配置
    SHOPIFY_API_KEY
    SHOPIFY_API_SECRET
    SCOPES
    SHOPIFY_APP_URL

📚 References

📚 参考资料