frontic-implementation について
frontic-implementationは、Fronticのブロック、リスティング、ページを作成・管理するスキルです。利用前のデータ構造確認と適合するリソースの再利用を重視し、クライアント生成、クエリ、ルーティング、フィールド設定も案内します。
主な機能
- 01利用前にリソースのデータ構造を確認
- 02再利用・拡張・新規作成を判断
- 03ブロックとリスティングの設定を案内
- 04ページとクエリでのクライアント利用を説明
- 05フィルター、ソート、フィールド検証を網羅
- 06変更後のクライアント再生成を要求
活用シーン
- 01Fronticの商品リスティングの構築
- 02動的ルートとページデータの連携
- 03フィルター、ソート、ページネーションの追加
- 04フィールド誤りや古い生成コードの調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ frontic/ skills --skill frontic-implementationインストール後、Claude Code で/frontic-implementationを入力して読み込まれたか確認します。
詳細
Frontic の実装と設定
ユーザーのリクエストに基づいてブロック、リスティング、ページを自律的に作成または管理する場合は、このスキルを使用します。常にモックデータより Frontic のデータソースを優先してください。
開始する前に
ブロック、リスティング、またはページを使用する前に、fetch_api_call ツールを使用してデータ構造を確認してください。これにより、存在しないコンテンツや判読できない ID がユーザーに表示されるのを防げます。
1. リソースの選択
新しいブロックやリスティングを作成する前に、現在の Frontic の設定を分析します。
- 再利用または拡張できる既存のブロック
- ユーザーの要件に合う既存のリスティング — パラメーターの互換性とデータ構造を確認します
- 関連データを含む利用可能なストレージ
再利用、拡張、新規作成の判断
| シナリオ | アクション |
|---|---|
| 完全に一致(同じユーザージャーニー、同じパラメーター) | 再利用 |
| 近いが完全には適合しない | 適切であれば拡張、または新規作成 |
| パラメーターまたは目的が異なる | 新規作成 |
ルール(厳守)
- 無理に適合させないでください:設計されていないユースケースに、回避策としてブロックやリスティングを使用しないでください。
- パラメーターの互換性は絶対条件です:リスティングにパラメーター(例:
categoryId)が必要で、ユーザーのユースケースでそれを指定できない(例:グローバル検索)場合は、新しいリスティングを作成してください。ダミー値やプレースホルダー値は決して使用しないでください。 - ユーザーの意図を最優先します:ユーザーが示した意図とユーザージャーニーに正確に合わせてください。
- 判断の理由を記録します:リソースを再利用、拡張、または新規作成した理由を簡潔に説明してください。
⚠️ 警告: 異なるユースケースに既存のリソースを適用するために、ダミー値やデフォルト値のパラメーターを使っていることに気づいたら、作業を中止してください。代わりに新しいリソースを作成してください。
2. ブロックとリスティングの作成
ブロックの作成
- データ構造を分析する:ブロックが表す単一のレコードまたはエンティティを特定します
- 包括的なスキーマを定義する:関連する e コマースのフィールドをすべて含めます
- バリエーションを想定する:商品タイプ、カテゴリーなどを考慮します
一般的なブロック: ProductDetail、CategoryDetail、UserProfile、OrderDetail、ReviewDetail、BrandDetail
リスティングの作成
- コレクションの種類を特定する:商品、カテゴリー、ブログ記事など
- 埋め込みブロックを特定する:リスティングは、プロジェクト内で作成済みのブロックを参照する必要があります
- フィルタリングを設定する:価格、カテゴリー、ブランド、評価、在庫状況
- 並べ替えを計画する:価格、人気度、新着順、評価
- 検索を有効にする:関連する場合(説明、タイトル)
- グローバル利用に向けてパラメーターを任意にする:グローバル検索と特定のコンテキストの両方で使うリスティングでは、デフォルト値を設定してパラメーターを任意にします。
{
"name": "categoryId",
"dataType": "string",
"required": false,
"defaultValue": "root-id"
}
これにより、グローバル検索には listing("GlobalSearch", {}) を、特定の範囲に絞った検索には listing("GlobalSearch", { categoryId: "xyz" }) を使用できます。
一般的なリスティング: ProductListing、CategoryListing、OrderListing、ReviewListing、SearchResults
クライアントの再生成
ブロックまたはリスティングを作成または更新したら、必ず Frontic Client を再生成してください。
npx @frontic/cli@latest generate
3. Frontic Client の使用
クライアントをインポートします(.frontic/ のパスはアプリケーションのルートを基準に解決します)。
import client from '../../.frontic/generated-client'
ブロック(キーで指定する単一アイテム)
単一アイテムは常にキーで識別されます。'root-category' のようなプレースホルダーキーを作らず、ブロックに関連付けられたストレージにある実際のキーを使用してください。
const data = await client.block(
'CategoryDetail',
'a3f4b5c6-d7e8-49ab-9c2d-3e4f5e6d7c8b'
)
const data = await client.block(
'ProductDetail',
'123e4567-e89b-12d3-a456-426614174000'
)
リスティング(フィルタリング、並べ替え、ページネーションに対応したコレクション)
フィルターと並べ替えのオプションについては、.frontic/query-types.ts と .frontic/fetch-api.d.ts を参照してください。
const products = await client.listing('ProductList', {
categoryId: 'e72b0f5e-1c3d-4a7e-913a-2e4c95cd8f31'
})
const results = await client.listing(
'ProductList',
{ categoryId: '...' },
{
query: {
filter: [
{
type: 'and',
filter: [
{ type: 'equals', field: 'attributes.size', value: '10' },
{ type: 'range', field: 'price.amount', from: 10 }
]
},
{ type: 'range', field: 'price.amount', from: 2000, to: 10000 },
{ type: 'equals', field: 'sale', value: true }
],
sort: { field: 'price.amount', order: 'asc' },
search: 'Red',
page: 1,
limit: 20
}
}
)
ページ(ルートで取得)
// The route path is the full path including the domain without the protocol
const pageData = await client.page('demo-shop.com/uk/women/shoes')
型リファレンス
ブロック、リスティング、ページ、パラメーター、戻り値の型については、.frontic/generated-types.d.ts と .frontic/query-types.ts を確認してください。
4. ページ統合と動的更新
動的ルーティングにはページを使用する
ユーザーが別の方法を求めている場合を除き、動的ルーティングには常にページを使用してください。ページは、スラッグで識別されるブロックを格納するコンテナです。ページのスラッグには、ドメイン、ロケール(設定されている場合)、パスが含まれます。ブロックには、ルート情報を示す pageRoute フィールドを設定できます。
ルートレベルの動的ルーティングには、組み立てたスラッグを指定して client.page() を呼び出すキャッチオールページを作成してください。ルートケース / は別途処理します(例:ハードコードしたブロックを使用)。
ページ構造の階層
Page (e.g. CategoryDetail)
├── Block (e.g. CategoryDetailShopware)
└── Nested Listing (e.g. ProductListShopware)
実装パターン
- 初期読み込み:
client.page()またはclient.block()を使用します。ページコンテキスト全体と埋め込みコンテンツが取得できます。 - フィルタリング/並べ替え/ページネーション:
client.listing()を使用します。client.page()とclient.block()はクエリパラメーターを受け付けません。
// 1. Initial page load
const pageData = await client.page('domain.com/category-name')
// 2. Dynamic updates — extract params, call embedded listing directly
const categoryId = pageData.data.key
const filteredData = await client.listing('ProductListShopware', { categoryId }, {
query: { filter: [...], sort: {...}, page: 2 }
})
まとめ: 初期読み込みには、常に client.page() を使用してください。動的更新では、ネストされたリスティングを特定し、ページデータから必要なパラメーターを抽出して、クエリパラメーターを指定して client.listing() を呼び出します。コンポーネントの状態は、リスティング部分のみを更新してください。
5. フィールド設定
パスを使用したフィルターフィールドの命名
フィルターフィールドに path パラメーターを設定している場合、クエリでは完全なドット区切りパスを使用する必要があります。
// Configuration:
{ "name": "price", "source": "blockField", "path": "amount" }
// In queries — use "price.amount", NOT "price":
{ "type": "range", "field": "price.amount", "from": 2000, "to": 10000 }
ルール: フィルターフィールドに path: "X" が設定されている場合、クエリでは常に fieldName.X として参照してください。
ソートフィールドの設定
Frontic では、異なる方向を持つ同名のソートフィールドはサポートされていません。各ソートフィールドは一度だけ追加してください。API は asc と desc の両方を自動的にサポートします。方向はクエリ時に指定してください。
// One sort field configured, direction chosen per query:
sort: { field: 'price.amount', order: 'asc' } // or 'desc'
フィールドソースの選択
検索、フィルター、ソートのフィールドを設定する際は、常に storageField より blockField を優先してください。
- ブロックフィールドはすでに公開され、設定されています
- ストレージフィールドはアクセスできない場合や、適切な型が設定されていない場合があります
- ブロックフィールドを使うと、ネストされたブロック構造との一貫性が保たれます
// Prefer:
{ type: "filter", name: "price", source: "blockField" }
// Avoid:
{ type: "filter", name: "price", source: "storageField" }
フィールドパスの検証
フィルターまたはソートフィールドを使用する前に、正確な名前を確認してください。
list_resourcesを使用して設定済みのフィールドを確認しますfetch_api_callを使用してリスティングをテストし、レスポンスの構造を確認します
// Test listing response reveals field structure:
// items: [{ price: { amount: 49595 } }]
// → filter field is "price.amount"
6. 価格の形式
Frontic の価格は、精度を持つ整数形式です。
{ "amount": 49595, "currency": "EUR", "precision": 2, "ref": 0 }
表示時の計算:
const displayPrice = price.amount / Math.pow(10, price.precision)
// 49595 / 100 = 495.95
7. トラブルシューティング
ブラウザーとビルドキャッシュ
クライアントを再生成した後も、キャッシュによって古いコードが残ることがあります。
- フレームワークのビルドキャッシュをクリアする(例:
.next/、.nuxt/、dist/) - ブラウザーをハードリフレッシュする: Cmd+Shift+R(Mac)/Ctrl+Shift+F5(Windows/Linux)
- DevTools でキャッシュを無効にする: Network タブ →「Disable cache」