Reactivity System
LiveStore has a high-performance, fine-grained reactivity system built in which is similar to Signals (e.g. in SolidJS).
Defining reactive state
Section titled “Defining reactive state”LiveStore provides 3 types of reactive state:
- Reactive SQL queries on top of SQLite state (
queryDb()) - Reactive state values (
signal()) - Reactive computed values (
computed())
Reactive state variables end on a $ by convention (e.g. todos$). The label option is optional but can be used to identify the reactive state variable in the devtools.
Reactive SQL queries
Section titled “Reactive SQL queries”import { const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb, import State
State, const signal: <T>(defaultValue: T, options?: { label?: string;}) => SignalDef<T>
signal } from '@livestore/livestore'
todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos: import State
State.import SQLite
SQLite.function table<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { columnType: "text"; ... 4 more ...; 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<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; };}
columns: { id: { columnType: "text"; 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<string, string, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; 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<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<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), completed: { columnType: "integer"; 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<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
boolean({ default?: false
default: false }), createdAt: { columnType: "text"; schema: Schema<Date, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;}
createdAt: import State
State.import SQLite
SQLite.const datetime: () => { columnType: "text"; schema: Schema<Date, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
datetime(), }, }),} as type const = { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
const
const const uiState$: SignalDef<{ showCompleted: boolean;}>
uiState$ = signal<{ showCompleted: boolean;}>(defaultValue: { showCompleted: boolean;}, options?: { label?: string;}): SignalDef<{ showCompleted: boolean;}>
signal({ showCompleted: boolean
showCompleted: false }, { label?: string
label: 'uiState$' })
const const todos$: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], "def">
todos$ = queryDb<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[]>(queryInput: QueryInputRaw<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], readonly any[]> | QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], any, any>, options?: { map?: (rows: readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }[]) => readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date; }[]; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb(const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos.orderBy: <"createdAt">(col: "createdAt", direction: "asc" | "desc") => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; ... 4 more ...; autoIncrement: false; }; readonly createdAt: { ...; };}>, State.SQLite.WithDefaults<...>>, "onConflict" | "returning"> (+1 overload)
orderBy('createdAt', 'desc'), { label?: string
Used for debugging / devtools
label: 'todos$' })
{ const const todos$: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], "def">
todos$ = queryDb<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[]>(queryInput: ((get: GetAtomResult) => QueryInputRaw<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], readonly any[]>) | ((get: GetAtomResult) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean; readonly createdAt: Date;}[], any, any>), options?: { ...;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb( (get: GetAtomResult
get) => { const { const showCompleted: boolean
showCompleted } = get: <{ showCompleted: boolean;}>(atom: SignalDef<{ showCompleted: boolean;}> | Atom<{ showCompleted: boolean;}, any, RefreshReason> | LiveQueryDef<{ showCompleted: boolean;}, "def"> | LiveQuery<{ showCompleted: boolean;}> | ISignal<{ showCompleted: boolean;}>, otelContext?: Context | undefined, debugRefreshReason?: RefreshReason | undefined) => { showCompleted: boolean;}
get(const uiState$: SignalDef<{ showCompleted: boolean;}>
uiState$) return const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly createdAt: { ...; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos.where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined; readonly createdAt: Date | ... 2 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where(const showCompleted: boolean
showCompleted ? { completed?: boolean | { op: QueryBuilder.WhereOps.SingleValue; value: boolean;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly boolean[];} | undefined
completed: true } : {}) }, { label?: string
Used for debugging / devtools
label: 'todos$' }, )}
Signals
Section titled “Signals”Signals are reactive state values that can be set and get. This can be useful for state that is not materialized from events into SQLite tables.
import { type class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store, const signal: <T>(defaultValue: T, options?: { label?: string;}) => SignalDef<T>
signal } from '@livestore/livestore'
declare const const store: Store<LiveStoreSchema.Any, {}>
store: class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
const const now$: SignalDef<number>
now$ = signal<number>(defaultValue: number, options?: { label?: string;}): SignalDef<number>
signal(var Date: DateConstructor
Enables basic storage and retrieval of dates and times.
Date.DateConstructor.now(): number
Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).
now(), { label?: string
label: 'now$' })
function setInterval<[]>(callback: () => void, delay?: number): NodeJS.Timeout (+5 overloads)
Schedules repeated execution of callback every delay milliseconds.
When delay is larger than 2147483647 or less than 1 or NaN, the delay
will be set to 1. Non-integer delays are truncated to an integer.
If callback is not a function, a TypeError will be thrown.
This method has a custom variant for promises that is available using
timersPromises.setInterval().
setInterval(() => { const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.setSignal: <number>(signalDef: SignalDef<number>, value: number | ((prev: number) => number)) => void
Set the value of a signal
setSignal(const now$: SignalDef<number>
now$, var Date: DateConstructor
Enables basic storage and retrieval of dates and times.
Date.DateConstructor.now(): number
Returns the number of milliseconds elapsed since midnight, January 1, 1970 Universal Coordinated Time (UTC).
now())}, 1000)
const const num$: SignalDef<number>
num$ = signal<number>(defaultValue: number, options?: { label?: string;}): SignalDef<number>
signal(0, { label?: string
label: 'num$' })const const increment: () => void
increment = () => const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.setSignal: <number>(signalDef: SignalDef<number>, value: number | ((prev: number) => number)) => void
Set the value of a signal
setSignal(const num$: SignalDef<number>
num$, (prev: number
prev) => prev: number
prev + 1)
const increment: () => void
increment()const increment: () => void
increment()
var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
- A global
console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s', 'world');// Prints: hello world, to stdoutconsole.error(new Error('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();const err = getStreamSomehow();const myConsole = new console.Console(out, err);
myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s', 'world');// Prints: hello world, to outmyConsole.error(new Error('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+3 overloads)
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const count = 5;console.log('count: %d', count);// Prints: count: 5, to stdoutconsole.log('count:', count);// Prints: count: 5, to stdout
See util.format() for more information.
log(const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.query: <number>(query: Queryable<number> | { query: string; bindValues: Bindable; schema?: Schema<number, number, never>;}, options?: { otelContext?: Context; debugRefreshReason?: RefreshReason;}) => number
Synchronously queries the database without creating a LiveQuery.
This is useful for queries that don't need to be reactive.
Example: Query builder
const completedTodos = store.query(tables.todo.where({ complete: true }))
Example: Raw SQL query
const completedTodos = store.query({ query: 'SELECT * FROM todo WHERE complete = 1', bindValues: {} })
query(const num$: SignalDef<number>
num$))Computed values
Section titled “Computed values”import { const computed: <TResult>(fn: (get: GetAtomResult) => TResult, options?: { label?: string; deps?: DepKey;}) => LiveQueryDef<TResult>
computed, const signal: <T>(defaultValue: T, options?: { label?: string;}) => SignalDef<T>
signal } from '@livestore/livestore'
const const duplicated$: LiveQueryDef<number, "def">
duplicated$ = computed<number>(fn: (get: GetAtomResult) => number, options?: { label?: string; deps?: DepKey;}): LiveQueryDef<number, "def">
computed((get: GetAtomResult
get) => get: <number>(atom: SignalDef<number> | Atom<number, any, RefreshReason> | LiveQueryDef<number, "def"> | LiveQuery<number> | ISignal<number>, otelContext?: Context | undefined, debugRefreshReason?: RefreshReason | undefined) => number
get(const num$: SignalDef<number>
num$) * 2, { label?: string
label: 'duplicated$' })
Accessing reactive state
Section titled “Accessing reactive state”Reactive state is always bound to a Store instance. You can access the current value of reactive state the following ways:
Using the Store instance
Section titled “Using the Store instance”import { const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb, import State
State, type class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store } from '@livestore/livestore'
declare const const store: Store<LiveStoreSchema.Any, {}>
store: class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store
const const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables = { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos: import State
State.import SQLite
SQLite.function table<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}, Partial<{ indexes: Index[];}>>(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<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}
columns: { id: { columnType: "text"; 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<string, string, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;} (+1 overload)
text({ primaryKey?: true
primaryKey: true }), title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;}
title: import State
State.import SQLite
SQLite.const text: () => { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), completed: { columnType: "integer"; 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<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<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
const
const const count$: LiveQueryDef<number, "def">
count$ = queryDb<number, number>(queryInput: QueryInputRaw<number, readonly any[]> | QueryBuilder<number, any, any>, options?: { map?: (rows: number) => number; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<number, "def"> (+1 overload)
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb(const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos.count: () => QueryBuilder<number, State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly title: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 7 more ... | "row">
Example:
db.todos.count()db.todos.count().where('completed', true)
count(), { label?: string
Used for debugging / devtools
label: 'count$' })const const count: number
count = const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.query: <number>(query: Queryable<number> | { query: string; bindValues: Bindable; schema?: Schema<number, number, never>;}, options?: { otelContext?: Context; debugRefreshReason?: RefreshReason;}) => number
Synchronously queries the database without creating a LiveQuery.
This is useful for queries that don't need to be reactive.
Example: Query builder
const completedTodos = store.query(tables.todo.where({ complete: true }))
Example: Raw SQL query
const completedTodos = store.query({ query: 'SELECT * FROM todo WHERE complete = 1', bindValues: {} })
query(const count$: LiveQueryDef<number, "def">
count$)var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
- A global
console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s', 'world');// Prints: hello world, to stdoutconsole.error(new Error('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();const err = getStreamSomehow();const myConsole = new console.Console(out, err);
myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s', 'world');// Prints: hello world, to outmyConsole.error(new Error('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+3 overloads)
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const count = 5;console.log('count: %d', count);// Prints: count: 5, to stdoutconsole.log('count:', count);// Prints: count: 5, to stdout
See util.format() for more information.
log(const count: number
count)
const const unsubscribe: Unsubscribe
unsubscribe = const store: Store<LiveStoreSchema.Any, {}>
store.Store<LiveStoreSchema<TDbSchema extends DbSchema = DbSchema, TEventsDefRecord extends EventDefRecord = EventDefRecord>.Any, {}>.subscribe: <number>(query: Queryable<number>, onUpdate: (value: number) => void, options?: SubscribeOptions<number> | undefined) => Unsubscribe (+1 overload)
subscribe(const count$: LiveQueryDef<number, "def">
count$, (value: number
value) => { var console: Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
- A
Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
- A global
console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s', 'world');// Prints: hello world, to stdoutconsole.error(new Error('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3
const name = 'Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
const out = getStreamSomehow();const err = getStreamSomehow();const myConsole = new console.Console(out, err);
myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s', 'world');// Prints: hello world, to outmyConsole.error(new Error('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to err
const name = 'Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to err
console.Console.log(message?: any, ...optionalParams: any[]): void (+3 overloads)
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
const count = 5;console.log('count: %d', count);// Prints: count: 5, to stdoutconsole.log('count:', count);// Prints: count: 5, to stdout
See util.format() for more information.
log(value: number
value)})
const unsubscribe: () => void
unsubscribe()Via framework integrations
Section titled “Via framework integrations”import type { interface LiveQueryDef<TResult, TTag extends string = "def">
LiveQueryDef } from '@livestore/livestore'import { const useQuery: <TQueryable extends Queryable<any>>(queryable: TQueryable, options?: { store?: Store;}) => Queryable.Result<TQueryable>
Returns the result of a query and subscribes to future updates.
Example:
const App = () => { const todos = useQuery(queryDb(tables.todos.query.where({ complete: true }))) return <div>{todos.map((todo) => <div key={todo.id}>{todo.title}</div>)}</div>}
useQuery } from '@livestore/react'import type { type FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC } from 'react'
declare const const state$: LiveQueryDef<number, "def">
state$: interface LiveQueryDef<TResult, TTag extends string = "def">
LiveQueryDef<number>
export const const MyComponent: FC
MyComponent: type FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC = () => { const const value: number
value = useQuery<LiveQueryDef<number, "def">>(queryable: LiveQueryDef<number, "def">, options?: { store?: Store;}): number
Returns the result of a query and subscribes to future updates.
Example:
const App = () => { const todos = useQuery(queryDb(tables.todos.query.where({ complete: true }))) return <div>{todos.map((todo) => <div key={todo.id}>{todo.title}</div>)}</div>}
useQuery(const state$: LiveQueryDef<number, "def">
state$)
return <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>{const value: number
value}</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>}import type { interface LiveQueryDef<TResult, TTag extends string = "def">
LiveQueryDef } from '@livestore/livestore'import { const query: <TResult>(queryDef: Queryable<TResult>, initialValue: TResult) => Accessor<TResult>
query } from '@livestore/solid'
declare const const state$: LiveQueryDef<number, "def">
state$: interface LiveQueryDef<TResult, TTag extends string = "def">
LiveQueryDef<number>
export const const MyComponent: () => JSX.Element
MyComponent = () => { const const value: Accessor<number>
value = query<number>(queryDef: Queryable<number>, initialValue: number): Accessor<number>
query(const state$: LiveQueryDef<number, "def">
state$, 0)
return <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>{const value: () => number
value()}</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>}Reacting to changing variables passed to queries
Section titled “Reacting to changing variables passed to queries”If your query depends on a variable passed in by the component, use the deps array to react to changes in this variable.
import { const queryDb: { <TResultSchema, TResult = TResultSchema>(queryInput: QueryInputRaw<TResultSchema, ReadonlyArray<any>> | QueryBuilder<TResultSchema, any, any>, options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>; <TResultSchema, TResult = TResultSchema>(queryInput: ((get: GetAtomResult) => QueryInputRaw<TResultSchema, ReadonlyArray<any>>) | ((get: GetAtomResult) => QueryBuilder<TResultSchema, any, any>), options?: { map?: (rows: TResultSchema) => TResult; label?: string; deps?: DepKey; }): LiveQueryDef<TResult>;}
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb, import State
State, type class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store } from '@livestore/livestore'import type { type ReactApi = { useQuery: typeof useQuery; useClientDocument: typeof useClientDocument;}
ReactApi } from '@livestore/react'import type { type FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC } from 'react'
const const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables = { todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos: import State
State.import SQLite
SQLite.function table<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}, Partial<{ indexes: Index[];}>>(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<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}
columns: { id: { columnType: "text"; 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<string, string, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; 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<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<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), completed: { columnType: "integer"; 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<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<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
const
declare const const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store: class Store<TSchema extends LiveStoreSchema = LiveStoreSchema.Any, TContext = {}>
Store & type ReactApi = { useQuery: typeof useQuery; useClientDocument: typeof useClientDocument;}
ReactApi
export const const todos$: ({ showCompleted }: { showCompleted: boolean;}) => LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], "def">
todos$ = ({ showCompleted: boolean
showCompleted }: { showCompleted: boolean
showCompleted: boolean }) => queryDb<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[]>(queryInput: ((get: GetAtomResult) => QueryInputRaw<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], readonly any[]>) | ((get: GetAtomResult) => QueryBuilder<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], any, any>), options?: { map?: (rows: readonly { readonly id: string; readonly text: string; readonly completed: boolean; }[]) => readonly { readonly id: string; readonly text: string; readonly completed: boolean; }[]; label?: string; deps?: DepKey;} | undefined): LiveQueryDef<...> (+1 overload)
NOTE queryDb is only supposed to read data. Don't use it to insert/update/delete data but use events instead.
When using contextual data when constructing the query, please make sure to include it in the deps option.
queryDb( () => { return const tables: { readonly todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; }; }>, State.SQLite.WithDefaults<...>, Schema<...>>;}
tables.todos: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"todos", { readonly id: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly text: { columnType: "text"; schema: Schema<string, string, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly completed: { columnType: "integer"; schema: Schema<boolean, number, never>; default: Some<false>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema<...>>
todos.where: (params: Partial<{ readonly id: string | { op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly text: string | { op: QueryBuilder.WhereOps.SingleValue; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly completed: boolean | ... 2 more ... | undefined;}>) => QueryBuilder<...> (+2 overloads)
where(showCompleted: boolean
showCompleted ? { completed?: boolean | { op: QueryBuilder.WhereOps.SingleValue; value: boolean;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly boolean[];} | undefined
completed: true } : {}) }, { label?: string
Used for debugging / devtools
label: 'todos$', deps?: DepKey
deps: [showCompleted: boolean
showCompleted ? 'true' : 'false'], }, )
export const const MyComponent: FC<{ showCompleted: boolean;}>
MyComponent: type FC<P = {}> = React.FunctionComponent<P>
Represents the type of a function component. Can optionally
receive a type argument that represents the props the component
receives.
FC<{ showCompleted: boolean
showCompleted: boolean }> = ({ showCompleted: boolean
showCompleted }) => { const const todos: readonly { id: string; text: string; completed: boolean;}[]
todos = const store: Store<LiveStoreSchema.Any, {}> & ReactApi
store.useQuery: <LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], "def">>(queryable: LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], "def">, options?: { store?: Store;}) => readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[]
Returns the result of a query and subscribes to future updates.
Example:
const App = () => { const todos = useQuery(queryDb(tables.todos.query.where({ complete: true }))) return <div>{todos.map((todo) => <div key={todo.id}>{todo.title}</div>)}</div>}
useQuery(const todos$: ({ showCompleted }: { showCompleted: boolean;}) => LiveQueryDef<readonly { readonly id: string; readonly text: string; readonly completed: boolean;}[], "def">
todos$({ showCompleted: boolean
showCompleted })) as interface ReadonlyArray<T>
ReadonlyArray<{ id: string
id: string text: string
text: string completed: boolean
completed: boolean }>
return <React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>{const todos: readonly { id: string; text: string; completed: boolean;}[]
todos.ReadonlyArray<T>.length: number
Gets the length of the array. This is a number one higher than the highest element defined in an array.
length} Done</React.JSX.IntrinsicElements.div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>
div>}Further reading
Section titled “Further reading”- Riffle: Building data-centric apps with a reactive relational database
- Adapton / miniAdapton
Related technologies
Section titled “Related technologies”- Signia: Signia is a minimal, fast, and scalable signals library for TypeScript developed by TLDraw.