tanstack-db
コレクション・ライブクエリ・楽観的ミューテーションを備えた、API向けのリアクティブなクライアントファーストストアです。
description の原文を見る
Reactive client-first store for your API with collections, live queries, and optimistic mutations.
SKILL.md 本文
概要
TanStack DB は差分データフローに基づいて構築されたクライアント側の組み込みデータベースレイヤーです。正規化されたコレクションを維持し、ライブクエリのための増分計算を使用し、自動的な楽観的ミューテーションを提供し、データフェッチのために TanStack Query と統合します。100k 行以上であっても 1 ミリ秒未満の更新を実現します。
パッケージ: @tanstack/react-db
クエリ統合: @tanstack/query-db-collection
ステータス: ベータ版 (v0.5)
インストール
npm install @tanstack/react-db @tanstack/query-db-collection
コア概念
- Collections: データソース (TanStack Query、Electric など) をラップした正規化データストア
- Live Queries: SQL ライクなクエリビルダーによるリアクティブなサブスクリプション
- Optimistic Mutations: 自動的なインスタント UI 更新と失敗時のロールバック
- Differential Dataflow: 変更時に影響を受けるクエリ結果のみを再計算
コレクション
コレクションの作成
import { createCollection } from '@tanstack/react-db'
import { queryCollectionOptions } from '@tanstack/query-db-collection'
const todoCollection = createCollection(
queryCollectionOptions({
queryKey: ['todos'],
queryFn: async () => api.todos.getAll(),
getKey: (item) => item.id,
schema: todoSchema,
onInsert: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) =>
api.todos.create(mutation.modified)
)
)
},
onUpdate: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) =>
api.todos.update(mutation.modified)
)
)
},
onDelete: async ({ transaction }) => {
await Promise.all(
transaction.mutations.map((mutation) =>
api.todos.delete(mutation.original.id)
)
)
},
})
)
シンクモード
// Eager (デフォルト): コレクション全体を事前にロード。10k 行未満の場合に最適。
const smallCollection = createCollection(
queryCollectionOptions({ syncMode: 'eager', /* ... */ })
)
// On-Demand: クエリがリクエストしたものだけをロード。50k 行以上、検索に最適。
const largeCollection = createCollection(
queryCollectionOptions({
syncMode: 'on-demand',
queryFn: async (ctx) => {
const params = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions)
return api.getProducts(params)
},
})
)
// Progressive: クエリサブセットを即座にロード、フルシンクはバックグラウンドで実行。
const collaborativeCollection = createCollection(
queryCollectionOptions({ syncMode: 'progressive', /* ... */ })
)
ライブクエリ
基本的なクエリ
import { useLiveQuery } from '@tanstack/react-db'
import { eq } from '@tanstack/db'
function TodoList() {
const { data: todos } = useLiveQuery((query) =>
query
.from({ todos: todoCollection })
.where(({ todos }) => eq(todos.completed, false))
)
return <ul>{todos.map(todo => <li key={todo.id}>{todo.text}</li>)}</ul>
}
クエリビルダー API
const { data } = useLiveQuery((q) =>
q
.from({ t: todoCollection })
.where(({ t }) => eq(t.status, 'active'))
.orderBy(({ t }) => t.createdAt, 'desc')
.limit(10)
)
ジョイン
const { data } = useLiveQuery((q) =>
q
.from({ t: todoCollection })
.innerJoin(
{ u: userCollection },
({ t, u }) => eq(t.userId, u.id)
)
.innerJoin(
{ p: projectCollection },
({ u, p }) => eq(u.projectId, p.id)
)
.where(({ p }) => eq(p.id, currentProject.id))
)
フィルタオペレータ
import { eq, lt, and } from '@tanstack/db'
// 等価比較
eq(field, value)
// より小さい
lt(field, value)
// AND
and(eq(product.category, 'electronics'), lt(product.price, 100))
並べ替えと制限付き
const { data } = useLiveQuery((q) =>
q
.from({ product: productsCollection })
.where(({ product }) =>
and(eq(product.category, 'electronics'), lt(product.price, 100))
)
.orderBy(({ product }) => product.price, 'asc')
.limit(10)
)
楽観的ミューテーション
挿入
todoCollection.insert({
id: uuid(),
text: 'New todo',
completed: false,
})
// 即座: このコレクションを参照するすべてのライブクエリを更新
// バックグラウンド: onInsert ハンドラを呼び出してサーバーと同期
// 失敗時: 自動ロールバック
手動ボイラープレートなし
| Before (TanStack Query のみ) | After (TanStack DB) |
|---|---|
楽観的状態の手動 onMutate | 自動 |
手動 onError ロールバックロジック | 自動 |
| ミューテーションごとのキャッシュ無効化 | すべてのライブクエリが自動更新 |
クエリドリブンシンク (On-Demand)
ライブクエリは自動的に最適化されたネットワークリクエストを生成します:
// このライブクエリ...
useLiveQuery((q) =>
q.from({ product: productsCollection })
.where(({ product }) => and(eq(product.category, 'electronics'), lt(product.price, 100)))
.orderBy(({ product }) => product.price, 'asc')
.limit(10)
)
// ...自動的に生成:
// GET /api/products?category=electronics&price_lt=100&sort=price:asc&limit=10
述語マッピング
queryFn: async (ctx) => {
const { filters, sorts, limit } = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions)
const params = new URLSearchParams()
filters.forEach(({ field, operator, value }) => {
if (operator === 'eq') params.set(field.join('.'), String(value))
else if (operator === 'lt') params.set(`${field.join('.')}_lt`, String(value))
})
if (limit) params.set('limit', String(limit))
return fetch(`/api/products?${params}`).then(r => r.json())
}
パフォーマンス
| 操作 | レイテンシ |
|---|---|
| 単一行の更新 (100k ソート済みコレクション) | ~0.7 ms |
| 後続クエリ (シンク後) | <1 ms |
| コレクション間のジョイン | 1 ミリ秒未満 |
サポートされているコレクションタイプ
- Query Collection - TanStack Query 統合
- Electric Collection - Electric SQL リアルタイムシンク
- TrailBase Collection - TrailBase バックエンド
- RxDB Collection - RxDB 統合
- PowerSync Collection - PowerSync シンク
- LocalStorage Collection - ブラウザ永続化
- LocalOnly Collection - メモリ内のみ
API サマリー
import { createCollection, useLiveQuery } from '@tanstack/react-db'
import { queryCollectionOptions } from '@tanstack/query-db-collection'
import { eq, lt, and, parseLoadSubsetOptions } from '@tanstack/db'
ベストプラクティス
- モジュールレベルでコレクションを定義する - シングルトンです
- 適切なシンクモードを選択する:
eager(<10k)、on-demand(>50k)、progressive(コラボレーティブ) - ビュー固有の API の代わりにジョインを使用する - 正規化されたコレクションを一度ロード
- TanStack Query にフェッチを処理させる - DB は Query を拡張し、置き換えません
parseLoadSubsetOptionsを使用する - ライブクエリ述語を API パラメータにマップ- 自動楽観的更新に頼る - 楽観的状態を手動で管理しない
- スキーマを使用する - ランタイムバリデーションと TypeScript の推論のため
- 増分計算を活用する - 手動
.filter()の代わりにエンジンにフィルタリングを処理させる
一般的な落とし穴
- コンポーネント内でコレクションを作成する (モジュールレベルである必要があります)
- TanStack Query を完全に置き換えようとする (DB はその上に構築されています)
- レンダリング時に手動
.filter()を使用する (ライブクエリのwhere句を使用) - 適切な正規化のために
getKeyを指定しない - サーバーシンクのためのミューテーションハンドラ (
onInsert、onUpdate、onDelete) を忘れる
ライセンス: MIT(寛容ライセンスのため全文を引用しています) · 原本リポジトリ
詳細情報
- 作者
- tanstack-skills
- ライセンス
- MIT
- 最終更新
- 不明
Source: https://github.com/tanstack-skills/tanstack-skills / ライセンス: MIT
関連スキル
doubt-driven-development
重要な判断はすべて、本番環境への展開前に新しい視点から対抗的レビューを実施します。速度より正確性が重要な場合、不慣れなコードを扱う場合、本番環境・セキュリティに関わるロジック・取り消し不可の操作など影響度が高い場合、または後でバグを修正するよりも今検証する方が効率的な場合に活用してください。
apprun-skills
TypeScriptを使用したAppRunアプリケーションのMVU設計に関する総合的なガイダンスが得られます。コンポーネントパターン、イベントハンドリング、状態管理(非同期ジェネレータを含む)、パラメータと保護機能を備えたルーティング・ナビゲーション、vistestを使用したテストに対応しています。AppRunコンポーネントの設計・レビュー、ルートの配線、状態フローの管理、AppRunテストの作成時に活用してください。
desloppify
コードベースのヘルスチェックと技術負債の追跡ツールです。コード品質、技術負債、デッドコード、大規模ファイル、ゴッドクラス、重複関数、コードスメル、命名規則の問題、インポートサイクル、結合度の問題についてユーザーが質問した場合に使用してください。また、ヘルススコアの確認、次の改善項目の提案、クリーンアップ計画の作成をリクエストされた際にも対応します。29言語に対応しています。
debugging-and-error-recovery
テストが失敗したり、ビルドが壊れたり、動作が期待と異なったり、予期しないエラーが発生したりした場合に、体系的な根本原因デバッグをガイドします。推測ではなく、根本原因を見つけて修正するための体系的なアプローチが必要な場合に使用してください。
test-driven-development
テスト駆動開発により実装を進めます。ロジックの実装、バグの修正、動作の変更など、あらゆる場面で活用できます。コードが正常に動作することを証明する必要がある場合、バグ報告を受けた場合、既存機能を修正する予定がある場合に使用してください。
incremental-implementation
変更を段階的に実施します。複数のファイルに影響する機能や変更を実装する場合に使用してください。大量のコードを一度に書こうとしている場合や、タスクが一度では完結できないほど大きい場合に活用します。