Vendure開発ガイド¶
このドキュメントは、Vendure公式ドキュメントを基に、プロジェクト内で開発に必要な主要情報をまとめた包括的な開発ガイドです。
より深い技術詳細が必要な場合: Vendureのアーキテクチャ、コアコンセプト、高度な実装パターンについては Vendure開発ハンドブック を参照してください。
目次¶
Vendureの基本概念¶
GraphQL API¶
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: 商品コレクション(カテゴリ)
コレクションとファセットの運用ルール(標準)¶
- 役割分担
- Collection: ナビゲーション/特集ページ用の「見せ方のグルーピング」。階層・順序・アイキャッチを持つ。
- Facet: 検索・絞り込み・自動分類の「属性軸」。商品やバリアントに複数付与できる。
- 推奨フロー
1) 商品にファセット値を付与(code は ASCII、表示名は日本語OK)。
2) コレクションはファセット条件(facet-value-filter)で自動収集する。手動追加は例外的な特集のみ。
3) 新しい切り口が必要なときは、先にファセット軸を定義し、その値をコレクション条件で参照する。 - 標準軸(本プロジェクト)
series(表示: シリーズ):exuviance,mesoceutical, …product-type(表示: 商品種別):consumables,skincare, …- 命名規約
- Facet code / Facet value code は ASCII に統一。翻訳で日本語表示を付ける。
- コレクションの slug も ASCII。表示名は日本語で良い。
- 運用ポイント
- 商品追加時は「ファセット付与だけ」でコレクション反映を完結させ、運用コストを最小化する。
- 大量インポート時は
setApplyAllFiltersOnProductUpdates(false)で一時停止→インポート→triggerApplyFiltersJobで再適用すると高速。 - 絞り込み UI とナビゲーションの分類軸を揃えることで、UX とデータ整合性を保つ。
チャネルとマルチテナント¶
Vendureはチャネルベースのマルチテナントアーキテクチャをサポートしています。各チャネルは独立した商品カタログ、価格設定、在庫管理を持てます。
詳細情報: チャネルとゾーンの技術的な詳細については Vendureコアコンセプト詳細解説 を参照してください。
認証・認可システム¶
- 認証: CookieまたはBearer Tokenによる認証
- 認可: Role-Based Access Control (RBAC)による権限制御
- Shop API: 顧客認証(Customer)
- Admin API: 管理者認証(Administrator)
開発環境セットアップ¶
前提条件¶
- Node.js: v22.x以上
- PostgreSQL: 17+ (PGroonga拡張機能付き)
- PNPM: ワークスペース管理用
ローカル開発環境の構築¶
1. データベース設定¶
# PostgreSQLを起動(ポート5433)
createdb -p 5433 ritsubi_vendure_dev
2. 依存関係のインストール¶
# プロジェクトルートから
pnpm install
3. 環境変数の設定¶
.env.localファイルを作成:
# データベース
DATABASE_HOST=localhost
DATABASE_PORT=5433
DATABASE_NAME=ritsubi_vendure_dev
DATABASE_USERNAME=postgres
DATABASE_PASSWORD=your_password
# サーバー
PORT=4000
# 認証
SUPERADMIN_USERNAME=superadmin
SUPERADMIN_PASSWORD=superadmin
COOKIE_SECRET=cookie-secret
4. 開発サーバーの起動¶
# Vendureサーバーの起動(dotenvx経由、ホットリロード対応)
cd apps/vendure-server
pnpm run dev
# Docker依存サービスも含めて起動
pnpm run dev:full
# 本番相当のビルドで確認
pnpm run build
pnpm start
5. Dashboardのビルド(初回のみ)¶
cd apps/vendure-server
pnpm run dashboard:build
注意: pnpm + monorepo環境では、vite.config.mts で tempCompilationDir
を指定する必要があります。詳細は
Vendure Server AGENTS.md
を参照してください。
アクセスURL¶
- Shop API:
http://localhost:4000/shop-api - Admin API:
http://localhost:4000/admin-api - Dashboard:
http://localhost:4000/dashboard/ - GraphiQL:
http://localhost:4000/admin-api(ブラウザで開くとGraphiQLが表示)
主要な開発タスク¶
プラグイン開発の基本¶
プラグインの構造¶
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!
}
`;
データベースマイグレーション¶
運用方針(TypeORM)¶
- Vendure公式が推奨する標準手段は
TypeORM マイグレーション。
npx vendure migrateで生成・実行・ロールバックを行い、本番ではsynchronizeを無効化(false)する。参照: Vendure Migrations ガイド - 本リポジトリでは下記スクリプトに統一(Vendure CLI を内部で呼び出し)。環境に応じて利用すること。
マイグレーションの生成¶
# Vendure CLIを使用
pnpm run migrate:generate --name=my-migration
マイグレーションの実行¶
# 開発環境
pnpm run db:migrate:local
# 本番環境
pnpm run db: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: 4000,
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'),
// assetUrlPrefixは完全なURLを指定するか、undefinedにしてプラグインに自動推測させる
// 詳細は object-storage.md を参照
assetUrlPrefix: process.env.VENDURE_ASSET_URL ?? process.env.ASSET_URL ?? 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.FRONTEND_URL || 'http://localhost:3000',
credentials: true,
},
}
API開発¶
GraphQLスキーマの理解¶
VendureのGraphQLスキーマは自動生成されます。スキーマを確認するには:
- GraphiQLで開く:
http://localhost:4000/admin-api - スキーマ生成コマンドを実行:
pnpm run schema:generate
生成されたスキーマは packages/shared-graphql/schema/ に保存されます。
GraphiQLの使用方法¶
GraphiQLはVendureに標準搭載されています。
アクセス方法¶
- Admin API:
http://localhost:4000/admin-apiをブラウザで開く - Shop API:
http://localhost:4000/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
}
参照リンク¶
ストアフロント開発¶
ストアフロント(Next.js)の開発については、以下を参照してください:
- Storefront AGENTS.md: ストアフロント開発ガイドライン
- コンポーネント管理ルール
- スタイリング規約(Tailwind CSS v4 + shadcn/ui)
- Storybook運用ルール
サーバー開発¶
Vendureサーバーの開発については、以下を参照してください:
- Vendure Server AGENTS.md: サーバー開発ガイドライン
- Dashboard Plugin設定
- 日本語化(国際化)手順
- Dashboard拡張の開発
プラグイン実装¶
カスタムプラグインの実装については、以下を参照してください:
- プラグイン実装ガイド: プラグイン実装の包括的なガイド
- プラグイン概要と設計方針
- 顧客管理プラグイン
- 価格システムプラグイン
- 配送計算プラグイン
- キャンペーンエンジンプラグイン
- 在庫管理プラグイン
- 同意システムプラグイン
- SMILE連携プラグイン
システムアーキテクチャ¶
インフラストラクチャとシステムアーキテクチャについては、以下を参照してください:
- インフラストラクチャ設計: システムアーキテクチャの詳細
- システムアーキテクチャ設計書
- デプロイメントガイド
- 監視・運用設計書
カートシート(簡易カートサマリー)の仕様¶
- 表示タイミング:
Vendureの
addItemToOrderがOrder型で成功し、CustomerProvider.refreshCustomer()が完了した後にのみ表示する。APIがErrorResultを返す場合や、同意未完了などで失敗した場合はシートを開かず、エラー表示にフォールバックする。 - レイアウト:
- デスクトップは画面右側に固定幅のSheet、モバイルはボトムシートで全幅表示(
shadcn/uiベースのSheetコンポーネント)。 - ヘッダー(追加完了メッセージ)→ 追加した商品の概要 → 「カートに入っている商品」リスト → 合計金額+CTAの順で構成する。
- 操作:
- カート行は
activeOrder.lines由来の確定済みデータのみ表示。数量更新・削除はADJUST_ORDER_LINE/REMOVE_ORDER_LINEを呼び出し、完了後にrefreshCustomer()で再取得する。 - 追加直後のラインは
refreshCustomer()によって確定データが取得される前提のため、Sheet内に仮行は生成しない。 - CTA: 「商品選択に戻る」「カートを確認」「注文手続きへ」の3ボタンを常設し、
lucide-reactアイコンで用途を明示する。
実装箇所:
apps/storefront/src/components/cart/cart-sheet.tsx、apps/storefront/src/components/product/add-to-cart-button.tsx
その他のリファレンス¶
- Vendure B2B機能リファレンス: VendureのB2B向け標準機能の詳細解説
- 開発ガイド: Vendure CLI開発ワークフロー
- Vendure公式ドキュメント: 公式ドキュメント(英語)
データ投入・シード¶
- 開発時シード:
pnpm --filter ritsubi-vendure-server run db:seed:local(ts-node 実行) - 本番相当:
pnpm --filter ritsubi-vendure-server run db:seed(distビルド後) - クリーンリセット:
pnpm --filter ritsubi-vendure-server run db:reset:clean(スキーマ再作成+マイグレーション+シード) - ドライ整合性チェック(DB書き込みなし)
pnpm --filter ritsubi-vendure-server run seed:validate -- --products ./apps/vendure-server/src/data/products.csv - defaultZone / zones / countries / taxCategories / products.csvヘッダーと必須値 / 数値フィールド / SKU・slug重複を検査し、問題があれば exit code 1
- 詳細は vendure-seed-data-guide.md を参照
更新日: 2025-01-XX
メンテナー: Ritsubi開発チーム