references/powersync-adapter.md
references/powersync-adapter.mdBrowse 8 files
1,642 tokens
6,926 bytes
Token encoding: o200k_base
Snapshot 09776a8
← Back to SKILL.md
PowerSync Adapter Reference
Install
pnpm add @tanstack/powersync-db-collection @powersync/web @journeyapps/wa-sqlite
Required Config
import { createCollection, safeRandomUUID } from '@tanstack/react-db'
import { powerSyncCollectionOptions } from '@tanstack/powersync-db-collection'
import { Schema, Table, column, PowerSyncDatabase } from '@powersync/web'
const APP_SCHEMA = new Schema({
documents: new Table({
name: column.text,
author: column.text,
created_at: column.text,
archived: column.integer,
}),
})
const db = new PowerSyncDatabase({
database: { dbFilename: 'app.sqlite' },
schema: APP_SCHEMA,
})
const documentsCollection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.documents,
}),
)
database--PowerSyncDatabaseinstancetable-- PowerSyncTablefrom schema (providesgetKeyand type inference)
Optional Config (with defaults)
| Option | Default | Description |
|---|---|---|
schema | (none) | StandardSchema for mutation validation |
deserializationSchema | (none) | Transforms SQLite types to output types; required when input types differ from SQLite |
onDeserializationError | (none) | Fatal error handler; required when using schema or deserializationSchema |
serializer | (none) | Per-field functions to serialize output types back to SQLite |
syncBatchSize | 1000 | Batch size for initial sync |
SQLite Type Mapping
| PowerSync Column | TypeScript Type |
|---|---|
column.text | string | null |
column.integer | number | null |
column.real | number | null |
All columns nullable by default. id: string is always included automatically.
Conversions (4 patterns)
1. Type Inference Only (no schema)
const collection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.documents,
}),
)
// Input/Output: { id: string, name: string | null, created_at: string | null, ... }
2. Schema Validation (same SQLite types)
const schema = z.object({
id: z.string(),
name: z.string().min(3),
author: z.string(),
created_at: z.string(),
archived: z.number(),
})
const collection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.documents,
schema,
onDeserializationError: (error) => {
/* fatal */
},
}),
)
3. Transform SQLite to Rich Output Types
const schema = z.object({
id: z.string(),
name: z.string().nullable(),
created_at: z
.string()
.nullable()
.transform((val) => (val ? new Date(val) : null)),
archived: z
.number()
.nullable()
.transform((val) => (val != null ? val > 0 : null)),
})
const collection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.documents,
schema,
onDeserializationError: (error) => {
/* fatal */
},
serializer: { created_at: (value) => (value ? value.toISOString() : null) },
}),
)
// Input: { created_at: string | null, ... }
// Output: { created_at: Date | null, archived: boolean | null, ... }
4. Custom Input + Output with deserializationSchema
const schema = z.object({
id: z.string(),
name: z.string(),
created_at: z.date(),
archived: z.boolean(),
})
const deserializationSchema = z.object({
id: z.string(),
name: z.string(),
created_at: z.string().transform((val) => new Date(val)),
archived: z.number().transform((val) => val > 0),
})
const collection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.documents,
schema,
deserializationSchema,
onDeserializationError: (error) => {
/* fatal */
},
}),
)
// Input: { created_at: Date, archived: boolean }
// Output: { created_at: Date, archived: boolean }
Metadata Tracking
Enable on the table, then pass metadata with operations:
const APP_SCHEMA = new Schema({
documents: new Table({ name: column.text }, { trackMetadata: true }),
})
await collection.insert(
{ id: safeRandomUUID(), name: 'Report' },
{ metadata: { source: 'web-app', userId: 'user-123' } },
).isPersisted.promise
Metadata appears as entry.metadata (stringified JSON) in PowerSync CrudEntry.
Advanced Transactions
import { createTransaction } from '@tanstack/react-db'
import { PowerSyncTransactor } from '@tanstack/powersync-db-collection'
const tx = createTransaction({
autoCommit: false,
mutationFn: async ({ transaction }) => {
await new PowerSyncTransactor({ database: db }).applyTransaction(
transaction,
)
},
})
tx.mutate(() => {
documentsCollection.insert({
id: safeRandomUUID(),
name: 'Doc 1',
created_at: new Date().toISOString(),
})
})
await tx.commit()
await tx.isPersisted.promise
On-Demand Sync Mode
PowerSync supports on-demand sync mode (query-driven sync), where only rows matching active live query predicates are loaded from SQLite into the collection. This can be combined with Sync Streams via onLoad (eager) or onLoadSubset (on-demand) hooks to also control which data the PowerSync Service syncs to the device. Use extractSimpleComparisons or parseWhereExpression to derive Sync Stream parameters dynamically from live query predicates.
Complete Example
import { Schema, Table, column, PowerSyncDatabase } from '@powersync/web'
import { createCollection } from '@tanstack/react-db'
import { powerSyncCollectionOptions } from '@tanstack/powersync-db-collection'
import { z } from 'zod'
const APP_SCHEMA = new Schema({
tasks: new Table({
title: column.text,
due_date: column.text,
completed: column.integer,
}),
})
const db = new PowerSyncDatabase({
database: { dbFilename: 'app.sqlite' },
schema: APP_SCHEMA,
})
const taskSchema = z.object({
id: z.string(),
title: z.string().nullable(),
due_date: z
.string()
.nullable()
.transform((val) => (val ? new Date(val) : null)),
completed: z
.number()
.nullable()
.transform((val) => (val != null ? val > 0 : null)),
})
const tasksCollection = createCollection(
powerSyncCollectionOptions({
database: db,
table: APP_SCHEMA.props.tasks,
schema: taskSchema,
onDeserializationError: (error) => console.error('Fatal:', error),
syncBatchSize: 500,
}),
)