shopify-remix-template

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Shopify Remix Template Guide

Shopify Remix 模板指南

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;
此文件导出一个
authenticate
对象,用于加载器(loaders)和动作(actions)中。它配置为使用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)

在加载器(Loaders)中使用(数据获取)

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);
  
  // 使用Admin API
  const response = await admin.graphql(`...`);
  
  return json({ data: response });
};

📡 Webhooks

📡 Webhook

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
    中处理对应的主题。

🗄️ 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

在加载器中使用

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组件,实现原生Shopify风格。
  • 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

📚 参考资料