SQLite state schema (Effect schema)
LiveStore supports defining SQLite tables using Effect Schema with annotations for database constraints. This approach provides strong type safety, composability, and automatic type mapping from TypeScript to SQLite.
Note: This approach will become the default once Effect Schema v4 is released. See livestore#382 for details.
For the traditional column-based approach, see SQLite State Schema.
Basic usage
Section titled “Basic usage”Define tables using Effect Schema with database constraint annotations:
import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>
UserSchema = import Schema
Schema.function Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>(fields: { readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}): Schema.Struct<...>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), email: Schema.String
email: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique), name: Schema.String
name: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, age: Schema.Int
age: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int.Pipeable.pipe<Schema.Int, Schema.Int>(this: Schema.Int, ab: (_: Schema.Int) => Schema.Int): Schema.Int (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault(0)), isActive: Schema.Boolean
isActive: import Schema
Schema.const Boolean: Schema.Boolean
Type-level representation of
Boolean
.
Schema for boolean values. Validates that the input is typeof "boolean".
When to use
Use to validate values that are already JavaScript booleans.
Boolean.Pipeable.pipe<Schema.Boolean, Schema.Boolean>(this: Schema.Boolean, ab: (_: Schema.Boolean) => Schema.Boolean): Schema.Boolean (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault(true)), metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>
metadata: import Schema
Schema.const optional: optionalLambda<Schema.$Record<Schema.String, Schema.Unknown>>(self: Schema.$Record<Schema.String, Schema.Unknown>) => Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>
Type-level representation returned by
optional
.
Marks a struct field as optional, allowing the key to be absent or
undefined.
Details
The resulting property may be absent or explicitly set to undefined.
Equivalent to optionalKey(UndefinedOr(S)).
Use
optionalKey
instead if you want exact optional semantics (absent
only, not undefined).
Example (Defining an optional field accepting undefined)
import { Schema } from "effect"
const schema = Schema.Struct({ name: Schema.String, age: Schema.optional(Schema.Number)})
// { readonly name: string; readonly age?: number | undefined }type Person = typeof schema.Type
optional(import Schema
Schema.function Record<Schema.String, Schema.Unknown>(key: Schema.String, value: Schema.Unknown, options?: { readonly keyValueCombiner: { readonly decode?: Combiner<readonly [string, unknown]> | undefined; readonly encode?: Combiner<readonly [string, unknown]> | undefined; };} | undefined): Schema.$Record<Schema.String, Schema.Unknown>
Defines a record schema whose dynamic properties are selected by a key schema
and decoded with a value schema.
Details
For dynamic keys, the key schema selects matching own properties and the
value schema decodes or encodes only those selected properties. Checks on
string, number, symbol, and template literal key schemas narrow which
properties are selected.
For transformed key schemas, property selection is based on encoded property
names before the selected key is decoded.
Example (Defining a string-keyed record of numbers)
import { Schema } from "effect"
const schema = Schema.Record(Schema.String, Schema.Number)
// { readonly [x: string]: number }type R = typeof schema.Type
const result = Schema.decodeUnknownSync(schema)({ a: 1, b: 2 })console.log(result)// { a: 1, b: 2 }
Record(import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, import Schema
Schema.const Unknown: Schema.Unknown
Type-level representation of
Unknown
.
Schema for the unknown type. Accepts any value without validation.
When to use
Use as a top schema when you need to accept any input while preserving
TypeScript's unknown safety at use sites.
Unknown)),}).Bottom<unknown, unknown, unknown, unknown, Objects, Struct<{ readonly id: String; readonly email: String; readonly name: String; readonly age: Int; readonly isActive: Boolean; readonly metadata: optional<...>; }>, ... 8 more ..., "required">.annotate(annotations: Schema.Annotations.Bottom<{ readonly id: string; readonly email: string; readonly age: number; readonly isActive: boolean; readonly name: string; readonly metadata?: { readonly [x: string]: unknown; } | undefined;}, readonly []>): Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>
annotate({ Annotations.Augment.title?: string | undefined
title: 'users' })
export const const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, { readonly id: string; readonly email: string; readonly age: number; readonly isActive: boolean; readonly name: string; readonly metadata?: { readonly [x: string]: unknown; } | undefined;}, { readonly id: string; readonly email: string; readonly age: number; readonly isActive: boolean; readonly name: string; readonly metadata?: { readonly [x: string]: unknown; } | undefined;}, Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>>, State.SQLite.TableOptions, Schema.Struct<...>>
userTable = import State
State.import SQLite
SQLite.function table<Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>, Partial<{ indexes: Index[];}>>(args: { schema: Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<...>; }>;} & 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({ schema: Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>
schema: const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int; readonly isActive: Schema.Boolean; readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;}>
UserSchema })Schema annotations
Section titled “Schema annotations”You can annotate schema fields with database constraints:
Primary keys
Section titled “Primary keys”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly id: Schema.String;}>
_schema = import Schema
Schema.function Struct<{ readonly id: Schema.String;}>(fields: { readonly id: Schema.String;}): Schema.Struct<{ readonly id: Schema.String;}>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), // Other fields...})Important: Primary key columns cannot be nullable. This will throw an error:
import { import Schema
Schema, import State
State } from '@livestore/livestore'
// ❌ This will throw an error at runtime because primary keys cannot be nullableconst const _badSchema: Schema.Struct<{ readonly id: Schema.NullOr<Schema.String>;}>
_badSchema = import Schema
Schema.function Struct<{ readonly id: Schema.NullOr<Schema.String>;}>(fields: { readonly id: Schema.NullOr<Schema.String>;}): Schema.Struct<{ readonly id: Schema.NullOr<Schema.String>;}>
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({ id: Schema.NullOr<Schema.String>
id: import Schema
Schema.const NullOr: NullOrLambda<Schema.String>(self: Schema.String) => Schema.NullOr<Schema.String>
Type-level representation returned by
NullOr
.
Creates a union schema of S | null.
NullOr(import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String).Pipeable.pipe<Schema.NullOr<Schema.String>, Schema.NullOr<Schema.String>>(this: Schema.NullOr<Schema.String>, ab: (_: Schema.NullOr<Schema.String>) => Schema.NullOr<Schema.String>): Schema.NullOr<Schema.String> (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey),})Auto-Increment
Section titled “Auto-Increment”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly id: Schema.Int;}>
_schema = import Schema
Schema.function Struct<{ readonly id: Schema.Int;}>(fields: { readonly id: Schema.Int;}): Schema.Struct<{ readonly id: Schema.Int;}>
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({ id: Schema.Int
id: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int.Pipeable.pipe<Schema.Int, Schema.Int, Schema.Int>(this: Schema.Int, ab: (_: Schema.Int) => Schema.Int, bc: (_: Schema.Int) => Schema.Int): Schema.Int (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey, import State
State.import SQLite
SQLite.const withAutoIncrement: <T extends Schema.Top>(schema: T) => T
Adds an auto-increment annotation to a schema.
withAutoIncrement), // Other fields...})Default values
Section titled “Default values”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly status: Schema.String; readonly createdAt: Schema.String; readonly count: Schema.Int;}>
_schema = import Schema
Schema.function Struct<{ readonly status: Schema.String; readonly createdAt: Schema.String; readonly count: Schema.Int;}>(fields: { readonly status: Schema.String; readonly createdAt: Schema.String; readonly count: Schema.Int;}): Schema.Struct<{ readonly status: Schema.String; readonly createdAt: Schema.String; readonly count: Schema.Int;}>
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({ status: Schema.String
status: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault('active')), createdAt: Schema.String
createdAt: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault('CURRENT_TIMESTAMP')), count: Schema.Int
count: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int.Pipeable.pipe<Schema.Int, Schema.Int>(this: Schema.Int, ab: (_: Schema.Int) => Schema.Int): Schema.Int (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault(0)),})Unique constraints
Section titled “Unique constraints”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly email: Schema.String; readonly username: Schema.String;}>
_schema = import Schema
Schema.function Struct<{ readonly email: Schema.String; readonly username: Schema.String;}>(fields: { readonly email: Schema.String; readonly username: Schema.String;}): Schema.Struct<{ readonly email: Schema.String; readonly username: Schema.String;}>
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({ email: Schema.String
email: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique), username: Schema.String
username: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique),})Unique annotations automatically create unique indexes.
Custom column types
Section titled “Custom column types”Override the automatically inferred SQLite column type:
import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly version: Schema.Finite; readonly data: Schema.Uint8Array;}>
_schema = import Schema
Schema.function Struct<{ readonly version: Schema.Finite; readonly data: Schema.Uint8Array;}>(fields: { readonly version: Schema.Finite; readonly data: Schema.Uint8Array;}): Schema.Struct<{ readonly version: Schema.Finite; readonly data: Schema.Uint8Array;}>
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({ // Store a number as text instead of real version: Schema.Finite
version: 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.Finite, Schema.Finite>(this: Schema.Finite, ab: (_: Schema.Finite) => Schema.Finite): Schema.Finite (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withColumnType: (type: FieldColumnType) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withColumnType('text')), // Store binary data as blob data: Schema.Uint8Array
data: import Schema
Schema.const Uint8Array: Schema.Uint8Array
Type-level representation of
Uint8Array
.
Schema for JavaScript Uint8Array objects.
Details
Default JSON serializer:
The default JSON serializer encodes Uint8Array as a Base64 encoded string.
Uint8Array.Pipeable.pipe<Schema.Uint8Array, Schema.Uint8Array>(this: Schema.Uint8Array, ab: (_: Schema.Uint8Array) => Schema.Uint8Array): Schema.Uint8Array (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withColumnType: (type: FieldColumnType) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withColumnType('blob')),})Combining annotations
Section titled “Combining annotations”Annotations can be chained together:
import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const _schema: Schema.Struct<{ readonly id: Schema.Int; readonly email: Schema.String;}>
_schema = import Schema
Schema.function Struct<{ readonly id: Schema.Int; readonly email: Schema.String;}>(fields: { readonly id: Schema.Int; readonly email: Schema.String;}): Schema.Struct<{ readonly id: Schema.Int; readonly email: Schema.String;}>
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({ id: Schema.Int
id: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int.Pipeable.pipe<Schema.Int, Schema.Int, Schema.Int>(this: Schema.Int, ab: (_: Schema.Int) => Schema.Int, bc: (_: Schema.Int) => Schema.Int): Schema.Int (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey, import State
State.import SQLite
SQLite.const withAutoIncrement: <T extends Schema.Top>(schema: T) => T
Adds an auto-increment annotation to a schema.
withAutoIncrement), email: Schema.String
email: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String, bc: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique, import State
State.import SQLite
SQLite.const withColumnType: (type: FieldColumnType) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withColumnType('text')),})Table naming
Section titled “Table naming”You can specify table names in several ways:
Using schema annotations
Section titled “Using schema annotations”import { import Schema
Schema, import State
State } from '@livestore/livestore'
// Using title annotationconst const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
UserSchema = import Schema
Schema.function Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>(fields: { readonly id: Schema.String; readonly name: Schema.String;}): Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), name: Schema.String
name: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String,}).Bottom<unknown, unknown, unknown, unknown, Objects, Struct<{ readonly id: String; readonly name: String; }>, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.annotate(annotations: Schema.Annotations.Bottom<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Type">, readonly []>): Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
annotate({ Annotations.Augment.title?: string | undefined
title: 'users' })
export const const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Encoded">, Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>>, State.SQLite.TableOptions, Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>>
userTable = import State
State.import SQLite
SQLite.function table<Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>, Partial<{ indexes: Index[];}>>(args: { schema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String; }>;} & Partial<Partial<{ indexes: Index[];}>>): State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<...>, Schema.Struct<...>>, State.SQLite.TableOptions, Schema.Struct<...>> (+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({ schema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
schema: const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
UserSchema })
// Using identifier annotationconst const PostSchema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>
PostSchema = import Schema
Schema.function Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>(fields: { readonly id: Schema.String; readonly title: Schema.String;}): Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), title: Schema.String
title: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String,}).Bottom<unknown, unknown, unknown, unknown, Objects, Struct<{ readonly id: String; readonly title: String; }>, unknown, unknown, readonly [], unknown, "readonly", "required", "no-default", "readonly", "required">.annotate(annotations: Schema.Annotations.Bottom<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String;}, "Type">, readonly []>): Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>
annotate({ Annotations.Bottom<T, TypeParameters extends ReadonlyArray<Constraint>>.identifier?: string | undefined
Stable identifier for this schema node.
Details
Identifiers are used by schema tooling, including JSON Schema
generation, to name references. The default formatter also uses
identifier as the expected label for type-level failures, such as
Expected UserId, got null.
identifier does not name a failed filter or refinement. If the base
type matches and a filter fails, put expected or message on the
filter/refinement instead.
identifier: 'posts' })
export const const postTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String;}, "Encoded">, Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>>, State.SQLite.TableOptions, Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly title: Schema.Codec<string, string, never, never>;}>>
postTable = import State
State.import SQLite
SQLite.function table<Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>, Partial<{ indexes: Index[];}>>(args: { schema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; }>;} & Partial<Partial<{ indexes: Index[];}>>): State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<...>, Schema.Struct<...>>, State.SQLite.TableOptions, Schema.Struct<...>> (+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({ schema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>
schema: const PostSchema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String;}>
PostSchema })Explicit name
Section titled “Explicit name”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
UserSchema = import Schema
Schema.function Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>(fields: { readonly id: Schema.String; readonly name: Schema.String;}): Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), name: Schema.String
name: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String,})
export const const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<"users", Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Encoded">, Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>>, State.SQLite.TableOptions, Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>;}>>
userTable = import State
State.import SQLite
SQLite.function table<"users", Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>, Partial<{ indexes: Index[];}>>(args: { name: "users"; schema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String; }>;} & Partial<Partial<{ indexes: Index[];}>>): State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<"users", Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly name: Schema.String;}, "Type">, Schema.Struct.ReadonlySide<...>, Schema.Struct<...>>, State.SQLite.TableOptions, Schema.Struct<...>> (+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: "users"
name: 'users', schema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
schema: const UserSchema: Schema.Struct<{ readonly id: Schema.String; readonly name: Schema.String;}>
UserSchema,})Note: Title annotation takes precedence over identifier annotation.
Type mapping
Section titled “Type mapping”Effect Schema types are automatically mapped to SQLite column types:
| Schema Type | SQLite Type | TypeScript Type |
|---|---|---|
Schema.String | text | string |
Schema.Number | real | number |
Schema.Int | integer | number |
Schema.Boolean | integer | boolean |
Schema.Date | text | Date |
Schema.BigInt | text | bigint |
| Complex types (Struct, Array, etc.) | text (JSON encoded) | Decoded type |
Schema.optional(T) | Nullable column | T | undefined |
Schema.NullOr(T) | Nullable column | T | null |
Advanced examples
Section titled “Advanced examples”Complex schema with multiple constraints
Section titled “Complex schema with multiple constraints”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const ProductSchema: Schema.Struct<{ readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}>
ProductSchema = import Schema
Schema.function Struct<{ readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}>(fields: { readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}): Schema.Struct<...>
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({ id: Schema.Int
id: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int.Pipeable.pipe<Schema.Int, Schema.Int, Schema.Int>(this: Schema.Int, ab: (_: Schema.Int) => Schema.Int, bc: (_: Schema.Int) => Schema.Int): Schema.Int (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey, import State
State.import SQLite
SQLite.const withAutoIncrement: <T extends Schema.Top>(schema: T) => T
Adds an auto-increment annotation to a schema.
withAutoIncrement), sku: Schema.String
sku: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique), name: Schema.String
name: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, price: Schema.Finite
price: 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.Finite, Schema.Finite>(this: Schema.Finite, ab: (_: Schema.Finite) => Schema.Finite): Schema.Finite (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault(0)), category: Schema.Literals<readonly ["electronics", "clothing", "books"]>
category: import Schema
Schema.function Literals<readonly ["electronics", "clothing", "books"]>(literals: readonly ["electronics", "clothing", "books"]): Schema.Literals<readonly ["electronics", "clothing", "books"]>
Creates a union schema from an array of literal values.
Example (Defining status codes)
import { Schema } from "effect"
const schema = Schema.Literals(["active", "inactive", "pending"])// accepts "active", "inactive", or "pending"
Literals(['electronics', 'clothing', 'books']), metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}>>
metadata: import Schema
Schema.const optional: optionalLambda<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}>>(self: Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}>) => Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}>>
Type-level representation returned by
optional
.
Marks a struct field as optional, allowing the key to be absent or
undefined.
Details
The resulting property may be absent or explicitly set to undefined.
Equivalent to optionalKey(UndefinedOr(S)).
Use
optionalKey
instead if you want exact optional semantics (absent
only, not undefined).
Example (Defining an optional field accepting undefined)
import { Schema } from "effect"
const schema = Schema.Struct({ name: Schema.String, age: Schema.optional(Schema.Number)})
// { readonly name: string; readonly age?: number | undefined }type Person = typeof schema.Type
optional( import Schema
Schema.function Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}>(fields: { readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>;}): Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: 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({ weight: Schema.Finite
weight: import Schema
Schema.const Finite: Schema.Finite
Type-level representation of
Finite
.
Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.
Finite, dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite;}>
dimensions: import Schema
Schema.function Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite;}>(fields: { readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite;}): Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: 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({ width: Schema.Finite
width: import Schema
Schema.const Finite: Schema.Finite
Type-level representation of
Finite
.
Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.
Finite, height: Schema.Finite
height: import Schema
Schema.const Finite: Schema.Finite
Type-level representation of
Finite
.
Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.
Finite, depth: Schema.Finite
depth: import Schema
Schema.const Finite: Schema.Finite
Type-level representation of
Finite
.
Schema for finite numbers, rejecting NaN, Infinity, and -Infinity.
Finite, }), }), ), isActive: Schema.Boolean
isActive: import Schema
Schema.const Boolean: Schema.Boolean
Type-level representation of
Boolean
.
Schema for boolean values. Validates that the input is typeof "boolean".
When to use
Use to validate values that are already JavaScript booleans.
Boolean.Pipeable.pipe<Schema.Boolean, Schema.Boolean>(this: Schema.Boolean, ab: (_: Schema.Boolean) => Schema.Boolean): Schema.Boolean (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault(true)), createdAt: Schema.Date
createdAt: import Schema
Schema.const Date: Schema.Date
Type-level representation of
Date
.
Schema for JavaScript Date objects.
When to use
Use to validate in-memory values that must already be JavaScript date
objects.
Details
This schema accepts any Date instance, including invalid dates. The default
JSON serializer encodes valid dates as ISO 8601 strings; invalid dates encode
as "Invalid Date".
Example (Defining a Date schema)
import { Schema } from "effect"
Schema.decodeUnknownSync(Schema.Date)(new Date("2024-01-01"))// => Date { 2024-01-01T00:00:00.000Z }
Date.Pipeable.pipe<Schema.Date, Schema.Date>(this: Schema.Date, ab: (_: Schema.Date) => Schema.Date): Schema.Date (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withDefault: (value: unknown) => <T extends Schema.Top>(schema: T) => T (+1 overload)
withDefault('CURRENT_TIMESTAMP')),}).Bottom<unknown, unknown, unknown, unknown, Objects, Struct<{ readonly id: Int; readonly sku: String; readonly name: String; readonly price: Finite; readonly category: Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: optional<...>; readonly isActive: Boolean; readonly createdAt: Date; }>, ... 8 more ..., "required">.annotate(annotations: Schema.Annotations.Bottom<{ readonly id: number; readonly sku: string; readonly price: number; readonly category: "electronics" | "clothing" | "books"; readonly isActive: boolean; readonly createdAt: Date; readonly name: string; readonly metadata?: Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }, "Type"> | undefined;}, readonly []>): Schema.Struct<...>
annotate({ Annotations.Augment.title?: string | undefined
title: 'products' })
export const const productTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, { readonly id: number; readonly sku: string; readonly price: number; readonly category: "electronics" | "clothing" | "books"; readonly isActive: boolean; readonly createdAt: Date; readonly name: string; readonly metadata?: Schema.Struct.ReadonlySide<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }, "Type"> | undefined;}, { readonly id: number; readonly sku: string; ... 5 more ...; readonly metadata?: Schema.Struct.ReadonlySide<...> | undefined;}, Schema.Struct<...>>, State.SQLite.TableOptions, Schema.Struct<...>>
productTable = import State
State.import SQLite
SQLite.function table<Schema.Struct<{ readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}>, 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({ schema: Schema.Struct<{ readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}>
schema: const ProductSchema: Schema.Struct<{ readonly id: Schema.Int; readonly sku: Schema.String; readonly name: Schema.String; readonly price: Schema.Finite; readonly category: Schema.Literals<readonly ["electronics", "clothing", "books"]>; readonly metadata: Schema.optional<Schema.Struct<{ readonly weight: Schema.Finite; readonly dimensions: Schema.Struct<{ readonly width: Schema.Finite; readonly height: Schema.Finite; readonly depth: Schema.Finite; }>; }>>; readonly isActive: Schema.Boolean; readonly createdAt: Schema.Date;}>
ProductSchema })Working with Schema.Class
Section titled “Working with Schema.Class”import { import Schema
Schema, import State
State } from '@livestore/livestore'
class class User
User extends import Schema
Schema.const Class: <User, {}>(identifier: string) => { <Fields>(fields: Fields, annotations?: Schema.Annotations.Declaration<User, readonly [Schema.Struct<Fields>]> | undefined): Schema.Class<User, Schema.Struct<Fields>, {}>; <S>(schema: S, annotations?: Schema.Annotations.Declaration<User, readonly [S]> | undefined): Schema.Class<User, S, {}>;}
Creates a schema-backed class whose constructor validates input against a
Struct
schema. Construction throws a
SchemaError
on invalid
input.
When to use
Use when you need a schema-backed data class with validated construction,
schema-derived decoding/encoding, and class-style methods or inheritance.
Details
Pass the desired class type as the first type parameter. The second optional
type parameter can be used to add nominal brands.
Gotchas
Passing disableChecks in the options skips constructor validation.
Example (Defining a basic class)
import { Schema } from "effect"
class Person extends Schema.Class<Person>("Person")({ name: Schema.String, age: Schema.Number}) {}
const alice = new Person({ name: "Alice", age: 30 })console.log(alice.name) // "Alice"console.log(`${alice}`) // "Person({ name: Alice, age: 30 })"
Example (Extending a class)
import { Schema } from "effect"
class Animal extends Schema.Class<Animal>("Animal")({ name: Schema.String}) {}
class Dog extends Animal.extend<Dog>("Dog")({ breed: Schema.String}) {}
const dog = new Dog({ name: "Rex", breed: "Labrador" })console.log(dog.name) // "Rex"console.log(dog.breed) // "Labrador"
Class<class User
User>('User')({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), email: Schema.String
email: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withUnique: <T extends Schema.Top>(schema: T) => T
Adds a unique constraint annotation to a schema.
withUnique), name: Schema.String
name: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, age: Schema.Int
age: import Schema
Schema.const Int: Schema.Int
Type-level representation of
Int
.
Schema for integers, rejecting NaN, Infinity, and -Infinity.
Int,}) {}
export const const userTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<"users", User, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int;}, "Encoded">, typeof User>, State.SQLite.TableOptions, Schema.Struct<{ readonly id: Schema.Codec<string, string, never, never>; readonly email: Schema.Codec<string, string, never, never>; readonly name: Schema.Codec<string, string, never, never>; readonly age: Schema.Codec<...>;}>>
userTable = import State
State.import SQLite
SQLite.function table<"users", typeof User, Partial<{ indexes: Index[];}>>(args: { name: "users"; schema: typeof User;} & Partial<Partial<{ indexes: Index[];}>>): State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<"users", User, Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly email: Schema.String; readonly name: Schema.String; readonly age: Schema.Int;}, "Encoded">, typeof User>, State.SQLite.TableOptions, Schema.Struct<...>> (+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: "users"
name: 'users', schema: typeof User
schema: class User
User,})Custom indexes
Section titled “Custom indexes”import { import Schema
Schema, import State
State } from '@livestore/livestore'
const const PostSchema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>
PostSchema = import Schema
Schema.function Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>(fields: { readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}): Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>
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({ id: Schema.String
id: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String.Pipeable.pipe<Schema.String, Schema.String>(this: Schema.String, ab: (_: Schema.String) => Schema.String): Schema.String (+21 overloads)
pipe(import State
State.import SQLite
SQLite.const withPrimaryKey: <T extends Schema.Top>(schema: T) => T
Adds a primary key annotation to a schema.
withPrimaryKey), title: Schema.String
title: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, authorId: Schema.String
authorId: import Schema
Schema.const String: Schema.String
Type-level representation of
String
.
Schema for string values. Validates that the input is typeof "string".
String, createdAt: Schema.Date
createdAt: import Schema
Schema.const Date: Schema.Date
Type-level representation of
Date
.
Schema for JavaScript Date objects.
When to use
Use to validate in-memory values that must already be JavaScript date
objects.
Details
This schema accepts any Date instance, including invalid dates. The default
JSON serializer encodes valid dates as ISO 8601 strings; invalid dates encode
as "Invalid Date".
Example (Defining a Date schema)
import { Schema } from "effect"
Schema.decodeUnknownSync(Schema.Date)(new Date("2024-01-01"))// => Date { 2024-01-01T00:00:00.000Z }
Date,}).Bottom<unknown, unknown, unknown, unknown, Objects, Struct<{ readonly id: String; readonly title: String; readonly authorId: String; readonly createdAt: Date; }>, ... 8 more ..., "required">.annotate(annotations: Schema.Annotations.Bottom<Schema.Struct<Fields extends Schema.Struct.Fields>.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}, "Type">, readonly []>): Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>
annotate({ Annotations.Augment.title?: string | undefined
title: 'posts' })
export const const postTable: State.SQLite.TableDef<State.SQLite.SqliteTableDefForSchemaInput<string, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}, "Type">, Schema.Struct.ReadonlySide<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}, "Encoded">, Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>>, State.SQLite.TableOptions, Schema.Struct<...>>
postTable = import State
State.import SQLite
SQLite.function table<Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>, { readonly schema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date; }>; readonly indexes: [{ readonly name: "idx_posts_author"; readonly columns: readonly ["authorId"]; }, { readonly name: "idx_posts_created"; readonly columns: readonly ["createdAt"]; }];}>(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({ schema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>
schema: const PostSchema: Schema.Struct<{ readonly id: Schema.String; readonly title: Schema.String; readonly authorId: Schema.String; readonly createdAt: Schema.Date;}>
PostSchema, indexes?: [{ readonly name: "idx_posts_author"; readonly columns: readonly ["authorId"];}, { readonly name: "idx_posts_created"; readonly columns: readonly ["createdAt"];}]
indexes: [ { name: "idx_posts_author"
name: 'idx_posts_author', columns: readonly ["authorId"]
columns: ['authorId'] }, { name: "idx_posts_created"
name: 'idx_posts_created', columns: readonly ["createdAt"]
columns: ['createdAt'] }, ],})Best Practices
Section titled “Best Practices”Schema Design
Section titled “Schema Design”- Always use
withPrimaryKeyfor primary key columns - never combine it with nullable types - Use
Schema.optional()for truly optional fields that can be undefined - Use
Schema.NullOr()for fields that can explicitly be set to null - Leverage schema annotations like
titleoridentifierto avoid repeating table names - Group related schemas in the same module for better organization
Type safety
Section titled “Type safety”- Let TypeScript infer table types rather than explicitly typing them
- Use Effect Schema’s refinements and transformations for data validation
- Prefer Effect Schema’s built-in types (
Schema.Int,Schema.Date) over generic types where appropriate
Performance
Section titled “Performance”- Be mindful of complex types stored as JSON - they can impact query performance
- Use appropriate indexes for frequently queried columns
- Consider using
withColumnTypeto optimize storage for specific use cases
When to Use This Approach
Section titled “When to Use This Approach”Use Effect Schema-based tables when:
- You already have Effect Schema definitions to reuse
- You prefer Effect Schema’s composability and transformations
- Your schemas are shared across different parts of your application
- You want automatic type mapping and strong type safety
- You plan to migrate to Effect Schema v4 when it becomes available
Consider column-based tables when:
- You need precise control over SQLite column types
- You’re migrating from existing SQLite schemas
- You prefer explicit column configuration
- You’re not already using Effect Schema extensively in your project