Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 202 additions & 0 deletions docs/collections/pocketbase-collection.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
---
title: PocketBase Collection
---

# PocketBase Collection

PocketBase collections provide seamless integration between TanStack DB and [PocketBase](https://pocketbase.io), enabling real-time data synchronization with PocketBase's open-source backend.

## Overview

[PocketBase](https://pocketbase.io) is an open-source backend consisting of an embedded database (SQLite), real-time subscriptions, built-in auth, file storage, and an admin dashboard.

The `@tanstack/pocketbase-db-collection` package allows you to create collections that:
- Automatically sync data from PocketBase collections
- Support real-time subscriptions for live updates
- Handle optimistic updates with automatic rollback on errors
- Provide built-in mutation handlers for create, update, and delete operations

## Installation

```bash
npm install @tanstack/pocketbase-db-collection @tanstack/react-db pocketbase
```

## Basic Usage

```typescript
import { createCollection } from '@tanstack/react-db'
import { pocketbaseCollectionOptions } from '@tanstack/pocketbase-db-collection'
import PocketBase from 'pocketbase'

const pb = new PocketBase('https://your-pocketbase-instance.com')

// Authenticate if needed
await pb.collection("users").authWithPassword('test@example.com', '1234567890');

const todosCollection = createCollection(
pocketbaseCollectionOptions({
recordService: pb.collection('todos'),
})
)
```

## Configuration Options

The `pocketbaseCollectionOptions` function accepts the following options:

### Required Options

- `recordService`: PocketBase RecordService instance created via `pb.collection(collectionName)`

### Optional Options

- `id`: Unique identifier for the collection
- `schema`: [Standard Schema](https://standardschema.dev) compatible schema (e.g., Zod, Effect) for client-side validation

## Real-time Subscriptions

PocketBase supports real-time subscriptions out of the box. The collection automatically subscribes to changes and updates in real-time:

```typescript
const todosCollection = createCollection(
pocketbaseCollectionOptions({
recordService: pb.collection('todos'),
})
)

// Changes from other clients will automatically update
// the collection in real-time via PocketBase subscriptions
```

The sync implementation subscribes to all records (`*`) and handles `create`, `update`, and `delete` events automatically.

## Mutations

The collection provides built-in mutation handlers that persist changes to PocketBase:

```typescript
// Insert a new record
const tx = todosCollection.insert({
id: 'todo-1',
text: 'Buy milk',
completed: false,
})
await tx.isPersisted.promise

// Update a record
const updateTx = todosCollection.update('todo-1', (draft) => {
draft.completed = true
})
await updateTx.isPersisted.promise

// Delete a record
const deleteTx = todosCollection.delete('todo-1')
await deleteTx.isPersisted.promise
```

## Data Types

PocketBase records must have an `id` field (string) and can include any other fields. The `collectionId` and `collectionName` fields from PocketBase's `RecordModel` are automatically excluded:

```typescript
type Todo = {
id: string // Required - PocketBase uses string IDs
text: string
completed: boolean
// You can add any other fields from your PocketBase collection
}

const todosCollection = createCollection(
pocketbaseCollectionOptions<Todo>({
recordService: pb.collection('todos'),
})
)
```

## Complete Example

```typescript
import { createCollection } from '@tanstack/react-db'
import { pocketbaseCollectionOptions } from '@tanstack/pocketbase-db-collection'
import PocketBase from 'pocketbase'
import { z } from 'zod'

const pb = new PocketBase('https://your-pocketbase-instance.com')

// Authenticate
await pb.collection('users').authWithPassword('user@example.com', 'password')

// Define schema
const todoSchema = z.object({
id: z.string(),
text: z.string(),
completed: z.boolean(),
created: z.string(),
updated: z.string(),
})

type Todo = z.infer<typeof todoSchema>

// Create collection
export const todosCollection = createCollection(
pocketbaseCollectionOptions({
id: 'todos',
recordService: pb.collection('todos'),
schema: todoSchema,
})
)

// Use in component
function TodoList() {
const { data: todos } = useLiveQuery((q) =>
q.from({ todo: todosCollection })
.where(({ todo }) => !todo.completed)
.orderBy(({ todo }) => todo.created, 'desc')
)

const addTodo = (text: string) => {
todosCollection.insert({
id: crypto.randomUUID(),
text,
completed: false,
created: new Date().toISOString(),
updated: new Date().toISOString(),
})
}

return (
<div>
{todos.map((todo) => (
<div key={todo.id}>{todo.text}</div>
))}
</div>
)
}
```

## Error Handling

The collection handles errors gracefully:
- If the initial fetch fails, the subscription is automatically cleaned up
- Mutations that fail will automatically rollback optimistic updates
- The collection is marked as ready even if the initial fetch fails to avoid blocking the application

## Cleanup

The collection automatically unsubscribes from PocketBase when cleaned up:

```typescript
// Cleanup is called automatically when the collection is no longer needed
await todosCollection.cleanup()
```

This ensures no memory leaks and properly closes the real-time subscription connection.

## Learn More

- [PocketBase Documentation](https://pocketbase.io/docs/)
- [PocketBase JavaScript SDK](https://github.com/pocketbase/js-sdk)
- [Optimistic Mutations](../guides/mutations.md)
- [Live Queries](../guides/live-queries.md)

4 changes: 4 additions & 0 deletions docs/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@
"label": "TrailBase Collection",
"to": "collections/trailbase-collection"
},
{
"label": "PocketBase Collection",
"to": "collections/pocketbase-collection"
},
{
"label": "RxDB Collection",
"to": "collections/rxdb-collection"
Expand Down
1 change: 1 addition & 0 deletions docs/guides/collection-options-creator.md
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,7 @@ For complete, production-ready examples, see the collection packages in the TanS

- **[@tanstack/query-collection](https://github.com/TanStack/db/tree/main/packages/query-collection)** - Pattern A: User-provided handlers with full refetch strategy
- **[@tanstack/trailbase-collection](https://github.com/TanStack/db/tree/main/packages/trailbase-collection)** - Pattern B: Built-in handlers with ID-based tracking
- **[@tanstack/pocketbase-collection](https://github.com/TanStack/db/tree/main/packages/pocketbase-db-collection)** - Pattern B: Built-in handlers with real-time subscriptions via PocketBase
- **[@tanstack/electric-collection](https://github.com/TanStack/db/tree/main/packages/electric-collection)** - Pattern A: Transaction ID tracking with complex sync protocols
- **[@tanstack/rxdb-collection](https://github.com/TanStack/db/tree/main/packages/rxdb-collection)** - Pattern B: Built-in handlers that bridge [RxDB](https://rxdb.info) change streams into TanStack DB's sync lifecycle

Expand Down
2 changes: 2 additions & 0 deletions docs/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ TanStack DB provides several built-in collection types for different data source

- **[TrailBaseCollection](../collections/trailbase-collection.md)** &mdash; Sync data into collections using TrailBase's self-hosted backend with real-time subscriptions.

- **[PocketBaseCollection](../collections/pocketbase-collection.md)** &mdash; Sync data into collections using PocketBase's open-source backend with real-time subscriptions, built-in auth, and file storage.

- **[RxDBCollection](../collections/rxdb-collection.md)** &mdash; Integrate with RxDB for offline-first local persistence with powerful replication and sync capabilities.

- **[PowerSyncCollection](../collections/powersync-collection.md)** &mdash; Sync with PowerSync's SQLite-based database for offline-first persistence with real-time synchronization with PostgreSQL, MongoDB, and MySQL backends.
Expand Down
62 changes: 62 additions & 0 deletions packages/pocketbase-db-collection/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"name": "@tanstack/pocketbase-db-collection",
"description": "PocketBase collection for TanStack DB",
"version": "1.0.0",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@tanstack/db": "workspace:*",
"@tanstack/store": "^0.8.0",
"pocketbase": "^0.26.3"
},
"exports": {
".": {
"import": {
"types": "./dist/esm/index.d.ts",
"default": "./dist/esm/index.js"
},
"require": {
"types": "./dist/cjs/index.d.cts",
"default": "./dist/cjs/index.cjs"
}
},
"./package.json": "./package.json"
},
"files": [
"dist",
"src"
],
"main": "dist/cjs/index.cjs",
"module": "dist/esm/index.js",
"packageManager": "pnpm@10.22.0",
"author": "PocketBase",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/TanStack/db.git",
"directory": "packages/pocketbase-db-collection"
},
"homepage": "https://tanstack.com/db",
"keywords": [
"pocketbase",
"nosql",
"realtime",
"local-first",
"sync-engine",
"sync",
"replication",
"opfs",
"indexeddb",
"localstorage",
"optimistic",
"typescript"
],
"scripts": {
"build": "vite build",
"dev": "vite build --watch",
"lint": "eslint . --fix",
"test": "npx vitest --run"
},
"sideEffects": false,
"type": "module",
"types": "dist/esm/index.d.ts"
}
4 changes: 4 additions & 0 deletions packages/pocketbase-db-collection/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export {
pocketbaseCollectionOptions,
type PocketBaseCollectionConfig,
} from "./pocketbase"
Loading