SQL queries
Query builder
Section titled “Query builder”LiveStore also provides a small query builder for the most common queries. The query builder automatically derives the appropriate result schema internally.
import { import Schema
Schema } from 'effect'
import { import State
State } from '@livestore/livestore'
const const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table = import State
State.import SQLite
SQLite.function table<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; 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.annotate({ 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,}).annotate({ 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: "my_table"
name: 'my_table', columns: { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}
columns: { id: { columnType: "text"; schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;} (+1 overload)
text({ primaryKey?: true
primaryKey: true }), name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;}
name: import State
State.import SQLite
SQLite.const text: () => { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false;}
tags: import State
State.import SQLite
SQLite.const json: <readonly string[], false, readonly [], false, false>(args: { schema?: Schema.Codec<readonly string[], any, never, never>; default?: readonly []; nullable?: false; primaryKey?: false; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
json({ schema?: Schema.Codec<readonly string[], any, never, never>
schema: import Schema
Schema.Array<Schema.String>(self: Schema.String): Schema.$Array<Schema.String>export Array
Defines a ReadonlyArray schema for a given element schema.
Example (Defining an array of strings)
import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
const result = Schema.decodeUnknownSync(schema)(["a", "b", "c"])console.log(result)// [ 'a', 'b', 'c' ]
Array(import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String), default?: readonly []
default: [] }), },})
// Read queriesconst table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.select: <"name">(pluckColumn: "name") => QueryBuilder<readonly string[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 2 more ... | "row"> (+1 overload)
Selects and plucks a single column
select('name')const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.where: <"name">(col: "name", op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, value: string) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<...>, "select" | "row"> (+3 overloads)
where('name', '=', 'Alice')const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.where: (params: Partial<{ readonly id: string | { op: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly name: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { ...; } | undefined; readonly tags: readonly string[] | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+3 overloads)
where({ name?: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
name: 'Alice' })const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.orderBy: <"name">(col: "name", direction: "asc" | "desc") => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { ...; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "onConflict" | "returning"> (+1 overload)
orderBy('name', 'desc').offset: (offset: number) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<...>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "orderBy" | ... 3 more ... | "row">
Example:
db.todos.offset(10)
offset(10).limit: (limit: number) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<...>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "orderBy" | ... 5 more ... | "row">
Example:
db.todos.limit(10)
limit(10)const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.count: () => QueryBuilder<number, State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 7 more ... | "row">
Example:
db.todos.count()db.todos.count().where('completed', true)
count().where: <"name">(col: "name", op: QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, value: string) => QueryBuilder<number, State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 7 more ... | "row"> (+3 overloads)
where('name', 'LIKE', '%Ali%')
// JSON array containment queries// NOTE: These use SQLite's json_each() which cannot be indexedconst table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.where: (params: Partial<{ readonly id: string | { op: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly name: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { ...; } | undefined; readonly tags: readonly string[] | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+3 overloads)
where({ tags?: readonly string[] | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: readonly string[];} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly (readonly string[])[];} | { op: QueryBuilder.WhereOps.JsonArray; value: string;} | undefined
tags: { op: QueryBuilder.WhereOps.JsonArray
op: 'JSON_CONTAINS', value: string
value: 'important' } })const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.where: (params: Partial<{ readonly id: string | { op: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly name: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { ...; } | undefined; readonly tags: readonly string[] | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+3 overloads)
where({ tags?: readonly string[] | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: readonly string[];} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly (readonly string[])[];} | { op: QueryBuilder.WhereOps.JsonArray; value: string;} | undefined
tags: { op: QueryBuilder.WhereOps.JsonArray
op: 'JSON_NOT_CONTAINS', value: string
value: 'archived' } })
// Write queriesconst table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.insert: (values: { readonly name: string; readonly id: string; readonly tags?: readonly string[];}) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { ...; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Insert a new row into the table.
insert({ id: string
id: '123', name: string
name: 'Bob' })const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.update: (values: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<...>, "select" | ... 6 more ... | "row">
Update rows in the table that match the where clause
Example:
db.todos.update({ status: 'completed' }).where({ id: '123' })
update({ name?: string
name: 'Alice' }).where: (params: Partial<{ readonly id: string | { op: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly name: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { ...; } | undefined; readonly tags: readonly string[] | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+3 overloads)
where({ id?: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
id: '123' })const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.delete: () => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Delete rows from the table that match the where clause
Example:
db.todos.delete().where({ status: 'completed' })
Note that it's generally recommended to do soft-deletes for synced apps.
delete().where: (params: Partial<{ readonly id: string | { op: Exclude<QueryBuilder<TResult, TTableDef extends State.SQLite.TableDefBase<any, any>, TWithout extends QueryBuilder.ApiFeature = never>.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[]; } | undefined; readonly name: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string; } | { ...; } | undefined; readonly tags: readonly string[] | ... 3 more ... | undefined;}>) => QueryBuilder<...> (+3 overloads)
where({ id?: string | { op: Exclude<QueryBuilder.WhereOps.SingleValue, QueryBuilder.WhereOps.JsonArray>; value: string;} | { op: QueryBuilder.WhereOps.MultiValue; value: readonly string[];} | undefined
id: '123' })
// Upserts (insert or update on conflict)const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.insert: (values: { readonly name: string; readonly id: string; readonly tags?: readonly string[];}) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { ...; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Insert a new row into the table.
insert({ id: string
id: '123', name: string
name: 'Charlie' }).onConflict: <"id">(target: "id", action: "ignore" | "replace") => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { ...; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row"> (+1 overload)
onConflict('id', 'replace')const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; readonly tags: { columnType: "text"; schema: Schema.Codec<readonly string[], string, never, never>; default: Some<readonly []>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<...>, Schema.Struct<...>>
table.insert: (values: { readonly name: string; readonly id: string; readonly tags?: readonly string[];}) => QueryBuilder<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { ...; }; readonly tags: { ...; };}>, State.SQLite.WithDefaults<...>>, "select" | ... 6 more ... | "row">
Insert a new row into the table.
insert({ id: string
id: '456', name: string
name: 'Diana' }).onConflict: <"id">(target: "id", action: "update", updateValues: Partial<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">>) => QueryBuilder<readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly tags: Schema.Codec<readonly string[], string, never, never>;}, "Type">[], State.SQLite.TableDefBase<...>, "select" | ... 6 more ... | "row"> (+1 overload)
onConflict('id', 'update', { name?: string
name: 'Diana Updated' })Raw SQL queries
Section titled “Raw SQL queries”LiveStore supports arbitrary SQL queries on top of SQLite. In order for LiveStore to handle the query results correctly, you need to provide the result schema.
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 Schema
Schema, import State
State, const sql: (template: TemplateStringsArray, ...args: unknown[]) => string
This is a tag function for tagged literals.
it lets us get syntax highlighting on SQL queries in VSCode, but
doesn't do anything at runtime.
Code copied from: https://esdiscuss.org/topic/string-identity-template-tag
sql } from '@livestore/livestore'const const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<{ readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}>, Schema.Struct<...>>
table = import State
State.import SQLite
SQLite.function table<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}, Partial<{ indexes: Index[];}>>(args: { name: "my_table"; columns: { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; }; };} & 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.annotate({ 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,}).annotate({ 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: "my_table"
name: 'my_table', columns: { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}
columns: { id: { columnType: "text"; schema: Schema.Codec<string, string, never, 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.Codec<string, string, never, never>; default?: typeof NoDefault; nullable?: false; primaryKey?: true; autoIncrement?: false;}) => { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false;} (+1 overload)
text({ primaryKey?: true
primaryKey: true }), name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;}
name: import State
State.import SQLite
SQLite.const text: () => { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false;} (+1 overload)
text(), },})
const const filtered$: LiveQueryDef<readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}, "Type">[], "def">
filtered$ = queryDb<readonly Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}, "Type">[], readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}, "Type">[]>(queryInput: QueryInputRaw<readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}, "Type">[], readonly any[]> | QueryBuilder<...>, 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({ query: string
query: const sql: (template: TemplateStringsArray, ...args: unknown[]) => string
This is a tag function for tagged literals.
it lets us get syntax highlighting on SQL queries in VSCode, but
doesn't do anything at runtime.
Code copied from: https://esdiscuss.org/topic/string-identity-template-tag
sql`select * from my_table where name = 'Alice'`, schema: Schema.Codec<readonly Schema.Struct.ReadonlySide<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}, "Type">[], readonly any[], never, never>
schema: import Schema
Schema.Array<Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>>(self: Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>): Schema.$Array<Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>>export Array
Defines a ReadonlyArray schema for a given element schema.
Example (Defining an array of strings)
import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
const result = Schema.decodeUnknownSync(schema)(["a", "b", "c"])console.log(result)// [ 'a', 'b', 'c' ]
Array(const table: State.SQLite.TableDef<State.SQLite.SqliteTableDefForInput<"my_table", { readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}>, State.SQLite.WithDefaults<{ readonly id: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: true; autoIncrement: false; }; readonly name: { columnType: "text"; schema: Schema.Codec<string, string, never, never>; default: None<never>; nullable: false; primaryKey: false; autoIncrement: false; };}>, Schema.Struct<...>>
table.rowSchema: Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>
rowSchema),})
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({ query: string
query: const sql: (template: TemplateStringsArray, ...args: unknown[]) => string
This is a tag function for tagged literals.
it lets us get syntax highlighting on SQL queries in VSCode, but
doesn't do anything at runtime.
Code copied from: https://esdiscuss.org/topic/string-identity-template-tag
sql`select count(*) as count from my_table`, schema: Schema.Codec<number, readonly any[], never, never>
schema: import Schema
Schema.function Struct<{ readonly count: Schema.Finite;}>(fields: { readonly count: Schema.Finite;}): Schema.Struct<{ readonly count: Schema.Finite;}>
Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to
mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's Type is a readonly object type with the fields'
decoded types. The Encoded form mirrors the field schemas' encoded types.
Example (Defining a basic struct)
import { Schema } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Number, email: Schema.optionalKey(Schema.String)})
// { readonly name: string; readonly age: number; readonly email?: string }type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })console.log(alice)// { name: 'Alice', age: 30 }
Struct({ count: Schema.Finite
count: import Schema
Schema.const Finite: Schema.Finite
Type-level representation of
Finite
.
Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.
Finite }).Pipeable.pipe<Schema.Struct<{ readonly count: Schema.Finite;}>, PluckSchema<{ readonly count: Schema.Finite;}, "count">, Schema.$Array<PluckSchema<{ readonly count: Schema.Finite;}, "count">>, HeadOrElse<PluckSchema<{ readonly count: Schema.Finite;}, "count">>>(this: Schema.Struct<...>, ab: (_: Schema.Struct<{ readonly count: Schema.Finite;}>) => PluckSchema<{ readonly count: Schema.Finite;}, "count">, bc: (_: PluckSchema<{ readonly count: Schema.Finite;}, "count">) => Schema.$Array<...>, cd: (_: Schema.$Array<...>) => HeadOrElse<...>): HeadOrElse<...> (+21 overloads)
pipe(import Schema
Schema.const pluck: <"count">(key: "count") => <Fields extends { readonly count: Schema.Top;}>(schema: Schema.Struct<Fields>) => PluckSchema<Fields, "count" & keyof Fields>
pluck('count'), import Schema
Schema.const Array: ArrayLambdaexport Array
Defines a ReadonlyArray schema for a given element schema.
Example (Defining an array of strings)
import { Schema } from "effect"
const schema = Schema.Array(Schema.String)
const result = Schema.decodeUnknownSync(schema)(["a", "b", "c"])console.log(result)// [ 'a', 'b', 'c' ]
Array, import Schema
Schema.const headOrElse: () => <S extends Schema.Top>(array: Schema.$Array<S>) => HeadOrElse<S> (+2 overloads)
headOrElse()),})
JSON array containment
Section titled “JSON array containment”For JSON array columns, you can use JSON_CONTAINS and JSON_NOT_CONTAINS operators to check if an array contains (or doesn’t contain) a specific value:
// Find items with a specific tagtable.where({ tags: { op: 'JSON_CONTAINS', value: 'important' } })
// Find items without a specific tagtable.where({ tags: { op: 'JSON_NOT_CONTAINS', value: 'archived' } })Best practices
Section titled “Best practices”- Query results should be treated as immutable/read-only
- For queries which could return many rows, it’s recommended to paginate the results
- Usually both via paginated/virtualized rendering as well as paginated queries
- You’ll get best query performance by using a
WHEREclause over an indexed column combined with aLIMITclause. AvoidOFFSETas it can be slow on large tables
- You’ll get best query performance by using a
- Usually both via paginated/virtualized rendering as well as paginated queries
- For very large/complex queries, it can also make sense to implement incremental view maintenance (IVM) for your queries
- You can for example do this by have a separate table which is a materialized version of your query results which you update manually (and ideally incrementally) as the underlying data changes.