Skip to content
Back to skills

Db Patterns

ASecurity

TanStack DB patterns for client-first reactive data stores. Activates when implementing offline-first apps, local-first data, or reactive client databases. NOTE: Beta library - API may change.

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 28, 2026
ai-agentstypescriptreactapidatabasebackend

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 28, 2026

npx -y skills add smicolon/ai-kit --skill db-patterns --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Db Patterns?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Db Patterns
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/smicolon-db-patterns/badge)](https://www.skillsdirectory.com/skills/smicolon-db-patterns)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

SKILL.md
---
name: TanStack DB Patterns (Beta)
description: >-
  TanStack DB patterns for client-first reactive data stores. Activates when
  implementing offline-first apps, local-first data, or reactive client databases.
  NOTE: Beta library - API may change.
version: 1.0.0
---

# TanStack DB Patterns (Beta)

> **Beta Library**: TanStack DB is in beta. APIs may change between versions.

TanStack DB provides a client-first reactive data store with optional sync to remote sources.

## Core Concepts

- **Collections**: Named groups of documents (like tables)
- **Documents**: Individual records with unique IDs
- **Queries**: Reactive queries that update when data changes
- **Transactions**: Atomic operations across multiple documents
- **Sync**: Optional sync to remote backends

## Basic Setup

```typescript
// lib/db.ts
import { createDB, createCollection } from '@tanstack/db'

// Define document types
interface Post {
  id: string
  title: string
  content: string
  authorId: string
  published: boolean
  createdAt: number
  updatedAt: number
}

interface User {
  id: string
  name: string
  email: string
}

// Create database
export const db = createDB({
  collections: {
    posts: createCollection<Post>(),
    users: createCollection<User>(),
  },
})
```

## CRUD Operations

### Create
```typescript
import { db } from '@/lib/db'

// Insert a single document
const newPost = await db.posts.insert({
  id: crypto.randomUUID(),
  title: 'My Post',
  content: 'Post content...',
  authorId: 'user-1',
  published: false,
  createdAt: Date.now(),
  updatedAt: Date.now(),
})

// Insert multiple documents
await db.posts.insertMany([
  { id: '1', title: 'Post 1', ... },
  { id: '2', title: 'Post 2', ... },
])
```

### Read
```typescript
// Get by ID
const post = await db.posts.get('post-id')

// Query with filters
const publishedPosts = await db.posts.findMany({
  where: { published: true },
  orderBy: { createdAt: 'desc' },
  limit: 10,
})

// Query with complex filters
const userPosts = await db.posts.findMany({
  where: {
    authorId: 'user-1',
    published: true,
  },
})
```

### Update
```typescript
// Update by ID
await db.posts.update('post-id', {
  title: 'Updated Title',
  updatedAt: Date.now(),
})

// Update with function
await db.posts.update('post-id', (post) => ({
  ...post,
  viewCount: post.viewCount + 1,
  updatedAt: Date.now(),
}))

// Update many
await db.posts.updateMany(
  { where: { authorId: 'user-1' } },
  { published: false }
)
```

### Delete
```typescript
// Delete by ID
await db.posts.delete('post-id')

// Delete many
await db.posts.deleteMany({
  where: { published: false },
})
```

## Reactive Queries in React

```typescript
import { useQuery } from '@tanstack/db-react'
import { db } from '@/lib/db'

function PostList() {
  // Reactive query - updates when data changes
  const posts = useQuery(
    db.posts.query({
      where: { published: true },
      orderBy: { createdAt: 'desc' },
    })
  )

  return (
    <ul>
      {posts.map((post) => (
        <PostCard key={post.id} post={post} />
      ))}
    </ul>
  )
}

function PostDetail({ postId }: { postId: string }) {
  // Single document query
  const post = useQuery(db.posts.get(postId))

  if (!post) return <NotFound />

  return <article>{post.title}</article>
}
```

## Transactions

```typescript
import { db } from '@/lib/db'

async function transferPost(postId: string, newAuthorId: string) {
  await db.transaction(async (tx) => {
    // Get current post
    const post = await tx.posts.get(postId)
    if (!post) throw new Error('Post not found')

    // Update post author
    await tx.posts.update(postId, {
      authorId: newAuthorId,
      updatedAt: Date.now(),
    })

    // Update user post counts (if tracking)
    await tx.users.update(post.authorId, (user) => ({
      ...user,
      postCount: user.postCount - 1,
    }))

    await tx.users.update(newAuthorId, (user) => ({
      ...user,
      postCount: user.postCount + 1,
    }))
  })
}
```

## Sync with Remote Backend

```typescript
// lib/db.ts
import { createDB, createCollection, createSyncProvider } from '@tanstack/db'

const syncProvider = createSyncProvider({
  // Pull changes from server
  pull: async (collection, lastSync) => {
    const response = await fetch(`/api/${collection}/sync?since=${lastSync}`)
    return response.json()
  },

  // Push changes to server
  push: async (collection, changes) => {
    await fetch(`/api/${collection}/sync`, {
      method: 'POST',
      body: JSON.stringify(changes),
    })
  },
})

export const db = createDB({
  collections: {
    posts: createCollection<Post>(),
    users: createCollection<User>(),
  },
  sync: syncProvider,
})

// Trigger sync
await db.sync()

// Auto-sync on interval
setInterval(() => db.sync(), 30000)
```

## Offline-First Pattern

```typescript
import { useQuery, useMutation } from '@tanstack/db-react'
import { db } from '@/lib/db'

function CreatePostForm() {
  const createPost = useMutation(db.posts.insert)

  const handleSubmit = async (data: PostInput) => {
    // Immediately available locally
    await createPost.mutate({
      id: crypto.randomUUID(),
      ...data,
      createdAt: Date.now(),
      updatedAt: Date.now(),
      _pending: true, // Mark as pending sync
    })

    // Sync will happen in background when online
    if (navigator.onLine) {
      db.sync()
    }
  }

  return <form onSubmit={handleSubmit}>{/* form fields */}</form>
}

// Show pending items differently
function PostCard({ post }: { post: Post }) {
  return (
    <div className={post._pending ? 'opacity-50' : ''}>
      {post.title}
      {post._pending && <span>Syncing...</span>}
    </div>
  )
}
```

## Integration with TanStack Query

```typescript
// Hybrid approach: DB for offline, Query for server sync
import { useQuery as useReactQuery } from '@tanstack/react-query'
import { useQuery as useDBQuery } from '@tanstack/db-react'
import { db } from '@/lib/db'

function PostList() {
  // Local DB for immediate data
  const localPosts = useDBQuery(
    db.posts.query({ where: { published: true } })
  )

  // Server query for sync
  const { data: serverPosts } = useReactQuery({
    queryKey: ['posts', 'published'],
    queryFn: () => postApi.getPosts({ published: true }),
    onSuccess: (posts) => {
      // Update local DB with server data
      db.posts.upsertMany(posts)
    },
  })

  // Use local data (always available, even offline)
  return (
    <ul>
      {localPosts.map((post) => (
        <PostCard key={post.id} post={post} />
      ))}
    </ul>
  )
}
```

## When to Use TanStack DB

| Scenario | Solution |
|----------|----------|
| Standard server data | TanStack Query |
| Offline-first app | TanStack DB |
| Local-first with sync | TanStack DB + sync |
| Real-time collaboration | TanStack DB + WebSocket sync |
| Complex client state | TanStack DB or Store |

## Conventions

1. **Type your collections** - Always define document interfaces
2. **Use transactions** - For multi-document operations
3. **Handle offline** - Design for offline-first
4. **Sync strategy** - Define clear sync patterns
5. **Combine with Query** - Use Query for pure server data

## Anti-Patterns

```typescript
// ❌ WRONG: Not using transactions for related updates
await db.posts.delete(postId)
await db.comments.deleteMany({ where: { postId } }) // Could fail leaving orphans

// ✅ CORRECT: Use transaction
await db.transaction(async (tx) => {
  await tx.posts.delete(postId)
  await tx.comments.deleteMany({ where: { postId } })
})

// ❌ WRONG: Using DB for pure server data without offline need
const posts = useDBQuery(db.posts.query({}))

// ✅ CORRECT: Use Query for server data
const { data: posts } = useQuery(postsQueryOptions())
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.