Shopifyアプリを作りたいけれど、何から始めるか分からない。そんな方に向けて、公式テンプレートを使う手順を整理します。
この記事では、React RouterとPrismaを使い、QRコードを作成します。商品ページへの遷移と、スキャン数の計測まで実装します。
この記事で完成するQRコードアプリ
結論から言うと、管理画面付きのQRコードアプリが完成します。作成したQRコードは、商品ページやチェックアウトへ利用者を誘導できます。
QRコードの作成、編集、削除
Shopify商品との紐付け
管理画面でのQRコード一覧表示
公開URLでのQRコード表示
スキャン数の加算
設定先へのリダイレクト
対象読者と前提知識
Shopifyアプリ開発を始めたい初心者を対象にしています。JavaScript、React、Node.jsの基本操作があると進めやすくなります。
所要時間の目安
所要時間は、環境や経験によって変わります。本記事では、環境構築から動作確認までを順番に確認します。
先に完成イメージを確認してから、必要な章へ進んでください。
目次|環境構築からスキャン計測まで
公式テンプレートでアプリを作る
PrismaにQRCodeテーブルを追加する
QRコードのサーバー処理を作る
管理画面に一覧ページを作る
作成・編集ページを作る
公開ページでQRコードを表示する
スキャン数を計測してリダイレクトする
動作確認とトラブル対処
公式ドキュメントも確認しながら進めます。Shopify公式のReact Routerガイド
1. Shopify公式テンプレートでアプリを作る
まずは公式テンプレートで、認証済みの開発基盤を作ります。認証や管理画面への埋め込みに必要な構成が、最初から含まれています。
アプリのひな形を作成する
npm init @shopify/app@latestコマンドを実行すると、対話形式で設定を選びます。Frameworkでは、React Routerを選択します。
Framework:React Router
生成されたアプリ名を確認
表示される案内に沿って設定
この段階では、独自機能より開発基盤の生成を優先します。
開発用サーバーを起動する
cd your-app
npm install
npm run devcd your-app:アプリのフォルダへ移動npm install:依存ライブラリを導入npm run dev:開発用サーバーを起動
起動後、ターミナルにPreview URLが表示されます。そのURLを開くと、Shopify管理画面内でアプリを確認できます。

Preview URLが開ければ、環境構築は完了です。次はQRコード情報の保存先を用意します。
2. PrismaにQRCodeテーブルを追加する
QRコードの設定は、PrismaとSQLiteで保存します。商品、遷移先、タイトル、スキャン数をDBに持たせます。
Prismaは、schema.prismaでDB構造を定義します。その定義をもとに、Node.jsからDBを操作できます。
QRCodeモデルを定義する
prisma/schema.prismaを開き、QRCodeモデルを追加します。
model QRCode {
id Int @id @default(autoincrement())
shop String
title String
productId String
productHandle String
productVariantId String
destination String
scans Int @default(0)
createdAt DateTime @default(now())
}各カラムの役割
id:自動採番する主キーshop:ストアを識別する値title:管理用タイトルproductId:紐付ける商品のIDproductVariantId:商品のバリアントIDdestination:商品ページなどの遷移先scans:スキャン回数createdAt:作成日時
shopを持たせることで、ストア単位でデータを検索できます。
セッション情報に関する注意点
公式ドキュメントでは、要点のみのカラムが示されています。実際には、テンプレートの構成に応じてfirstNameやemailなどのカラムも必要です。
以前のRemixライブラリと、React Router v7フレームワークでは構成が異なります。利用中のテンプレートと公式情報を照合してください。
マイグレーションを実行する
schema.prismaを書くだけでは、DBにテーブルは作られません。次のコマンドでSQLiteへ反映します。
npx prisma migrate dev --name add_qrcodemigrate dev:開発用DBへ反映--name add_qrcode:変更履歴の名前
Prisma Studioで保存内容を確認する
Prisma Studioを使うと、DBの内容をブラウザで確認できます。
npm run prisma studiohttp://localhost:5555QRCodeテーブルにデータが入っているかを、画面上で確認できます。


