Skip to content

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.

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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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.

@since3.10.0

@since3.10.0

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.

@since4.0.0

@seeBooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@since4.0.0

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

@since3.10.0

@since3.10.0

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 }

@since3.10.0

Record
(
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

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.

@since3.10.0

@seeAny for the any variant.

@since3.10.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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
})

You can annotate schema fields with database constraints:

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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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 nullable
const
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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

NullOr
(
import Schema
Schema
.
const String: Schema.String

Type-level representation of

String

.

Schema for string values. Validates that the input is typeof "string".

@since4.0.0

@since4.0.0

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
),
})
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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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...
})
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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.

@since3.10.0

@since3.10.0

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)),
})
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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.

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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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.

@since4.0.0

@since4.0.0

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')),
})

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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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".

@since4.0.0

@since4.0.0

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')),
})

You can specify table names in several ways:

import {
import Schema
Schema
,
import State
State
} from '@livestore/livestore'
// Using title annotation
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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 annotation
const
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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
})
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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.

Effect Schema types are automatically mapped to SQLite column types:

Schema TypeSQLite TypeTypeScript Type
Schema.Stringtextstring
Schema.Numberrealnumber
Schema.Intintegernumber
Schema.Booleanintegerboolean
Schema.DatetextDate
Schema.BigInttextbigint
Complex types (Struct, Array, etc.)text (JSON encoded)Decoded type
Schema.optional(T)Nullable columnT | undefined
Schema.NullOr(T)Nullable columnT | null
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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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.

@since3.10.0

@since3.10.0

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"

@seeLiteral for a schema that represents a single literal.

@since4.0.0

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

@since3.10.0

@since3.10.0

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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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 }

@since3.10.0

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.

@since3.10.0

@since3.10.0

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.

@since3.10.0

@since3.10.0

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.

@since3.10.0

@since3.10.0

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.

@since4.0.0

@seeBooleanFromBit for a schema that decodes bit literals 0 or 1 into a boolean

@since4.0.0

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 }

@since4.0.0

@seeDateValid for accepting only valid Date instances

@seeDateFromString for decoding strings into Date instances

@seeDateFromMillis for decoding epoch milliseconds into Date instances

@since4.0.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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
})
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"

@seeTaggedClass for adding a _tag literal field to the class schema

@seeErrorClass for defining schema-backed error classes

@seeTaggedErrorClass for defining tagged schema-backed error classes

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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.

@since3.10.0

@since3.10.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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
,
})
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 }

@since3.10.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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".

@since4.0.0

@since4.0.0

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 }

@since4.0.0

@seeDateValid for accepting only valid Date instances

@seeDateFromString for decoding strings into Date instances

@seeDateFromMillis for decoding epoch milliseconds into Date instances

@since4.0.0

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:

  1. Using explicit column definitions
  2. Using an Effect Schema (either the name property needs to be provided or the schema needs to have a title/identifier)
// Using explicit columns
const 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 annotations
import { 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 name
const 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 indexes
const 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'] },
],
})
  • Always use withPrimaryKey for 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 title or identifier to avoid repeating table names
  • Group related schemas in the same module for better organization
  • 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
  • Be mindful of complex types stored as JSON - they can impact query performance
  • Use appropriate indexes for frequently queried columns
  • Consider using withColumnType to optimize storage for specific use cases

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