mcpField
T
tanstack-db

tanstack-db

github.com/tanstack-skillsデータ・データベース361.6k 回インストール39 週間前に更新
リポジトリ

tanstack-db について

tanstack-dbは、差分データフローを基盤とするベータ版のクライアントサイド組み込みデータベース層、TanStack DBを解説するスキルです。正規化コレクション、リアクティブなライブクエリ、楽観的ミューテーション、TanStack Queryや複数のデータソースとの連携を扱います。

主な機能

  1. 01複数のデータソースに対応する正規化コレクション
  2. 02SQL風ビルダーによるリアクティブなライブクエリ
  3. 03楽観的更新と失敗時の自動ロールバック
  4. 04インクリメンタルなクエリ計算
  5. 05イーガーおよびオンデマンド同期モード

活用シーン

  1. 01クライアントアプリのリアクティブなデータビュー構築
  2. 02ミューテーションの同期中にUIを即時更新
  3. 03正規化コレクションのクエリと結合
  4. 04大規模コレクションのオンデマンド読み込み

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/tanstack-skills/tanstack-skills --skill tanstack-db

インストール後、Claude Code で/tanstack-dbを入力して読み込まれたか確認します。

詳細

概要

TanStack DB は、差分データフローを基盤とするクライアントサイドの組み込みデータベースレイヤーです。正規化されたコレクションを維持し、ライブクエリに増分計算を使用し、自動的な楽観的ミューテーションを提供するとともに、データフェッチでは TanStack Query と連携します。10 万行を超えるデータでも、更新は 1 ミリ秒未満です。

パッケージ: @tanstack/react-db クエリ連携: @tanstack/query-db-collection ステータス: ベータ版(v0.5)

インストール

npm install @tanstack/react-db @tanstack/query-db-collection

主な概念

  • コレクション:データソース(TanStack Query、Electric など)をラップする正規化されたデータストア
  • ライブクエリ:SQL に似たクエリビルダーを使用するリアクティブなサブスクリプション
  • 楽観的ミューテーション:失敗時のロールバックを伴う、自動的で即時の UI 更新
  • 差分データフロー:変更の影響を受けるクエリ結果のみを再計算します

コレクション

コレクションの作成
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 (default): Load entire collection upfront. Best for <10k rows.
const smallCollection = createCollection(
  queryCollectionOptions({ syncMode: 'eager', /* ... */ })
)

// On-Demand: Load only what queries request. Best for >50k rows, search.
const largeCollection = createCollection(
  queryCollectionOptions({
    syncMode: 'on-demand',
    queryFn: async (ctx) => {
      const params = parseLoadSubsetOptions(ctx.meta?.loadSubsetOptions)
      return api.getProducts(params)
    },
  })
)

// Progressive: Load query subset immediately, full sync in background.
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'

// Equality
eq(field, value)

// Less than
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,
})
// Immediately: updates all live queries referencing this collection
// Background: calls onInsert handler to sync with server
// On failure: automatic rollback
手動の定型コードは不要
以前(TanStack Query のみ)以後(TanStack DB)
楽観的な状態のための手動 onMutate自動
手動の onError ロールバックロジック自動
ミューテーションごとのキャッシュ無効化すべてのライブクエリが自動的に更新

クエリ駆動の同期(オンデマンド)

ライブクエリは、最適化されたネットワークリクエストを自動的に生成します。

// This live query...
useLiveQuery((q) =>
  q.from({ product: productsCollection })
    .where(({ product }) => and(eq(product.category, 'electronics'), lt(product.price, 100)))
    .orderBy(({ product }) => product.price, 'asc')
    .limit(10)
)

// ...automatically generates:
// 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())
}

パフォーマンス

操作レイテンシー
単一行の更新(10 万件のソート済みコレクション)約 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'

ベストプラクティス

  1. コレクションはモジュールレベルで定義する - シングルトンです
  2. 適切な同期モードを選ぶ:eager(1 万件未満)、on-demand(5 万件超)、progressive(共同作業向け)
  3. ビュー固有の API ではなく結合を使用する - 正規化されたコレクションを一度だけ読み込みます
  4. フェッチは TanStack Query に任せる - DB は Query を拡張するものであり、置き換えるものではありません
  5. parseLoadSubsetOptions を使用する - ライブクエリの述語を API パラメーターにマッピングします
  6. 自動の楽観的更新を活用する - 楽観的な状態を手動で管理しないでください
  7. 実行時の検証と TypeScript の型推論にはスキーマを使用する
  8. 増分計算を活用する - 手動の .filter() ではなく、フィルタリングをエンジンに任せます

よくある落とし穴

  • コンポーネント内でコレクションを作成する(モジュールレベルで作成する必要があります)
  • TanStack Query を完全に置き換えようとする(DB はその上に構築されています)
  • ライブクエリの where 句の代わりに、レンダー内で手動の .filter() を使用する
  • 適切な正規化のための getKey を指定しない
  • サーバー同期用のミューテーションハンドラー(onInsert、onUpdate、onDelete)を指定し忘れる

コメント