Readonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
init: ((options: { Readonly
onReadonly
reloadStatic
ofReadonly
init: ((options: { Readonly
onReadonly
reloadReadonly
init: ((options: { Readonly
onReadonly
reloadStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextStatic
ofStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeReadonly
createimport * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Readonly
_opStatic
Readonly
[Readonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Readonly
[Readonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Optional
[ignoreStatic
Readonly
[Readonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Readonly
[STMTypeStatic
Readonly
[Readonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
CommonStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceReadonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
ofReadonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Readonly
createCreate Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
// The same API for different platforms
// import * as E from "@evolu/react-native";
// import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Static
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
nanoid: Effect<NanoId, never, never>Readonly
nodeReadonly
rowStatic
ofReadonly
nanoid: Effect<NanoId, never, never>Readonly
nodeReadonly
rowReadonly
nanoid: Effect<NanoId, never, never>Readonly
nodeReadonly
rowStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
LiveStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
open: ((key: Uint8Array, ciphertext: Uint8Array) => Effect<Uint8Array, never, never>)Readonly
seal: ((key: Uint8Array, plaintext: Uint8Array) => Effect<Uint8Array, never, never>)Static
ofReadonly
open: ((key: Uint8Array, ciphertext: Uint8Array) => Effect<Uint8Array, never, never>)Readonly
seal: ((key: Uint8Array, plaintext: Uint8Array) => Effect<Uint8Array, never, never>)Readonly
open: ((key: Uint8Array, ciphertext: Uint8Array) => Effect<Uint8Array, never, never>)Readonly
seal: ((key: Uint8Array, plaintext: Uint8Array) => Effect<Uint8Array, never, never>)Static
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
CommonStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextStatic
ofStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextStatic
ofStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
tryStatic
ofReadonly
tryReadonly
tryStatic
pipeStatic
toJSONStatic
toReadonly
[Readonly
TypeStatic
Readonly
_opStatic
Readonly
[Static
Readonly
[Static
Optional
[ignoreStatic
Readonly
[Static
Readonly
[STMTypeStatic
Readonly
[Static
Readonly
[Static
Optional
[typeStatic
Optional
[unifyStatic
Readonly
IdentifierStatic
Readonly
keyStatic
LiveStatic
Readonly
ServiceStatic
Optional
Readonly
stackStatic
[Static
[iterator]Static
[Static
contextReadonly
now: Effect<number & Brand<"Millis">, TimestampTimeOutOfRangeError, never>Static
ofReadonly
now: Effect<number & Brand<"Millis">, TimestampTimeOutOfRangeError, never>Readonly
now: Effect<number & Brand<"Millis">, TimestampTimeOutOfRangeError, never>Static
pipeStatic
toJSONStatic
toA helper for casting types not supported by SQLite. SQLite doesn't support +Date nor Boolean types, so Evolu emulates them with SqliteBoolean and +SqliteDate.
+Create SQLite indexes.
+ +Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+Optional
defaultSize: numberOptional
size: numberOptional
size: numberCreate database schema.
+Tables with a name prefixed with _ are local-only, which means they are not +synced. Local-only tables are useful for device-specific or temporal data.
+We detect only changes in the whole result and in-place edits. In the future, +we will add more heuristics. We will probably not implement the Myers diff +algorithm because it's faster to rerender all than to compute many detailed +patches. We will only implement logic a developer would implement manually, +if necessary.
+This function mutates for better performance.
+Parse a string to Mnemonic.
+Create table schema.
+Supported types are null, string, number, Uint8Array, JSON Object, and JSON +Array. Use SqliteDate for dates and SqliteBoolean for booleans.
+Reserved columns are createdAt, updatedAt, isDeleted. Those columns are added +by default.
+A React 19 use
polyfill.
See https://react.dev/reference/react/use. See +https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
+React Hook returning an instance of Evolu.
+Please don't use it without the generic parameter.
+Subscribe to EvoluError changes.
+The same as useQuery, but for many queries.
+Load and subscribe to the Query, and return an object with rows
and row
+properties that are automatically updated when data changes.
Note that useQuery uses React Suspense. It means every usage of +useQuery blocks rendering until loading is completed. To avoid loading +waterfall with more queries, use useQueries.
+// Get all rows.
const { rows } = useQuery(allTodos);
// Get the first row (it can be null).
const { row } = useQuery(todoById(1));
// Get all rows, but without subscribing to changes.
const { rows } = useQuery(allTodos, { once: true });
// Prefetch rows.
const allTodos = evolu.createQuery((db) =>
db.selectFrom("todo").selectAll(),
);
const allTodosPromise = evolu.loadQuery(allTodos);
// Use prefetched rows.
const { rows } = useQuery(allTodos, { promise: allTodosPromise });
+
+
+Subscribe to Query QueryResult changes.
+Create Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/common-web";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+A helper for casting types not supported by SQLite. SQLite doesn't support +Date nor Boolean types, so Evolu emulates them with SqliteBoolean and +SqliteDate.
+Create Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Create SQLite indexes.
+ +Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+Create database schema.
+Tables with a name prefixed with _ are local-only, which means they are not +synced. Local-only tables are useful for device-specific or temporal data.
+A SQLite helper for aggregating a subquery into a JSON array.
+NOTE: This helper only works correctly if you've installed the ParseJSONResultsPlugin
.
+Otherwise the nested selections will be returned as JSON strings.
The plugin can be installed like this:
+const db = new Kysely({
dialect: new SqliteDialect(config),
plugins: [new ParseJSONResultsPlugin()]
})
+
+
+const result = await db
.selectFrom('person')
.select((eb) => [
'id',
jsonArrayFrom(
eb.selectFrom('pet')
.select(['pet.id as pet_id', 'pet.name'])
.whereRef('pet.owner_id', '=', 'person.id')
.orderBy('pet.name')
).as('pets')
])
.execute()
result[0].id
result[0].pets[0].pet_id
result[0].pets[0].name
+
+
+The generated SQL (SQLite):
+select "id", (
+ select coalesce(json_group_array(json_object(
+ 'pet_id', "agg"."pet_id",
+ 'name', "agg"."name"
+ )), '[]') from (
+ select "pet"."id" as "pet_id", "pet"."name"
+ from "pet"
+ where "pet"."owner_id" = "person"."id"
+ order by "pet"."name"
+ ) as "agg"
+) as "pets"
+from "person"
+
+
+A SQLite helper for turning a subquery into a JSON object.
+The subquery must only return one row.
+NOTE: This helper only works correctly if you've installed the ParseJSONResultsPlugin
.
+Otherwise the nested selections will be returned as JSON strings.
The plugin can be installed like this:
+const db = new Kysely({
dialect: new SqliteDialect(config),
plugins: [new ParseJSONResultsPlugin()]
})
+
+
+const result = await db
.selectFrom('person')
.select((eb) => [
'id',
jsonObjectFrom(
eb.selectFrom('pet')
.select(['pet.id as pet_id', 'pet.name'])
.whereRef('pet.owner_id', '=', 'person.id')
.where('pet.is_favorite', '=', true)
).as('favorite_pet')
])
.execute()
result[0].id
result[0].favorite_pet.pet_id
result[0].favorite_pet.name
+
+
+The generated SQL (SQLite):
+select "id", (
+ select json_object(
+ 'pet_id', "obj"."pet_id",
+ 'name', "obj"."name"
+ ) from (
+ select "pet"."id" as "pet_id", "pet"."name"
+ from "pet"
+ where "pet"."owner_id" = "person"."id"
+ and "pet"."is_favorite" = ?
+ ) as obj
+) as "favorite_pet"
+from "person";
+
+
+Parse a string to Mnemonic.
+Template tag for creating raw SQL snippets and queries.
+import { sql } from 'kysely'
const id = 123
const snippet = sql<Person[]>`select * from person where id = ${id}`
+
+
+Substitutions (the things inside ${}
) are automatically passed to the database
+as parameters and are never interpolated to the SQL string. There's no need to worry
+about SQL injection vulnerabilities. Substitutions can be values, other sql
+expressions, queries and almost anything else Kysely can produce and they get
+handled correctly. See the examples below.
If you need your substitutions to be interpreted as identifiers, value literals or +lists of things, see the Sql.ref, Sql.table, Sql.id, +Sql.lit, Sql.raw and Sql.join functions.
+You can pass sql snippets returned by the sql
tag pretty much anywhere. Whenever
+something can't be done using the Kysely API, you should be able to drop down to
+raw SQL using the sql
tag. Here's an example query that uses raw sql in a bunch
+of methods:
const persons = await db
.selectFrom('person')
.select(
// If you use `sql` in a select statement, remember to call the `as`
// method to give it an alias.
sql<string>`concat(first_name, ' ', last_name)`.as('full_name')
)
.where(sql<boolean>`birthdate between ${date1} and ${date2}`)
// Here we assume we have list of nicknames for the person
// (a list of strings) and we use the PostgreSQL `@>` operator
// to test if all of them are valid nicknames for the user.
.where('nicknames', '@>', sql<string[]>`ARRAY[${sql.join(nicknames)}]`)
.orderBy(sql<string>`concat(first_name, ' ', last_name)`)
.execute()
+
+
+The generated SQL (PostgreSQL):
+select concat(first_name, ' ', last_name) as "full_name"
+from "person"
+where birthdate between $1 and $2
+and "nicknames" @> ARRAY[$3, $4, $5, $6, $7, $8, $9, $10]
+order by concat(first_name, ' ', last_name)
+
+
+SQL snippets can be executed by calling the execute
method and passing a Kysely
+instance as the only argument:
const result = await sql<Person[]>`select * from person`.execute(db)
+
+
+You can merge other sql
expressions and queries using substitutions:
const petName = db.selectFrom('pet').select('name').limit(1)
const fullName = sql<string>`concat(first_name, ' ', last_name)`
sql`
select ${fullName} as full_name, ${petName} as pet_name
from person
`
+
+
+Substitutions also handle ExpressionBuilder.ref, +DynamicModule.ref and pretty much anything else you +throw at it. Here's an example of calling a function in a +type-safe way:
+db.selectFrom('person')
.select([
'first_name',
'last_name',
(eb) => {
// The `eb.ref` method is type-safe and only accepts
// column references that are possible.
const firstName = eb.ref('first_name')
const lastName = eb.ref('last_name')
const fullName = sql<string>`concat(${firstName}, ' ', ${lastName})`
return fullName.as('full_name')
}
])
+
+
+don't know if that amount of ceremony is worth the small increase in +type-safety though... But it's possible.
+Rest
...parameters: unknown[]This can be used to add arbitrary identifiers to SQL snippets.
+Does the same thing as ref and table +but can also be used for any other identifiers like index names.
+You should use ref and table +instead of this whenever possible as they produce a more sematic +operation node tree.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const indexName = 'person_first_name_index'
sql`create index ${indexName} on person`
+
+
+The generated SQL (PostgreSQL):
+create index "person_first_name_index" on person
+
+
+Multiple identifiers get separated by dots:
+const schema = 'public'
const columnName = 'first_name'
const table = 'person'
sql`select ${sql.id(schema, table, columnName)}} from ${sql.id(schema, table)}`
+
+
+The generated SQL (PostgreSQL):
+select "public"."person"."first_name" from "public"."person"
+
+
+Rest
...ids: readonly string[]This can be used to add lists of things to SQL snippets.
+function findByNicknames(nicknames: string[]): Promise<Person[]> {
return db
.selectFrom('person')
.selectAll()
.where('nicknames', '@>', sql<string[]>`ARRAY[${sql.join(nicknames)}]`)
.execute()
}
+
+
+The generated SQL (PostgreSQL):
+select * from "person"
+where "nicknames" @> ARRAY[$1, $2, $3, $4, $5, $6, $7, $8]
+
+
+The second argument is the joining SQL expression that defaults +to
+sql`, `
+
+
+In addition to values, items in the list can be also sql +expressions, queries or anything else the normal substitutions +support:
+const things = [
123,
sql`(1 == 1)`,
db.selectFrom('person').selectAll(),
sql.lit(false),
sql.id('first_name')
]
sql`BEFORE ${sql.join(things, sql`::varchar, `)} AFTER`
+
+
+The generated SQL (PostgreSQL):
+BEFORE $1::varchar, (1 == 1)::varchar, (select * from "person")::varchar, false::varchar, "first_name" AFTER
+
+
+Optional
separator: RawBuilder<any>This can be used to add literal values to SQL snippets.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way. +You almost always want to use normal substitutions that get sent to the +db as parameters.
+const firstName = 'first_name'
sql`select * from person where first_name = ${sql.lit(firstName)}`
+
+
+The generated SQL (PostgreSQL):
+select * from person where first_name = 'first_name'
+
+
+As you can see from the example above, the value was added directly to +the SQL string instead of as a parameter. Only use this function when +something can't be sent as a parameter.
+This can be used to add arbitrary runtime SQL to SQL snippets.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const firstName = "'first_name'"
sql`select * from person where first_name = ${sql.raw(firstName)}`
+
+
+The generated SQL (PostgreSQL):
+select * from person where first_name = 'first_name'
+
+
+Note that the difference to sql.lit
is that this function
+doesn't assume the inputs are values. The input to this function
+can be any sql and it's simply glued to the parent string as-is.
This can be used to add runtime column references to SQL snippets.
+By default ${}
substitutions in sql template strings get
+transformed into parameters. You can use this function to tell
+Kysely to interpret them as column references instead.
WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const columnRef = 'first_name'
sql`select ${sql.ref(columnRef)} from person`
+
+
+The generated SQL (PostgreSQL):
+select "first_name" from person
+
+
+The refefences can also include a table name:
+const columnRef = 'person.first_name'
sql`select ${sql.ref(columnRef)}} from person`
+
+
+The generated SQL (PostgreSQL):
+select "person"."first_name" from person
+
+
+The refefences can also include a schema on supported databases:
+const columnRef = 'public.person.first_name'
sql`select ${sql.ref(columnRef)}} from person`
+
+
+The generated SQL (PostgreSQL):
+select "public"."person"."first_name" from person
+
+
+This can be used to add runtime table references to SQL snippets.
+By default ${}
substitutions in sql template strings get
+transformed into parameters. You can use this function to tell
+Kysely to interpret them as table references instead.
WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const table = 'person'
sql`select first_name from ${sql.table(table)}`
+
+
+The generated SQL (PostgreSQL):
+select first_name from "person"
+
+
+The refefences can also include a schema on supported databases:
+const table = 'public.person'
sql`select first_name from ${sql.table(table)}`
+
+
+The generated SQL (PostgreSQL):
+select first_name from "public"."person"
+
+
+Create table schema.
+Supported types are null, string, number, Uint8Array, JSON Object, and JSON +Array. Use SqliteDate for dates and SqliteBoolean for booleans.
+Reserved columns are createdAt, updatedAt, isDeleted. Those columns are added +by default.
+React Hook returning an instance of Evolu.
+Please don't use it without the generic parameter.
+Subscribe to EvoluError changes.
+The same as useQuery, but for many queries.
+Optional
options: Partial<{ Load and subscribe to the Query, and return an object with rows
and row
+properties that are automatically updated when data changes.
Note that useQuery uses React Suspense. It means every usage of +useQuery blocks rendering until loading is completed. To avoid loading +waterfall with more queries, use useQueries.
+// Get all rows.
const { rows } = useQuery(allTodos);
// Get the first row (it can be null).
const { row } = useQuery(todoById(1));
// Get all rows, but without subscribing to changes.
const { rows } = useQuery(allTodos, { once: true });
// Prefetch rows.
const allTodos = evolu.createQuery((db) =>
db.selectFrom("todo").selectAll(),
);
const allTodosPromise = evolu.loadQuery(allTodos);
// Use prefetched rows.
const { rows } = useQuery(allTodos, { promise: allTodosPromise });
+
+
+Optional
options: Partial<{ Subscribe to Query QueryResult changes.
+A helper for casting types not supported by SQLite. SQLite doesn't support +Date nor Boolean types, so Evolu emulates them with SqliteBoolean and +SqliteDate.
+Create Evolu from the database schema.
+Tables with a name prefixed with _
are local-only, which means they are
+never synced. It's useful for device-specific or temporal data.
import * as S from "@effect/schema/Schema";
import * as E from "@evolu/react-native";
const TodoId = E.id("Todo");
type TodoId = typeof TodoId.Type;
const TodoTable = E.table({
id: TodoId,
title: E.NonEmptyString1000,
});
type TodoTable = typeof TodoTable.Type;
const Database = E.database({
todo: TodoTable,
// Prefix `_` makes the table local-only (it will not sync)
_todo: TodoTable,
});
type Database = typeof Database.Type;
const evolu = E.createEvolu(Database);
+
+
+Create SQLite indexes.
+ +Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+Create database schema.
+Tables with a name prefixed with _ are local-only, which means they are not +synced. Local-only tables are useful for device-specific or temporal data.
+A SQLite helper for aggregating a subquery into a JSON array.
+NOTE: This helper only works correctly if you've installed the ParseJSONResultsPlugin
.
+Otherwise the nested selections will be returned as JSON strings.
The plugin can be installed like this:
+const db = new Kysely({
dialect: new SqliteDialect(config),
plugins: [new ParseJSONResultsPlugin()]
})
+
+
+const result = await db
.selectFrom('person')
.select((eb) => [
'id',
jsonArrayFrom(
eb.selectFrom('pet')
.select(['pet.id as pet_id', 'pet.name'])
.whereRef('pet.owner_id', '=', 'person.id')
.orderBy('pet.name')
).as('pets')
])
.execute()
result[0].id
result[0].pets[0].pet_id
result[0].pets[0].name
+
+
+The generated SQL (SQLite):
+select "id", (
+ select coalesce(json_group_array(json_object(
+ 'pet_id', "agg"."pet_id",
+ 'name', "agg"."name"
+ )), '[]') from (
+ select "pet"."id" as "pet_id", "pet"."name"
+ from "pet"
+ where "pet"."owner_id" = "person"."id"
+ order by "pet"."name"
+ ) as "agg"
+) as "pets"
+from "person"
+
+
+A SQLite helper for turning a subquery into a JSON object.
+The subquery must only return one row.
+NOTE: This helper only works correctly if you've installed the ParseJSONResultsPlugin
.
+Otherwise the nested selections will be returned as JSON strings.
The plugin can be installed like this:
+const db = new Kysely({
dialect: new SqliteDialect(config),
plugins: [new ParseJSONResultsPlugin()]
})
+
+
+const result = await db
.selectFrom('person')
.select((eb) => [
'id',
jsonObjectFrom(
eb.selectFrom('pet')
.select(['pet.id as pet_id', 'pet.name'])
.whereRef('pet.owner_id', '=', 'person.id')
.where('pet.is_favorite', '=', true)
).as('favorite_pet')
])
.execute()
result[0].id
result[0].favorite_pet.pet_id
result[0].favorite_pet.name
+
+
+The generated SQL (SQLite):
+select "id", (
+ select json_object(
+ 'pet_id', "obj"."pet_id",
+ 'name', "obj"."name"
+ ) from (
+ select "pet"."id" as "pet_id", "pet"."name"
+ from "pet"
+ where "pet"."owner_id" = "person"."id"
+ and "pet"."is_favorite" = ?
+ ) as obj
+) as "favorite_pet"
+from "person";
+
+
+Parse a string to Mnemonic.
+Template tag for creating raw SQL snippets and queries.
+import { sql } from 'kysely'
const id = 123
const snippet = sql<Person[]>`select * from person where id = ${id}`
+
+
+Substitutions (the things inside ${}
) are automatically passed to the database
+as parameters and are never interpolated to the SQL string. There's no need to worry
+about SQL injection vulnerabilities. Substitutions can be values, other sql
+expressions, queries and almost anything else Kysely can produce and they get
+handled correctly. See the examples below.
If you need your substitutions to be interpreted as identifiers, value literals or +lists of things, see the Sql.ref, Sql.table, Sql.id, +Sql.lit, Sql.raw and Sql.join functions.
+You can pass sql snippets returned by the sql
tag pretty much anywhere. Whenever
+something can't be done using the Kysely API, you should be able to drop down to
+raw SQL using the sql
tag. Here's an example query that uses raw sql in a bunch
+of methods:
const persons = await db
.selectFrom('person')
.select(
// If you use `sql` in a select statement, remember to call the `as`
// method to give it an alias.
sql<string>`concat(first_name, ' ', last_name)`.as('full_name')
)
.where(sql<boolean>`birthdate between ${date1} and ${date2}`)
// Here we assume we have list of nicknames for the person
// (a list of strings) and we use the PostgreSQL `@>` operator
// to test if all of them are valid nicknames for the user.
.where('nicknames', '@>', sql<string[]>`ARRAY[${sql.join(nicknames)}]`)
.orderBy(sql<string>`concat(first_name, ' ', last_name)`)
.execute()
+
+
+The generated SQL (PostgreSQL):
+select concat(first_name, ' ', last_name) as "full_name"
+from "person"
+where birthdate between $1 and $2
+and "nicknames" @> ARRAY[$3, $4, $5, $6, $7, $8, $9, $10]
+order by concat(first_name, ' ', last_name)
+
+
+SQL snippets can be executed by calling the execute
method and passing a Kysely
+instance as the only argument:
const result = await sql<Person[]>`select * from person`.execute(db)
+
+
+You can merge other sql
expressions and queries using substitutions:
const petName = db.selectFrom('pet').select('name').limit(1)
const fullName = sql<string>`concat(first_name, ' ', last_name)`
sql`
select ${fullName} as full_name, ${petName} as pet_name
from person
`
+
+
+Substitutions also handle ExpressionBuilder.ref, +DynamicModule.ref and pretty much anything else you +throw at it. Here's an example of calling a function in a +type-safe way:
+db.selectFrom('person')
.select([
'first_name',
'last_name',
(eb) => {
// The `eb.ref` method is type-safe and only accepts
// column references that are possible.
const firstName = eb.ref('first_name')
const lastName = eb.ref('last_name')
const fullName = sql<string>`concat(${firstName}, ' ', ${lastName})`
return fullName.as('full_name')
}
])
+
+
+don't know if that amount of ceremony is worth the small increase in +type-safety though... But it's possible.
+Rest
...parameters: unknown[]This can be used to add arbitrary identifiers to SQL snippets.
+Does the same thing as ref and table +but can also be used for any other identifiers like index names.
+You should use ref and table +instead of this whenever possible as they produce a more sematic +operation node tree.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const indexName = 'person_first_name_index'
sql`create index ${indexName} on person`
+
+
+The generated SQL (PostgreSQL):
+create index "person_first_name_index" on person
+
+
+Multiple identifiers get separated by dots:
+const schema = 'public'
const columnName = 'first_name'
const table = 'person'
sql`select ${sql.id(schema, table, columnName)}} from ${sql.id(schema, table)}`
+
+
+The generated SQL (PostgreSQL):
+select "public"."person"."first_name" from "public"."person"
+
+
+Rest
...ids: readonly string[]This can be used to add lists of things to SQL snippets.
+function findByNicknames(nicknames: string[]): Promise<Person[]> {
return db
.selectFrom('person')
.selectAll()
.where('nicknames', '@>', sql<string[]>`ARRAY[${sql.join(nicknames)}]`)
.execute()
}
+
+
+The generated SQL (PostgreSQL):
+select * from "person"
+where "nicknames" @> ARRAY[$1, $2, $3, $4, $5, $6, $7, $8]
+
+
+The second argument is the joining SQL expression that defaults +to
+sql`, `
+
+
+In addition to values, items in the list can be also sql +expressions, queries or anything else the normal substitutions +support:
+const things = [
123,
sql`(1 == 1)`,
db.selectFrom('person').selectAll(),
sql.lit(false),
sql.id('first_name')
]
sql`BEFORE ${sql.join(things, sql`::varchar, `)} AFTER`
+
+
+The generated SQL (PostgreSQL):
+BEFORE $1::varchar, (1 == 1)::varchar, (select * from "person")::varchar, false::varchar, "first_name" AFTER
+
+
+Optional
separator: RawBuilder<any>This can be used to add literal values to SQL snippets.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way. +You almost always want to use normal substitutions that get sent to the +db as parameters.
+const firstName = 'first_name'
sql`select * from person where first_name = ${sql.lit(firstName)}`
+
+
+The generated SQL (PostgreSQL):
+select * from person where first_name = 'first_name'
+
+
+As you can see from the example above, the value was added directly to +the SQL string instead of as a parameter. Only use this function when +something can't be sent as a parameter.
+This can be used to add arbitrary runtime SQL to SQL snippets.
+WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const firstName = "'first_name'"
sql`select * from person where first_name = ${sql.raw(firstName)}`
+
+
+The generated SQL (PostgreSQL):
+select * from person where first_name = 'first_name'
+
+
+Note that the difference to sql.lit
is that this function
+doesn't assume the inputs are values. The input to this function
+can be any sql and it's simply glued to the parent string as-is.
This can be used to add runtime column references to SQL snippets.
+By default ${}
substitutions in sql template strings get
+transformed into parameters. You can use this function to tell
+Kysely to interpret them as column references instead.
WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const columnRef = 'first_name'
sql`select ${sql.ref(columnRef)} from person`
+
+
+The generated SQL (PostgreSQL):
+select "first_name" from person
+
+
+The refefences can also include a table name:
+const columnRef = 'person.first_name'
sql`select ${sql.ref(columnRef)}} from person`
+
+
+The generated SQL (PostgreSQL):
+select "person"."first_name" from person
+
+
+The refefences can also include a schema on supported databases:
+const columnRef = 'public.person.first_name'
sql`select ${sql.ref(columnRef)}} from person`
+
+
+The generated SQL (PostgreSQL):
+select "public"."person"."first_name" from person
+
+
+This can be used to add runtime table references to SQL snippets.
+By default ${}
substitutions in sql template strings get
+transformed into parameters. You can use this function to tell
+Kysely to interpret them as table references instead.
WARNING! Using this with unchecked inputs WILL lead to SQL injection +vulnerabilities. The input is not checked or escaped by Kysely in any way.
+const table = 'person'
sql`select first_name from ${sql.table(table)}`
+
+
+The generated SQL (PostgreSQL):
+select first_name from "person"
+
+
+The refefences can also include a schema on supported databases:
+const table = 'public.person'
sql`select first_name from ${sql.table(table)}`
+
+
+The generated SQL (PostgreSQL):
+select first_name from "public"."person"
+
+
+Create table schema.
+Supported types are null, string, number, Uint8Array, JSON Object, and JSON +Array. Use SqliteDate for dates and SqliteBoolean for booleans.
+Reserved columns are createdAt, updatedAt, isDeleted. Those columns are added +by default.
+React Hook returning an instance of Evolu.
+Please don't use it without the generic parameter.
+Subscribe to EvoluError changes.
+The same as useQuery, but for many queries.
+Optional
options: Partial<{ Load and subscribe to the Query, and return an object with rows
and row
+properties that are automatically updated when data changes.
Note that useQuery uses React Suspense. It means every usage of +useQuery blocks rendering until loading is completed. To avoid loading +waterfall with more queries, use useQueries.
+// Get all rows.
const { rows } = useQuery(allTodos);
// Get the first row (it can be null).
const { row } = useQuery(todoById(1));
// Get all rows, but without subscribing to changes.
const { rows } = useQuery(allTodos, { once: true });
// Prefetch rows.
const allTodos = evolu.createQuery((db) =>
db.selectFrom("todo").selectAll(),
);
const allTodosPromise = evolu.loadQuery(allTodos);
// Use prefetched rows.
const { rows } = useQuery(allTodos, { promise: allTodosPromise });
+
+
+Optional
options: Partial<{ Subscribe to Query QueryResult changes.
+Optional
port: numberLocal-first platform designed for privacy, ease of use, and no vendor lock-in
+NonEmptyString1000
, PositiveInt
, etc.)Local-first apps allow users to own their data by storing them on their devices. Modern browsers provide API designed precisely for that. How is it different from keeping files on disk? Files are not the right abstraction for apps and cannot synchronize among devices. That's why traditional apps use the client-server architecture. But using client-server architecture also means that users' ability to use an app depends on some server that can be offline, temporarily or forever, if a company decides to ban a user or even goes bankrupt. That's unfortunate. Luckily, a way to restore data ownership exists. It's Evolu.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +Evolu monorepo uses pnpm.
+Install the dependencies with:
+pnpm install
+
+
+Build Evolu monorepo:
+pnpm build
+
+
+Start developing and watch for code changes:
+pnpm dev
+
+
+Start iOS developing (pnpm dev must be running too):
+pnpm ios
+
+
+Lint and tests:
+pnpm lint test
+
+
+Describe changes for release log:
+pnpm changeset
+
+
+Optional
enableEnables websocket connection
+If set to true runs a websocket connection on the sync url
+Optional
externalAllows to reuse websocket connections for multiple database setup
+Works INSTEAD of enableWebsocket and if set overrides enableWebsocket this +attaches the database to a connection produced by a call back from another +database that has enableWebsocketConnection
+Maximum physical clock drift allowed in ms.
+The default value is 5 * 60 * 1000 (5 minutes).
+Setting the minimum log level. The default value is none
.
For development, use trace
to log all events and debug
to log only
+events with values. For production, use warning
.
Evolu is multitenant - it can run more instances concurrently. Every Evolu +instance has to have its own unique name. Database files are separated and +invisible to each other.
+The default value is: Evolu
.
URL to reload browser tabs after Owner reset or restore.
+The default value is /
.
URL for Evolu sync and backup server.
+The default value is https://evolu.world
.
Readonly
ensureReadonly
exportReadonly
initReadonly
loadReadonly
mutateReadonly
resetReadonly
restoreReadonly
syncThe Evolu interface provides a type-safe SQL query building and state +management defined by a database schema. It leverages Kysely for creating SQL +queries in TypeScript, enabling operations such as data querying, loading, +subscription to data changes, and mutations (create, update, createOrUpdate). +It also includes functionalities for error handling, syncing state +management, and owner data manipulation. Specifically, Evolu allows:
+Create a row in the database and returns a new ID. The first argument is +the table name, and the second is an object.
+The third optional argument, the onComplete callback, is generally +unnecessary because creating a row cannot fail. Still, UI libraries can use +it to ensure the DOM is updated if we want to manipulate it, for example, +to focus an element.
+Evolu does not use SQL for mutations to ensure data can be safely and +predictably merged without conflicts.
+Explicit mutations also allow Evolu to automatically add and update a few
+useful columns common to all tables. Those columns are: createdAt
,
+updatedAt
, and isDeleted
.
Create or update a row in the database and return the existing ID. The +first argument is the table name, and the second is an object.
+This function is useful when we already have an id
and want to create a
+new row or update an existing one.
The third optional argument, the onComplete callback, is generally +unnecessary because updating a row cannot fail. Still, UI libraries can use +it to ensure the DOM is updated if we want to manipulate it, for example, +to focus an element.
+Evolu does not use SQL for mutations to ensure data can be safely and +predictably merged without conflicts.
+Explicit mutations also allow Evolu to automatically add and update a few
+useful columns common to all tables. Those columns are: createdAt
,
+updatedAt
, and isDeleted
.
Readonly
createCreate type-safe SQL Query.
+Evolu uses Kysely - the type-safe SQL query builder for TypeScript. See +https://kysely.dev.
+ +Readonly
ensureEnsure tables and columns defined in EvoluSchema exist in the +database.
+This function is for hot/live reloading.
+Readonly
exportExport SQLite database as Uint8Array.
+Readonly
getGet EvoluError.
+Readonly
getGet Owner.
+Readonly
getGet Query QueryResult.
+Readonly
getGet SyncState.
+Readonly
loadLoad an array of Query queries and return an array of
+QueryResult promises. It's like queries.map(loadQuery)
but with
+proper types for returned promises.
Readonly
loadLoad Query and return a promise with QueryResult.
+A returned promise always resolves successfully because there is no reason +why loading should fail. All data are local, and the query is typed. A +serious unexpected Evolu error shall be handled with +subscribeError.
+Loading is batched, and returned promises are cached, so there is no need +for an additional cache. Evolu's internal cache is invalidated on +mutation.
+The returned promise is enriched with special status and value properties
+for the upcoming React use
Hook, but other UI libraries can also leverage
+them. Speaking of React, there are two essential React Suspense-related
+patterns that every developer should be aware of—passing promises to
+children and caching over mutations.
With promises passed to children, we can load a query as soon as possible, +but we don't have to use the returned promise immediately. That's useful +for prefetching, which is generally not necessary for local-first apps but +can be if a query takes a long time to load.
+Caching over mutation is a pattern that every developer should know. As we +said, Evolu caches promise until a mutation happens. A query loaded after +that will return a new pending promise. That's okay for general usage but +not for UI with React Suspense because a mutation would suspend rerendered +queries on a page, and that's not a good UX.
+We call this pattern "caching over mutation" because it has no globally +accepted name yet. React RFC for React Cache does not exist yet.
+For better UX, a query must be subscribed for updates. This way, instead of +Suspense flashes, the user sees new data immediately because Evolu replaces +cached promises with fresh, already resolved new ones.
+If you are curious why Evolu does not do that for all queries by default, +the answer is simple: performance. Tracking changes is costly and +meaningful only for visible (hence subscribed) queries anyway. To subscribe +to a query, use subscribeQuery.
+Readonly
reloadReload the app in a platform-specific way. For browsers, this will reload +all tabs using Evolu. For native apps, it will restart the app.
+Readonly
resetDelete Owner and all their data from the current device. After the +deletion, Evolu will purge the application state. For browsers, this will +reload all tabs using Evolu. For native apps, it will restart the app.
+Reloading can be turned off via options if you want to provide a different +UX.
+Readonly
restoreRestore Owner with all their synced data. It uses resetOwner, +so be careful.
+Readonly
socketReadonly
subscribeSubscribe to EvoluError changes.
+Readonly
subscribeSubscribe to Owner changes.
+Readonly
subscribeSubscribe to Query QueryResult changes.
+Readonly
subscribeSubscribe to SyncState changes.
+Update a row in the database and return the existing ID. The first argument +is the table name, and the second is an object.
+The third optional argument, the onComplete callback, is generally +unnecessary because updating a row cannot fail. Still, UI libraries can use +it to ensure the DOM is updated if we want to manipulate it, for example, +to focus an element.
+Evolu does not use SQL for mutations to ensure data can be safely and +predictably merged without conflicts.
+Explicit mutations also allow Evolu to automatically add and update a few
+useful columns common to all tables. Those columns are: createdAt
,
+updatedAt
, and isDeleted
.
import * as S from "@effect/schema/Schema";
// Evolu uses the Schema to enforce domain model.
const title = S.decodeSync(Evolu.NonEmptyString1000)("A title");
evolu.update("todo", { id, title });
// To delete a row, set `isDeleted` to true.
evolu.update("todo", { id, isDeleted: true });
+
+
+Optional
enableEnables websocket connection
+If set to true runs a websocket connection on the sync url
+Optional
externalAllows to reuse websocket connections for multiple database setup
+Works INSTEAD of enableWebsocket and if set overrides enableWebsocket this +attaches the database to a connection produced by a call back from another +database that has enableWebsocketConnection
+Use the indexes
option to define SQLite indexes.
Table and column names are not typed because Kysely doesn't support it.
+https://medium.com/@JasonWyatt/squeezing-performance-from-sqlite-indexes-indexes-c4e175f3c346
+Use this option to create initial data (fixtures).
+Maximum physical clock drift allowed in ms.
+The default value is 5 * 60 * 1000 (5 minutes).
+Setting the minimum log level. The default value is none
.
For development, use trace
to log all events and debug
to log only
+events with values. For production, use warning
.
Use this option to create Evolu with the specified mnemonic. If omitted, +the mnemonic will be autogenerated. That should be the default behavior +until special UX requirements are needed (e.g., multitenancy).
+Evolu is multitenant - it can run more instances concurrently. Every Evolu +instance has to have its own unique name. Database files are separated and +invisible to each other.
+The default value is: Evolu
.
URL to reload browser tabs after Owner reset or restore.
+The default value is /
.
URL for Evolu sync and backup server.
+The default value is https://evolu.world
.
It's actually not Merkle Tree but a Merkleized prefix tree, aka Merkle Trie. +https://decomposition.al/blog/2019/05/31/how-i-learned-about-merklix-trees-without-having-to-become-a-cryptocurrency-enthusiast
+The Owner represents the Evolu database owner, a user. Instead of traditional +email with a password, the Owner uses a mnemonic, also known as a "seed +phrase," which is a set of 12 words in a specific order chosen from a +predefined list.
+The purpose of the BIP39 mnemonic is to provide a human-readable way of +storing a private key.
+Mnemonic is generated safely in the user's device and must not be shared with +anyone.
+An object with rows and row properties.
+Readonly
execReadonly
exportReadonly
transactionUse exclusive
for mutations and shared
for read-only queries. This
+shared/exclusive lock pattern allows multiple simultaneous readers but
+only one writer. In Evolu, this pattern also ensures that every write can
+be immediately read without waiting to complete. For example, we can add
+data on one page and then immediately redirect to another, and the data
+will be there.
There is also a last
mode that ensures no other transaction can run.
+It's for Db reset to ensure no data are accidentally saved after database
+wipe-out.
UnexpectedError represents errors that can occur unexpectedly anywhere, even +in third-party libraries, because Evolu uses Effect to track all errors.
+The Owner represents the Evolu database owner, a user. Instead of traditional +email with a password, the Owner uses a mnemonic, also known as a "seed +phrase," which is a set of 12 words in a specific order chosen from a +predefined list.
+The purpose of the BIP39 mnemonic is to provide a human-readable way of +storing a private key.
+Mnemonic is generated safely in the user's device and must not be shared with +anyone.
+An object with rows and row properties.
+UnexpectedError represents errors that can occur unexpectedly anywhere, even +in third-party libraries, because Evolu uses Effect to track all errors.
+The Owner represents the Evolu database owner, a user. Instead of traditional +email with a password, the Owner uses a mnemonic, also known as a "seed +phrase," which is a set of 12 words in a specific order chosen from a +predefined list.
+The purpose of the BIP39 mnemonic is to provide a human-readable way of +storing a private key.
+Mnemonic is generated safely in the user's device and must not be shared with +anyone.
+An object with rows and row properties.
+UnexpectedError represents errors that can occur unexpectedly anywhere, even +in third-party libraries, because Evolu uses Effect to track all errors.
+Evolu Server database schema.
+Readonly
initCreate database tables and indexes if they do not exist.
+Readonly
syncSync data.
+Local-first platform designed for privacy, ease of use, and no vendor lock-in
+NonEmptyString1000
, PositiveInt
, etc.)Local-first apps allow users to own their data by storing them on their devices. Modern browsers provide API designed precisely for that. How is it different from keeping files on disk? Files are not the right abstraction for apps and cannot synchronize among devices. That's why traditional apps use the client-server architecture. But using client-server architecture also means that users' ability to use an app depends on some server that can be offline, temporarily or forever, if a company decides to ban a user or even goes bankrupt. That's unfortunate. Luckily, a way to restore data ownership exists. It's Evolu.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +Evolu monorepo uses pnpm.
+Install the dependencies with:
+pnpm install
+
+
+Build Evolu monorepo:
+pnpm build
+
+
+Start developing and watch for code changes:
+pnpm dev
+
+
+Start iOS developing (pnpm dev must be running too):
+pnpm ios
+
+
+Lint and tests:
+pnpm lint test
+
+
+Describe changes for release log:
+pnpm changeset
+
+
+Common code for Evolu React and Evolu React Native.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +Common code for Evolu React and other UI libraries for the web.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +Evolu for React Native.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +Evolu Server for Evolu library.
+For detailed information and usage examples, please visit evolu.dev.
+The Evolu community is on GitHub Discussions, where you can ask questions and voice ideas.
+To chat with other community members, you can join the Evolu Discord.
+ +The EvoluError type is used to represent errors that can occur in Evolu.
+FlushSync is a service for libraries like React to synchronously flush +updates inside the provided callback to ensure the DOM is updated +immediately.
+ +Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+The SyncState type represents the various states that a synchronization +process can be in.
+The TimestampError type represents all Timestamp-related errors. If such an +error happens, the device clock is skewed and should be set to the current +time.
+The EvoluError type is used to represent errors that can occur in Evolu.
+Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+A type constant for marking a column as not null. Can be used with $narrowPartial
.
Example:
+const person = await db.selectFrom('person')
.where('nullable_column', 'is not', null)
.selectAll()
.$narrowType<{ nullable_column: NotNull }>()
.executeTakeFirstOrThrow()
+
+
+The SyncState type represents the various states that a synchronization +process can be in.
+The TimestampError type represents all Timestamp-related errors. If such an +error happens, the device clock is skewed and should be set to the current +time.
+The EvoluError type is used to represent errors that can occur in Evolu.
+Mnemonic is a password generated by Evolu in BIP39 format.
+A mnemonic, also known as a "seed phrase," is a set of 12 words in a specific +order chosen from a predefined list. The purpose of the BIP39 mnemonic is to +provide a human-readable way of storing a private key.
+A type constant for marking a column as not null. Can be used with $narrowPartial
.
Example:
+const person = await db.selectFrom('person')
.where('nullable_column', 'is not', null)
.selectAll()
.$narrowType<{ nullable_column: NotNull }>()
.executeTakeFirstOrThrow()
+
+
+The SyncState type represents the various states that a synchronization +process can be in.
+The TimestampError type represents all Timestamp-related errors. If such an +error happens, the device clock is skewed and should be set to the current +time.
+Evolu Server Kysely instance. Use only PostgreSQL or SQLite dialects for now. +https://kysely-org.github.io/kysely-apidoc/classes/InsertQueryBuilder.html#onConflict
+Const
Const
Const
Branded Id Schema. To create Id Schema for a specific table, use id.
+Const
Const
Millis represents a time that is valid for usage with the Merkle tree. It +must be between Apr 13, 1997, and Nov 05, 2051, to ensure MinutesBase3 length +equals 16. We can find diff for two Merkle trees only within this range. If +the device clock is out of range, Evolu will not store data until it's +fixed.
+Const
Const
A nonempty string with a maximum length of 1000 characters.
+Const
SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead. +Use the cast helper to cast SqliteBoolean from boolean and back. +https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
+Const
SQLite doesn't support the Date type, so Evolu uses SqliteDate instead. Use +the cast helper to cast SqliteDate from Date and back. +https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
+Const
String schema represents a string that is not stringified JSON. Using String +schema for strings stored in SQLite is crucial to ensure a stored string is +not automatically parsed to a JSON object or array when retrieved. Use String +schema for all string-based schemas.
+Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Const
Branded Id Schema. To create Id Schema for a specific table, use id.
+Const
A nonempty string with a maximum length of 1000 characters.
+Const
SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead. +Use the cast helper to cast SqliteBoolean from boolean and back. +https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
+Const
SQLite doesn't support the Date type, so Evolu uses SqliteDate instead. Use +the cast helper to cast SqliteDate from Date and back. +https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
+Const
String schema represents a string that is not stringified JSON. Using String +schema for strings stored in SQLite is crucial to ensure a stored string is +not automatically parsed to a JSON object or array when retrieved. Use String +schema for all string-based schemas.
+Const
A React 19 use
polyfill.
See https://react.dev/reference/react/use. See +https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
+Const
Const
Branded Id Schema. To create Id Schema for a specific table, use id.
+Const
A nonempty string with a maximum length of 1000 characters.
+Const
SQLite doesn't support the boolean type, so Evolu uses SqliteBoolean instead. +Use the cast helper to cast SqliteBoolean from boolean and back. +https://www.sqlite.org/quirks.html#no_separate_boolean_datatype
+Const
SQLite doesn't support the Date type, so Evolu uses SqliteDate instead. Use +the cast helper to cast SqliteDate from Date and back. +https://www.sqlite.org/quirks.html#no_separate_datetime_datatype
+Const
String schema represents a string that is not stringified JSON. Using String +schema for strings stored in SQLite is crucial to ensure a stored string is +not automatically parsed to a JSON object or array when retrieved. Use String +schema for all string-based schemas.
+Const
A React 19 use
polyfill.
See https://react.dev/reference/react/use. See +https://github.com/acdlite/rfcs/blob/first-class-promises/text/0000-first-class-support-for-promises.md
+Const
Create Evolu from the database schema.
+Tables with a name prefixed with
+_
are local-only, which means they are +never synced. It's useful for device-specific or temporal data.