DBを目視できるため、保存処理の確認がしやすくなります。次は、DBとShopify Admin APIをつなぐ処理を作ります。
3. QRコードのサーバー処理を作る
QRCode.server.jsに、QRコード機能の中核を集約します。画面側からDB操作やAPI処理を直接呼ばずに済みます。
PrismaでQRコードを取得、保存する
Admin APIで商品情報を補完する
QRコード画像をData URLで生成する
入力値をバリデーションする
保存と削除を処理する
モデル層の実装
// app/models/QRCode.server.js
import qrcode from "qrcode";
import invariant from "tiny-invariant";
import db from "../db.server";
export async function getQRCode(id, graphql) {
const qrCode = await db.qRCode.findFirst({ where: { id } });
if (!qrCode) return null;
return supplementQRCode(qrCode, graphql);
}
export async function getQRCodes(shop, graphql) {
const qrCodes = await db.qRCode.findMany({
where: { shop },
orderBy: { id: "desc" },
});
if (qrCodes.length === 0) return [];
return Promise.all(qrCodes.map((qrCode) => supplementQRCode(qrCode, graphql)));
}
export function getQRCodeImage(id) {
const url = new URL(process.env.SHOPIFY_APP_URL);
url.pathname = `/qrcodes/${id}`;
return qrcode.toDataURL(url.href);
}
export function getDestinationUrl(qrCode) {
switch (qrCode.destination) {
case "product":
return `https://${qrCode.shop}/products/${qrCode.productHandle}`;
case "checkout":
return `https://${qrCode.shop}/cart/${qrCode.productVariantId}:1`;
default:
return `https://${qrCode.shop}`;
}
}
async function supplementQRCode(qrCode, graphql) {
const query = `#graphql
query supplementQRCode($id: ID!) {
product(id: $id) {
title
handle
images(first: 1) {
edges {
node { url altText }
}
}
}
}
`;
const response = await graphql(query, {
variables: { id: qrCode.productId },
});
const { data: { product } } = await response.json();
const productDeleted = !product?.title;
const productHandle = product?.handle || qrCode.productHandle || "";
return {
...qrCode,
productDeleted,
productTitle: product?.title || "",
productHandle,
productImage: product?.images?.edges?.[0]?.node?.url || null,
productAlt: product?.images?.edges?.[0]?.node?.altText || "",
destinationUrl: getDestinationUrl({ ...qrCode, productHandle }),
image: await getQRCodeImage(qrCode.id),
};
}
export function validateQRCode(data) {
const errors = {};
if (!data.title) errors.title = "Title is required";
if (!data.productId) errors.productId = "Product is required";
if (!data.destination) {
errors.destination = "Destination is required";
} else if (!["product", "checkout"].includes(data.destination)) {
errors.destination = "Destination must be 'product' or 'checkout'";
}
return errors;
}
export async function upsertQRCode({ id, shop, data }) {
invariant(shop, "shop is required");
const payload = {
shop,
title: data.title,
productId: data.productId,
productHandle: data.productHandle || "",
productVariantId: data.productVariantId,
destination: data.destination,
};
if (id) {
return db.qRCode.update({ where: { id }, data: payload });
}
return db.qRCode.create({ data: payload });
}
export async function deleteQRCode(id) {
return db.qRCode.delete({ where: { id } });
}このファイルで押さえるポイント
findMany:ストア別にQRコードを一覧取得graphql:商品名や画像を取得supplementQRCode:表示用データを統合toDataURL:画像ファイルなしでQR画像を生成validateQRCode:必須項目を確認
画面側はモデル層を呼ぶだけにすると、処理の責務が分かれます。
4. 管理画面にQRコード一覧を作る
/appでは、保存済みのQRコードを一覧表示します。データがない場合は、作成ページへ進める空状態を表示します。
loaderで管理画面の認証を確認する
現在のストアを特定する
DBからQRコードを取得する
商品情報を補完して画面へ渡す
loaderの実装
export async function loader({ request }) {
const { admin, session } = await authenticate.admin(request);
const qrCodes = await getQRCodes(session.shop, admin.graphql);
return { qrCodes };
}authenticate.admin(request)は、管理画面からのアクセスを確認します。認証後は、ストア情報とAdmin API用のGraphQLクライアントを取得できます。
Polaris Web Componentsで表示する
<s-page>や<s-table>などのs-*は、Shopify管理画面向けのUIです。
余白、見出し、空状態、テーブルを整えやすくなります。まず機能を作り、UIの細部を後から調整できます。
一覧ページの実装要点
export default function Index() {
const { qrCodes } = useLoaderData();
return (
<s-page heading="QR codes">
<s-link slot="secondary-actions" href="/app/qrcodes/new">
Create QR code
</s-link>
{qrCodes.length === 0 ? (
<EmptyQRCodeState />
) : (
<QRTable qrCodes={qrCodes} />
)}
</s-page>
);
}実際の一覧表示では、タイトル、商品、作成日、スキャン数を表示します。商品が削除済みの場合は、その状態も表示できます。
空の状態
データがある状態
一覧ページが表示できたら、管理画面の基本導線は完成です。次に、QRコードの作成と編集を実装します。
5. QRコードの作成・編集ページを作る
作成と編集は、1つのルートでまとめて扱えます。/newなら新規作成、数字のIDなら編集として処理します。
loader:初期データや既存データを取得action:保存、削除、リダイレクトを処理React:入力状態と商品選択UIを管理
新規作成と編集の切り替え
React Routerのloaderとactionを使うと、表示と送信処理を同じファイルに整理できます。
export async function loader({ request, params }) {
const { admin } = await authenticate.admin(request);
if (params.id === "new") {
return { destination: "product", title: "" };
}
return await getQRCode(Number(params.id), admin.graphql);
}保存と削除の処理
export async function action({ request, params }) {
const { session, redirect } = await authenticate.admin(request);
const data = {
...Object.fromEntries(await request.formData()),
shop: session.shop,
};
if (data.action === "delete") {
await db.qRCode.delete({ where: { id: Number(params.id) } });
return redirect("/app");
}
const errors = validateQRCode(data);
if (errors) {
return new Response(JSON.stringify({ errors }), {
status: 422,
headers: { "Content-Type": "application/json" },
});
}
const qrCode = params.id === "new"
? await db.qRCode.create({ data })
: await db.qRCode.update({
where: { id: Number(params.id) },
data,
});
return redirect(`/app/qrcodes/${qrCode.id}`);
}商品選択と保存ボタンの注意点
商品選択には、ShopifyのResource Pickerを使います。選択後は商品ID、バリアントID、タイトル、ハンドル、画像をフォーム状態へ保存します。
公式ドキュメントの実装では、formの送信とuseSubmitの二重送信に注意が必要です。ここでは、e.preventDefault()で通常送信を止め、認証情報を含むuseSubmitで送信します。
function handleSave(e) {
e.preventDefault();
const data = {
title: formState.title,
productId: formState.productId || "",
productVariantId: formState.productVariantId || "",
productHandle: formState.productHandle || "",
destination: formState.destination,
};
submit(data, { method: "post" });
}保存処理で認証エラーが出る場合は、送信方法を確認してください。useSubmitやuseFetchを使う構成と、formの動作が重複していないかを見直します。
6. 公開ページでQRコード画像を表示する
公開ルートは、Shopify管理画面の外からアクセスできるようにします。QRコードを読む人は、管理画面へログインしているとは限りません。
そのため、/qrcodes/:idではAdmin認証を使わず、DBからQRコードを取得します。
公開ページの実装
import invariant from "tiny-invariant";
import { useLoaderData } from "react-router";
import db from "../db.server";
import { getQRCodeImage } from "../models/QRCode.server";
export const loader = async ({ params }) => {
invariant(params.id, "Could not find QR code destination");
const id = Number(params.id);
const qrCode = await db.qRCode.findFirst({ where: { id } });
invariant(qrCode, "Could not find QR code destination");
return {
title: qrCode.title,
image: await getQRCodeImage(id),
};
};
export default function QRCode() {
const { image, title } = useLoaderData();
return (
<>
<h1>{title}</h1>
<img src={image} alt="QR Code for product" />
</>
);
}
公開ページで画像が表示できれば、QRコードを外部へ配置できます。
7. スキャン数を加算して目的URLへ転送する
スキャン用ルートでは、計測とリダイレクトを一度に行います。画面を表示するのではなく、処理後に目的URLへ移動させます。
QRコードIDを確認する
DBから対象データを取得する
scansを1増やす商品ページなどへリダイレクトする
スキャン用loaderの実装
import { redirect } from "react-router";
import invariant from "tiny-invariant";
import db from "../db.server";
import { getDestinationUrl } from "../models/QRCode.server";
export const loader = async ({ params }) => {
invariant(params.id, "Could not find QR code destination");
const id = Number(params.id);
const qrCode = await db.qRCode.findFirst({ where: { id } });
invariant(qrCode, "Could not find QR code destination");
await db.qRCode.update({
where: { id },
data: { scans: { increment: 1 } },
});
return redirect(getDestinationUrl(qrCode));
};loaderは、ページ表示前に実行されるサーバー処理です。フロント側のJavaScriptが不要なため、スマートフォンや外部ブラウザからも利用できます。
計測URLは、生成したQRコードが指すURLとして利用します。実装後は、アクセスごとにscansが増えるか確認します。
8. 動作確認とトラブル対処
最後は、管理画面、公開ページ、スキャン処理の順で確認します。機能ごとに切り分けると、エラーの場所を特定しやすくなります。
管理画面の確認
/appにQRコード一覧が表示される作成ページを開ける
必須項目を入力して保存できる
保存後に編集画面へ移動する
QRコード画像と公開URLを確認できる
公開ページとスキャンの確認
/qrcodes/:idへログインなしでアクセスできるQRコード画像が表示される
/qrcodes/:id/scanでエラーが出ないPrisma Studioでscansが1増える
商品ページまたはチェックアウトへ移動する
よくある確認ポイント
QRコードのURLが
{APP_URL}/qrcodes/:id/scanになっているSHOPIFY_APP_URLが.envに設定されているQRCodeテーブルにデータがある
productIdとproductVariantIdが保存されている
destinationの値が許可された値になっている
認証エラーは送信処理を、表示エラーはloaderを確認します。DBの値はPrisma Studioで確認すると、切り分けが進みます。
9. まとめ|次にコードを試す
本記事では、React Router版のShopify公式テンプレートを使いました。Prisma、Admin API、Polaris Web Componentsを組み合わせ、QRコードアプリを構築しています。
アプリの初期構築
QRCodeテーブルの作成
商品情報の取得
管理画面の一覧と編集
公開ページの表示
スキャン数の計測
まずはテンプレートを起動し、QRCodeテーブルを追加してください。その後、一覧、編集、公開、計測の順に進めると確認しやすくなります。
さらに学びたい方へ
実務では、Resource Picker、Billing、Webhook、Flow、Functions連携も登場します。セキュリティや運用設計など、公式ドキュメントだけでは判断しにくい論点もあります。
Shopifyアプリ開発を体系的に学びたい方は、オンラインスクールテックギークも選択肢です。
実案件レベルの設計や、React Router、Web Componentsの構成を学びたい方は、公式サイトで内容を確認してください。
コードを試す際は、この記事の手順と公式ドキュメントを照合してください。質問やエラーの内容は、実行したコマンドとともに整理すると確認しやすくなります。
コメントを投稿