コンテンツにスキップ

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.mtstempCompilationDir を指定する必要があります。詳細は 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 }),
],

認証設定

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スキーマは自動生成されます。スキーマを確認するには:

  1. GraphiQLで開く: http://localhost:4000/admin-api
  2. スキーマ生成コマンドを実行:
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拡張の開発

プラグイン実装

カスタムプラグインの実装については、以下を参照してください:

システムアーキテクチャ

インフラストラクチャとシステムアーキテクチャについては、以下を参照してください:

カートシート(簡易カートサマリー)の仕様

  • 表示タイミング: VendureのaddItemToOrderOrder型で成功し、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.tsxapps/storefront/src/components/product/add-to-cart-button.tsx

その他のリファレンス

データ投入・シード

  • 開発時シード: 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開発チーム