Vendure開発ガイド¶
このドキュメントは、Vendure公式ドキュメントを基に、プロジェクト内で開発に必要な主要情報をまとめた包括的な開発ガイドです。
より深い技術詳細が必要な場合: Vendureのアーキテクチャ、コアコンセプト、高度な実装パターンについては Vendure開発ハンドブック を参照してください。
目次¶
Vendureは2つのGraphQL APIエンドポイントを提供します:
- Shop API (
/shop-api): ストアフロント(顧客向け)用のAPI - Admin API (
/admin-api): 管理画面(管理者向け)用のAPI
両方のAPIは同じVendureサーバーで動作し、異なる認証・認可ルールを適用します。
プラグインシステム¶
Vendureはプラグインアーキテクチャを採用しており、すべての機能はプラグインとして実装されます。
@VendurePlugin({
imports: [PluginCommonModule],
providers: [MyService],
shopApiExtensions: {
resolvers: [MyResolver],
schema: myGraphqlSchema,
},
})
export class MyPlugin {}
プラグイン開発の実装手順: 詳細な実装ガイドはプラグイン開発の基礎を参照してください。
データモデル¶
Vendureの主要なエンティティ:
- Product: 商品情報(複数のVariantを持つ)
- ProductVariant: 商品のバリエーション(SKU、価格、在庫)
- Customer: 顧客情報
- Order: 注文情報
- Channel: チャネル(マルチテナント対応)
- Collection: 商品コレクション(カテゴリ)
コレクション / ファセット / custom field の運用ルール(標準)¶
- 役割分担
- Collection: ストアフロントのカテゴリ / ナビゲーション階層 / 一覧ページ。Brand page、brand 配下 menu、campaign 導線をここで表現する。
- Facet: 属性・絞り込み・内部ルールの基礎。Brand は
brandFacet の FacetValue として保持する。 - custom field: 数値・複雑条件・計算専用値。Facet に載せにくい詳細業務条件に使う。
- 標準パターン
brand/product-type/customer-visibility/shipping-modeなどの Facet を定義する。- Product に FacetValue を付与する(Brand は通常 Product 単位)。
- Brand page は
facet-value-filterでbrand=<facet value>を条件にした Collection として作る。 - Brand 配下 menu は親 filter 継承に追加 Facet 条件を組み合わせて表現する。
- この案件での基本配置
- Facet:
brand,product-type,customer-visibility,shipping-mode、必要に応じてconsent-required。 - Collection:
brands親 collection、各ブランド Collection、ブランド配下のprofessional/retail/set/promotionなどの menu Collection、campaigns配下の導線。 - custom field:
minOrderQty, 請求書要否、送料ルールキー、価格計算用の詳細条件など。 - 命名規約
- Facet code / Facet value code / custom field key / collection slug は ASCII に統一する。
- 画面表示名は日本語でよい。
- Dashboard 表示
- Vendure Dashboard 上で表示する custom field のうち、SMILE が SSoT で Vendure
上では編集禁止 (readonly) または編集非推奨 (インポートのみ運用) の項目には、
日本語ラベルの先頭に
☺(U+263A) を付ける。 ☺は運用者向けの「この値は SMILE 側で管理しており Vendure では触らない」目印で あり、保存値・field key・GraphQL schema 名には含めない。helper (markSmileSourcedLabel/markSmileSourcedCustomFieldLabel) 経由での付与を 推奨する。- Vendure 上で通常通り編集する想定の項目(SMILE を参照していても運用上 Vendure 側で書き換える前提のもの)には付けない。
- 自作 extension / plugin 由来の項目は
◇marker を別軸で付与する (☺と両立する場合は☺◇ ラベルの順)。 - 運用ポイント
- Brand のデータ真実は
brandFacetValue とし、Collection はそのページ/導線表現として扱う。 facet-value-filterは Brand Collection とその子 Collection を構成する標準手段として使う。- 顧客別の表示制御は、Collection 対象と customer 系 Facet / custom field を plugin / policy 側で組み合わせて判定する。
Product.customFields.brandCodeは互換維持や内部連携の補助値であり、Brand の正本にはしない。- 予約商品は facet ではなく
ProductVariant.customFieldsを正本とする。予約種別、送料方針、出荷予定日、releaseGroupKey は reservation custom field で管理し、受注分離は seller strategy で解決する。 - 大量インポート時は
setApplyAllFiltersOnProductUpdates(false)で一時停止→インポート→triggerApplyFiltersJobで再適用すると高速。 - 詳細な境界線は
docs/specifications/2026-03-collection-facet-boundary.mdを正本とする。
チャネルとマルチテナント¶
Vendureはチャネルベースのマルチテナントアーキテクチャをサポートしています。各チャネルは独立した商品カタログ、価格設定、在庫管理を持てます。
詳細情報: チャネルとゾーンの技術的な詳細については Vendureコアコンセプト詳細解説 を参照してください。
認証・認可システム¶
- 認証: CookieまたはBearer Tokenによる認証
- 認可: Role-Based Access Control (RBAC)による権限制御
- Shop API: 顧客認証(Customer)
- Admin API: 管理者認証(Administrator)
開発環境セットアップ¶
注意: 本リポジトリでは原則として
.env/.env.localを使用しません。最新のローカル開発手順はapps/vendure-server/local-development.mdを正として参照してください。
前提条件¶
- Node.js / pnpm:
mise.tomlとpackage.json#enginesを正とする - 依存サービス:
just dev-services(PostgreSQL/Redis/Mailpit 等)
ローカル開発環境の構築¶
1. 依存関係のインストール¶
# プロジェクトルートから
pnpm install
2. 環境変数の考え方¶
- 機密情報: just 経由で AWS Secrets Manager。
just devは内部でwith-env.shを呼び出し、最新のシークレットを自動注入します。 - 非機密(例: ポート): 起動コマンドで
PORT=3022 ...のように一時的に上書き。 - portless による名前解決: ローカル開発では
named.localhost(例:vendure.localhost:<worktree-proxy-port>)を使用します。ポート番号を意識せずにサービス間通信ができるよう、DATABASE_URLやVENDURE_BASE_URLはjust dev起動時にSSOT(Single Source of Truth)として構成されます。
ストアフロントの画像ホスト許可設定は VENDURE_BASE_URL(推奨)または
VITE_PUBLIC_ASSET_HOSTS を利用します。詳細は apps/storefront/AGENTS.md
の「Storefront 画像配信設定」を参照してください。
3. 開発サーバーの起動¶
# Vendureサーバーの起動(SSM経由、ホットリロード対応)
機密情報をロードして起動するために **just 経由**で AWS Secrets Manager を使用します。
```bash
# Vendure 開発用の設定を使う
just dev-vendure
```
```bash
# 依存サービスだけ先に上げたい場合に使う
just dev-services
既定の dev_vendure / local-vendure では、ローカル PostgreSQL
(127.0.0.1:5433) が未起動なら just dev-services
相当を自動実行します。staging_vendure
など別 config を使う場合は自動起動しません。
dev_vendure では SUPERADMIN_USERNAME=ec-admin /
SUPERADMIN_PASSWORD=devPassword!123
を dev/test 用の既定値として許容します。本番相当環境では引き続き fail-fast のままです。
React Dashboard を別ターミナルで起動する場合は just dev-dashboard
を使い、公開 URL は just ports で確認します。Dashboard 側の /admin-api
proxy は VENDURE_BASE_URL を正本にするため、固定 localhost
と portless を切り替えた場合は、この値が Vendure の実 URL と一致していることを確認してください。
Docker依存サービスも含めて起動¶
just dev-full
補足: just dev-vendure / just local-vendure / just dev-full /
just local-full 実行時は packages/plugins の tsc -w
を自動起動するため、プラグイン変更が即時反映されます。
本番相当のビルドで確認¶
pnpm run build pnpm run start
##### 5. Dashboardのビルド(初回のみ)
```bash
cd apps/vendure-server
pnpm exec nx run ritsubi-vendure-server:dashboard:build
注意: pnpm + monorepo環境では、vite.config.mts で tempCompilationDir
を指定する必要があります。詳細は apps/vendure-server/AGENTS.md
の Vite 設定セクションを参照してください。
アクセスURL¶
既定の CLI 開発導線では portless を使用します。<worktree-proxy-port>
の実値は just ports で確認してください。
- Shop API:
http://vendure.localhost:<worktree-proxy-port>/shop-api - Admin API:
http://vendure.localhost:<worktree-proxy-port>/admin-api - React Dashboard:
http://dashboard.localhost:<worktree-proxy-port>/ - GraphiQL:
http://vendure.localhost:<worktree-proxy-port>/admin-api(ブラウザで開くとGraphiQLが表示)
固定 localhost ポートで確認したい場合のみ、PORTLESS=0 just dev-vendure /
PORTLESS=0 just dev-dashboard または個別の dev コマンドを使います。
プラグインの構造¶
packages/plugins/my-plugin/
├── src/
│ ├── index.ts # プラグイン定義
│ ├── entities/ # カスタムエンティティ
│ ├── services/ # ビジネスロジック
│ ├── api/ # GraphQL Resolver
│ └── types.ts # 型定義
└── package.json
プラグインの実装例¶
import { PluginCommonModule, VendurePlugin } from "@vendure/core";
import { gql } from "graphql-tag";
import { MyResolver } from "./api/my.resolver";
import { MyService } from "./services/my.service";
const myGraphqlSchema = gql`
extend type Query {
myQuery: String!
}
extend type Mutation {
myMutation(input: String!): Boolean!
}
`;
@VendurePlugin({
imports: [PluginCommonModule],
providers: [MyService],
shopApiExtensions: {
resolvers: [MyResolver],
schema: myGraphqlSchema,
},
})
export class MyPlugin {}
詳細は プラグイン実装ガイド を参照してください。
GraphQL APIの拡張¶
Resolverの実装¶
import { Resolver, Query, Mutation, Args } from "@nestjs/graphql";
import { Ctx, RequestContext } from "@vendure/core";
@Resolver()
export class MyResolver {
constructor(private myService: MyService) {}
@Query()
async myQuery(@Ctx() ctx: RequestContext): Promise<string> {
return this.myService.getData(ctx);
}
@Mutation()
async myMutation(@Ctx() ctx: RequestContext, @Args("input") input: string): Promise<boolean> {
return this.myService.processData(ctx, input);
}
}
既存タイプの拡張¶
const schema = gql`
extend type Product {
customField: String!
}
extend type Query {
customProductQuery(id: ID!): Product!
}
`;
Admin API schema 変更時の生成物更新¶
packages/plugins側で Admin API の schema / mutation / type を変更したら、tracked 生成物の差分も確認する。- このリポジトリでは少なくとも以下が更新対象になりうる。
packages/contract/schema/admin.graphqlapps/vendure-server/src/gql/graphql-env.d.tsjust dashboard-buildや commit hook のcodegen-smartで後から差分が出ることがあるため、機能本体をコミットした後でもgit statusを再確認する。- 直近の実例では、SMILE import rollback の GraphQL 拡張後に
packages/contract/schema/admin.graphqlの追加入力が必要になった。
データベースマイグレーション¶
運用方針(TypeORM)¶
- Vendure公式が推奨する標準手段は
TypeORM マイグレーション。
npx vendure migrateで生成・実行・ロールバックを行い、本番ではsynchronizeを無効化(false)する。参照: Vendure Migrations ガイド - 本リポジトリでは下記スクリプトに統一(Vendure CLI を内部で呼び出し)。環境に応じて利用すること。
マイグレーションの生成¶
# 生成後に timestamp 正規化 / lint / data guard まで実行
pnpm exec nx run ritsubi-vendure-server:migrate:generate -- --name=my-migration
migrate:generateは生成物を 13 桁 epoch-ms timestamp へ正規化します。 AI / 手作業で 14 桁YYYYMMDDHHmmssの migration file を作らないでください。 手書きが必要な drop / backfill / hand-written migration の場合も 13 桁 unix-ms (例:1780100500000_my_change.ts/class MyChange1780100500000) で揃え、 サンプルや例示でも 14 桁形式を書かないでください。directory に残る既存 14 桁 file は legacy grandfather なので、それを見て pattern-match して真似しないでください。migrate:review:latestで最新の未コミット migration だけを再 review できます。VENDURE_SYNCHRONIZE=trueは development/test 用です。共有環境では通常の migration フローを使い、空の DB 初期化時のみALLOW_SCHEMA_SYNC=trueを明示します。- 既存スキーマがある環境では、
ALLOW_SCHEMA_SYNC=trueでも「pending migration がない」だけで自動 synchronize は実行しません。ローカルでスキーマを作り直す場合はdb:reset:clean/db:reset:dry-runを使います。
スキーマ変更の設計指針¶
customFields: Vendure core entity に少数の安定した列を追加したいときに使う。追加後は生成 migration を鵜呑みにせず、必要なcustomFields...カラムが入っているか必ず確認する。- JSONB / シリアライズ済み payload: 形が頻繁に変わる、問い合わせ要件が弱い、短期的に試したい属性を載せる。
- 独自テーブル / カスタムエンティティ: 検索・結合・制約・履歴管理が必要なもの、rename / 型変更が多く migration を壊しやすいものに使う。
rename・型変更・大量の add/drop が絡む変更は、自動生成に任せず手書き migration を前提にします。
マイグレーションの実行¶
# 開発環境
pnpm exec nx run ritsubi-vendure-server:migrate:run
# 本番環境
pnpm run migrate
詳細は 開発ガイド を参照してください。
カスタムエンティティの追加¶
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
@Entity()
export class MyCustomEntity {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
}
プラグインでエンティティを登録:
@VendurePlugin({
entities: [MyCustomEntity],
})
export class MyPlugin {}
イベントシステムの活用¶
Vendureは豊富なイベントを提供します:
import { EventBus, OrderPlacedEvent } from "@vendure/core";
@Injectable()
export class MyService {
constructor(private eventBus: EventBus) {
// イベントの購読
this.eventBus.ofType(OrderPlacedEvent).subscribe((event) => {
console.log("Order placed:", event.entity.id);
});
}
}
Vendure設定¶
vendure-config.tsの主要設定項目¶
設定ファイル: apps/vendure-server/src/vendure-config.ts
基本設定¶
export const config: VendureConfig = {
// API設定
apiOptions: {
port: 3021,
adminApiPath: "admin-api",
shopApiPath: "shop-api",
cors: {
origin: true,
credentials: true,
},
},
// 認証設定
authOptions: {
tokenMethod: ["bearer", "cookie"],
cookieOptions: {
secret: "cookie-secret",
httpOnly: true,
secure: false,
sameSite: "lax",
},
},
// データベース設定
dbConnectionOptions: {
type: "postgres",
host: "localhost",
port: 5433,
database: "ritsubi_vendure_dev",
synchronize: false, // 本番ではfalse
},
// デフォルト言語
defaultLanguageCode: LanguageCode.ja,
};
プラグインの初期化¶
plugins: [
// React Dashboard
DashboardPlugin.init({
route: 'dashboard',
appDir: join(__dirname, '../dist/dashboard'),
}),
// Asset Server
AssetServerPlugin.init({
route: 'assets',
assetUploadDir: join(__dirname, '../static/assets'),
// shop API は R2 custom domain、admin API は same-server /assets/ を返す
// 詳細は object-storage.md を参照
assetUrlPrefix: (ctx) =>
ctx.apiType === 'shop' && process.env.VENDURE_ASSET_URL
? process.env.VENDURE_ASSET_URL
: process.env.VENDURE_BASE_URL
? `${process.env.VENDURE_BASE_URL}/assets/`
: undefined,
}),
// カスタムプラグイン
MyPlugin.init({ options }),
],
認証設定¶
Cookie認証¶
authOptions: {
tokenMethod: ['cookie'],
cookieOptions: {
secret: process.env.COOKIE_SECRET,
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'lax',
},
}
Bearer Token認証¶
authOptions: {
tokenMethod: ['bearer'],
}
CORS設定¶
apiOptions: {
cors: {
origin: process.env.STOREFRONT_URL || 'http://localhost:4001',
credentials: true,
},
}
API開発¶
GraphQLスキーマの理解¶
VendureのGraphQLスキーマは自動生成されます。スキーマを確認するには:
- GraphiQLで開く:
- Portless:
http://vendure.localhost:<worktree-proxy-port>/admin-api - localhost:
http://localhost:${VENDURE_PORT:-3021}/admin-api - スキーマ生成コマンドを実行:
pnpm run schema:generate
生成されたスキーマは packages/contract/schema/ に保存されます。
GraphiQLの使用方法¶
GraphiQLはVendureに標準搭載されています。
アクセス方法¶
- Admin API:
- Portless:
http://vendure.localhost:<worktree-proxy-port>/admin-api - localhost:
http://localhost:${VENDURE_PORT:-3021}/admin-api - Shop API:
- Portless:
http://vendure.localhost:<worktree-proxy-port>/shop-api - localhost:
http://localhost:${VENDURE_PORT:-3021}/shop-api
主要機能¶
- 自動補完:
Ctrl+Spaceで利用可能なフィールドを表示 - ドキュメント: 右側の「Docs」パネルでスキーマを確認
- クエリ実行: クエリを書いて実行ボタンをクリック
詳細は API ドキュメント・可視化ガイド を参照してください。
クエリとミューテーションの書き方¶
クエリ例¶
query GetProducts {
products {
items {
id
name
slug
variants {
id
sku
price
}
}
totalItems
}
}
ミューテーション例¶
mutation CreateProduct {
createProduct(
input: { translations: [{ languageCode: ja, name: "新商品", slug: "new-product" }] }
) {
id
name
}
}
エラーハンドリング¶
GraphQL APIは標準的なエラーレスポンスを返します:
{
"errors": [
{
"message": "エラーメッセージ",
"extensions": {
"code": "ERROR_CODE",
"exception": {
"stacktrace": ["..."]
}
}
}
],
"data": null
}
参照リンク¶
ストアフロント開発¶
ストアフロント(Vite)の開発については、以下を参照してください:
apps/storefront/AGENTS.md: ストアフロント開発ガイドライン- コンポーネント管理ルール
- スタイリング規約(Tailwind CSS v4 + shadcn/ui)
- UIプレビュー運用ルール
サーバー開発¶
Vendureサーバーの開発については、以下を参照してください:
apps/vendure-server/AGENTS.md: サーバー開発ガイドライン- Dashboard Plugin設定
- 日本語化(国際化)手順
- Dashboard拡張の開発
プラグイン実装¶
カスタムプラグインの実装については、以下を参照してください:
- プラグイン実装ガイド: プラグイン実装の包括的なガイド
- プラグイン概要と設計方針
- 顧客管理プラグイン
- 商流ルール
- 配送計算プラグイン
- 商流ルール移行メモ
- 在庫管理プラグイン
- 同意システムプラグイン
- SMILE連携プラグイン
システムアーキテクチャ¶
インフラストラクチャとシステムアーキテクチャについては、以下を参照してください:
- インフラストラクチャ設計: システムアーキテクチャの詳細
- システムアーキテクチャ設計書
- デプロイメントガイド
- 監視・運用設計書
カート投入成功時の通知仕様¶
- 表示タイミング:
Vendure の
addItemToOrderがOrder型で成功し、CustomerProvider.refreshCustomer()が完了した後にtoast.success("カートに追加しました")を表示する。API がErrorResultを返す場合や、同意未完了などで失敗した場合は成功 toast を出さず、既存のエラー toast / 同意モーダルへフォールバックする。 - レイアウト:
カート投入後に sheet / drawer / modal を自動表示しない。ユーザーは商品詳細・一覧・クイックオーダー画面に留まり、カート内容確認はヘッダーのカート導線または
/cartで行う。 - 操作:
成功後も
activeOrderはrefreshCustomer()で再取得し、ヘッダーやカートページの数量・合計は server 権威値へ同期する。
実装箇所:
apps/storefront/src/components/product/add-to-cart-button.tsx
その他のリファレンス¶
- Vendure B2B機能リファレンス: VendureのB2B向け標準機能の詳細解説
- 開発ガイド: Vendure CLI開発ワークフロー
- Vendure公式ドキュメント: 公式ドキュメント(英語)
データ投入・シード¶
- 開発時シード:
pnpm exec nx run ritsubi-vendure-server:db:seed:local(dist/node 実行) - 本番相当:
pnpm exec nx run ritsubi-vendure-server:db:seed(distビルド後) - クリーンリセット:
pnpm exec nx run ritsubi-vendure-server:db:reset:clean(スキーマ再作成+マイグレーション+シード) - ドライ整合性チェック(DB書き込みなし)
pnpm exec nx run ritsubi-vendure-server:seed:validate -- --products ./apps/vendure-server/src/data/products.csv - defaultZone / zones / countries / taxCategories /
products.csvヘッダーと必須値 / 数値フィールド /
SKU・slug重複に加え、Vendure公式
ImportParserで必須列・翻訳列・カスタムフィールドも検証し、問題があれば exit code 1 --jsonで JSON 出力可能- 詳細は vendure-seed-data-guide.md を参照
更新日: 2025-01-XX
メンテナー: Ritsubi開発チーム