Node Adapter
Works with Node.js, Bun and Deno.
Example
Section titled “Example”import { const makeAdapter: ({ sync, ...options }: NodeAdapterOptions & { sync?: SyncOptions;}) => Adapter
Runs everything in the same thread. Use makeWorkerAdapter for multi-threaded implementation.
makeAdapter } from '@livestore/adapter-node'import { const makeWsSync: (options: WsSyncOptions) => SyncBackendConstructor<SyncMetadata>
Creates a sync backend that uses WebSocket to communicate with the sync backend.
makeWsSync } from '@livestore/sync-cf/client'const const adapter: Adapter
adapter = function makeAdapter({ sync, ...options }: NodeAdapterOptions & { sync?: SyncOptions;}): Adapter
Runs everything in the same thread. Use makeWorkerAdapter for multi-threaded implementation.
makeAdapter({ NodeAdapterOptions.storage: { readonly type: "in-memory"; readonly importSnapshot?: Uint8Array<ArrayBuffer> | undefined;} | { readonly type: "fs"; readonly baseDirectory?: string | undefined;}
storage: { type: "fs"
type: 'fs' }, // or in-memory: // storage: { type: 'in-memory' }, sync?: SyncOptions
sync: { backend?: SyncBackendConstructor<any, JsonValue>
backend: function makeWsSync(options: WsSyncOptions): SyncBackendConstructor<SyncMetadata>
Creates a sync backend that uses WebSocket to communicate with the sync backend.
makeWsSync({ WsSyncOptions.url: string
URL of the sync backend
The protocol can either http/https or ws/wss
url: 'ws://localhost:8787' }) }, // To enable devtools: // devtools: { schemaPath: new URL('./schema.ts', import.meta.url) },})
Resetting local persistence
Section titled “Resetting local persistence”During development you can instruct the adapter to wipe the locally persisted state and eventlog databases on startup:
import { const makeAdapter: ({ sync, ...options }: NodeAdapterOptions & { sync?: SyncOptions;}) => Adapter
Runs everything in the same thread. Use makeWorkerAdapter for multi-threaded implementation.
makeAdapter } from '@livestore/adapter-node'
const const adapter: Adapter
adapter = function makeAdapter({ sync, ...options }: NodeAdapterOptions & { sync?: SyncOptions;}): Adapter
Runs everything in the same thread. Use makeWorkerAdapter for multi-threaded implementation.
makeAdapter({ NodeAdapterOptions.storage: { readonly type: "in-memory"; readonly importSnapshot?: Uint8Array<ArrayBuffer> | undefined;} | { readonly type: "fs"; readonly baseDirectory?: string | undefined;}
storage: { type: "fs"
type: 'fs' }, NodeAdapterOptions.resetPersistence?: boolean
Warning: This will reset both the app and eventlog database. This should only be used during development.
resetPersistence,})
Worker adapter
Section titled “Worker adapter”The worker adapter can be used for more advanced scenarios where it’s preferable to reduce the load of the main thread and run persistence/syncing in a worker thread.
import { const makeWorkerAdapter: ({ workerUrl, workerExtraArgs, ...options }: NodeAdapterOptions & { workerUrl: URL; workerExtraArgs?: JsonValue;}) => Adapter
Runs persistence and syncing in a worker thread.
makeWorkerAdapter } from '@livestore/adapter-node'
NodeAdapterOptions.storage: { readonly type: "in-memory"; readonly importSnapshot?: Uint8Array<ArrayBuffer> | undefined;} | { readonly type: "fs"; readonly baseDirectory?: string | undefined;}
storage: { type: "fs"
type: 'fs' }, workerUrl: URL
Example: new URL('./livestore.worker.ts', import.meta.url)
workerUrl: new var URL: new (url: string | URL, base?: string | URL) => URL
The URL interface is used to parse, construct, normalize, and encode URL.
URL('./livestore.worker.js', import.
The type of import.meta.
If you need to declare that a given property exists on import.meta,
this type may be augmented via interface merging.
meta.ImportMeta.url: string
file:// url string for the current module.
The absolute file: URL of the module.
This is defined exactly the same as it is in browsers providing the URL of the
current module file.
This enables useful patterns such as relative file loading:
import { readFileSync } from 'node:fs';const buffer = readFileSync(new URL('./data.proto', import.meta.url));
url),})
import { const makeWorker: (options: WorkerOptions) => void
makeWorker } from '@livestore/adapter-node/worker'import { const makeWsSync: (options: WsSyncOptions) => SyncBackendConstructor<SyncMetadata>
Creates a sync backend that uses WebSocket to communicate with the sync backend.
makeWsSync } from '@livestore/sync-cf/client'
import { const schema: FromInputSchema.DeriveSchema<{ events: { readonly todoCreated: EventDef<"v1.TodoCreated", { readonly id: string; readonly text: string; }, { readonly id: string; readonly text: string; }, false>; }; state: InternalState;}>
schema } from './schema.ts'
function makeWorker(options: WorkerOptions): void
makeWorker({ schema: LiveStoreSchema<DbSchema, EventDefRecord>
schema, sync?: SyncOptions
sync: { backend?: SyncBackendConstructor<any, JsonValue>
backend: function makeWsSync(options: WsSyncOptions): SyncBackendConstructor<SyncMetadata>
Creates a sync backend that uses WebSocket to communicate with the sync backend.
makeWsSync({ WsSyncOptions.url: string
URL of the sync backend
The protocol can either http/https or ws/wss
url: 'ws://localhost:8787' }) },})import { const defineMaterializer: <TEventDef extends State.SQLite.EventDef.AnyWithoutFn>(_eventDef: TEventDef, materializer: State.SQLite.Materializer<TEventDef>) => State.SQLite.Materializer<TEventDef>
defineMaterializer, import Events
Events, const makeSchema: <TInputSchema extends InputSchema>(inputSchema: TInputSchema) => FromInputSchema.DeriveSchema<TInputSchema>
makeSchema, import Schema
Schema, import State
State } from '@livestore/livestore'
const const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>;}
tables = { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos: import State
State.import SQLite
SQLite.function table<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}, Partial<...>>(args: { ...;} & Partial<...>): State.SQLite.TableDef<...> (+2 overloads)
Creates a SQLite table definition from columns or an Effect Schema.
This function supports two main ways to define a table:
- Using explicit column definitions
- Using an Effect Schema (either the
name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columnsconst usersTable = State.SQLite.table({ name: 'users', columns: { id: State.SQLite.text({ primaryKey: true }), name: State.SQLite.text({ nullable: false }), email: State.SQLite.text({ nullable: false }), age: State.SQLite.integer({ nullable: true }), },})
// Using Effect Schema with annotationsimport { Schema } from '@livestore/utils/effect'
const UserSchema = Schema.Struct({ id: Schema.Int.pipe(State.SQLite.withPrimaryKey).pipe(State.SQLite.withAutoIncrement), email: Schema.String.pipe(State.SQLite.withUnique), name: Schema.String, active: Schema.Boolean.pipe(State.SQLite.withDefault(true)), createdAt: Schema.optional(Schema.Date),})
// Option 1: With explicit nameconst usersTable = State.SQLite.table({ name: 'users', schema: UserSchema,})
// Option 2: With name from schema annotation (title or identifier)const AnnotatedUserSchema = UserSchema.annotations({ title: 'users' })const usersTable2 = State.SQLite.table({ schema: AnnotatedUserSchema,})
// Adding indexesconst PostSchema = Schema.Struct({ id: Schema.String.pipe(State.SQLite.withPrimaryKey), title: Schema.String, authorId: Schema.String, createdAt: Schema.Date,}).annotations({ identifier: 'posts' })
const postsTable = State.SQLite.table({ schema: PostSchema, indexes: [ { name: 'idx_posts_author', columns: ['authorId'] }, { name: 'idx_posts_created', columns: ['createdAt'], isUnique: false }, ],})
table({ name: "todos"
name: 'todos', columns: { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}
columns: { id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;}
id: import State
State.import SQLite
SQLite.const text: <string, string, false, typeof NoDefault, true, false>(args: { schema?: Schema.Schema<string, string, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;} (+1 overload)
text({ primaryKey?: true
primaryKey: true }), text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;}
text: import State
State.import SQLite
SQLite.const text: () => { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false;}
completed: import State
State.import SQLite
SQLite.const boolean: <boolean, false, false, false, false>(args: { default?: false; nullable?: false; primaryKey?: false; autoIncrement?: false;}) => { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
boolean({ default?: false
default: false }), }, }),} as type const = { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>;}
const
const const events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
events = { todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>
todoCreated: import Events
Events.synced<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}>(args: { name: "v1.TodoCreated"; schema: Schema.Schema<{ readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, never>;} & Omit<State.SQLite.DefineEventOptions<{ readonly text: string; readonly id: string;}, false>, "derived" | "clientOnly">): State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>export synced
synced({ name: "v1.TodoCreated"
name: 'v1.TodoCreated', schema: Schema.Schema<{ readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, never>
schema: import Schema
Schema.function Struct<{ id: typeof Schema.String; text: typeof Schema.String;}>(fields: { id: typeof Schema.String; text: typeof Schema.String;}): Schema.Struct<{ id: typeof Schema.String; text: typeof Schema.String;}> (+1 overload)
Struct({ id: typeof Schema.String
id: import Schema
Schema.class Stringexport String
String, text: typeof Schema.String
text: import Schema
Schema.class Stringexport String
String }), }),} as type const = { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
const
const const materializers: { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>;}
materializers = import State
State.import SQLite
SQLite.const materializers: <{ readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}>(_eventDefRecord: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}, handlers: { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>;}) => { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>;}
materializers(const events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
events, { [const events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
events.todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>
todoCreated.name: "v1.TodoCreated"
name]: defineMaterializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>>(_eventDef: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>, materializer: State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>>): State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>>
defineMaterializer(const events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
events.todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string;}, { readonly text: string; readonly id: string;}, false>
todoCreated, ({ id: string
id, text: string
text }) => const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>
todos.insert: (values: { readonly text: string; readonly id: string; readonly completed?: boolean;}) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; ... 4 more ...; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Insert a new row into the table
Example:
db.todos.insert({ id: '123', text: 'Buy milk', status: 'active' })
insert({ id: string
id, text: string
text, completed?: boolean
completed: false }), ),})
const const state: InternalState
state = import State
State.import SQLite
SQLite.const makeState: <{ tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; }; materializers: { ...; };}>(inputSchema: { tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>; }; materializers: { ...; };}) => InternalState
makeState({ tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema.Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema.Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema.Schema<...>>;}
tables, materializers: { "v1.TodoCreated": State.SQLite.Materializer<State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>>;}
materializers })
export const const schema: FromInputSchema.DeriveSchema<{ events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; }; state: InternalState;}>
schema = makeSchema<{ events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; }; state: InternalState;}>(inputSchema: { events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; }; state: InternalState;}): FromInputSchema.DeriveSchema<{ events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>; }; state: InternalState;}>
makeSchema({ events: { readonly todoCreated: State.SQLite.EventDef<"v1.TodoCreated", { readonly text: string; readonly id: string; }, { readonly text: string; readonly id: string; }, false>;}
events, state: InternalState
state })