[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [local‑first/Evolu](https://evolu.dev/docs/api-reference/common/local-first/Evolu) › DeviceEvolu

Defined in: [packages/common/src/local-first/Evolu.ts:2053](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2053)

The instance of an app's device database, for the app's data that belongs to
this device rather than to an AppOwner, such as settings. Its own owner is
the device owner, which never syncs, so it has no [Evolu.appOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#appowner):
local-only tables keep their rows on this device, and a mutation of a synced
table must pass, as its `owner`, an owner the app syncs with
[Evolu.useOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#useowner), such as one for settings shared by every AppOwner. An
AppOwner's data lives only in its own database, which [EvoluApp.remove](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluApp#remove)
deletes, so neither takes an AppOwner. The app's
[EvoluConfig.onOwnerDeleted](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluConfig#onownerdeleted) also runs for an owner deleted in it, in
any tab.

It has no [Evolu.exportDatabase](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#exportdatabase) or [Evolu.deleteDatabase](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#deletedatabase), because
the device database also holds the secret of every AppOwner on this device,
which its type hides. An export would write them all in plaintext, even with
encrypted local data, and a deletion would lose them all, leaving their
databases with nothing to open them. [EvoluApp.remove](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluApp#remove) deletes one
AppOwner's data. The app disposes it, so it can't be disposed alone. Its SQL
isn't traced, because tracing logs values, and those include the secrets.

## Extends

- [`Omit`](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittype-keys)\<[`Evolu`](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu)\<`D`\>,
  \| `"appOwner"`
  \| `"exportDatabase"`
  \| `"deleteDatabase"`
  \| `"useOwner"`
  \| `"insert"`
  \| `"update"`
  \| `"upsert"`
  \| _typeof_ `Symbol.asyncDispose`\>

## Properties

<a id="deleteowner"></a>

### deleteOwner

```ts
readonly deleteOwner: (owner:
  | ShardOwner
  | SharedOwner
| SharedReadonlyOwner) => Promise<Result<void, DeleteOwnerError>>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:1061](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L1061)

Deletes the rows of a [ShardOwner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/ShardOwner), [SharedOwner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/SharedOwner), or
[SharedReadonlyOwner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/SharedReadonlyOwner) from the local database of this [Evolu](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu)
instance on this device, stops syncing the owner, and resolves once the
rows are gone.

The deletion runs after the mutations and requests made before it. It
deletes the owner's rows of every table, local-only tables included, and
its history in one transaction. Then it ends every [Evolu.useOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#useowner)
registration of the owner in every instance of the database, identified by
[Evolu.name](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#name), in every tab, so relays don't bring the rows back, and
calling the [UnuseOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/type-aliases/UnuseOwner) of such a registration does nothing. Then
each instance calls [EvoluConfig.onOwnerDeleted](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluConfig#onownerdeleted), this one before code
awaiting this call continues, and loads its subscribed queries again, which
return the owner's rows until they load.

It deletes only this device's copy. Changes not yet synced are lost. Relays
keep the owner's data, and so do other local databases that use the owner.
A later `useOwner` syncs the data back, and a mutation with the owner
stores its rows again, so stop using the owner first, such as by removing
it from where the app lists it. Encrypted copies in device backups and in
freed storage remain.

It takes no [AppOwner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/AppOwner), whose ID names the database;
[Evolu.deleteDatabase](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#deletedatabase) deletes the whole database instead. An owner
created from the AppOwner's secret has the same ID, so this method throws
for it.

Each call deletes on its own, in call order. It fails with an
[UnknownError](https://evolu.dev/docs/api-reference/common/Error/variables/UnknownError) when SQLite fails the deletion, which then deletes
nothing, and the owner keeps syncing. When the database is refused, it
fails with the refusal's error, as [EvoluError](https://evolu.dev/docs/api-reference/common/local-first/Evolu/type-aliases/EvoluError) describes. It fails
with [EvoluDisposedError](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluDisposedError) when this instance is disposed before the
deletion ends, which may or may not then run.

### Example

```ts
import {
  assertType,
  type DeleteOwnerError,
  type Evolu,
  type Result,
  type ShardOwner,
} from "@evolu/common";

const deleteWallet = (evolu: Evolu, walletOwner: ShardOwner) =>
  evolu.deleteOwner(walletOwner);

assertType<
  Awaited<ReturnType<typeof deleteWallet>>,
  Result<void, DeleteOwnerError>
>();

const _deleteAppOwner = (evolu: Evolu) =>
  // @ts-expect-error deleteOwner takes no AppOwner, which names the database.
  evolu.deleteOwner(evolu.appOwner);
```

#### Inherited from

`Omit.deleteOwner`

---

<a id="devicepersistence"></a>

### devicePersistence

```ts
readonly devicePersistence: Promise<DevicePersistence>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:562](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L562)

Whether this device keeps the database, which [createEvolu](https://evolu.dev/docs/api-reference/common/local-first/Evolu/functions/createEvolu) decides
before it creates the instance.

Apps tell the user when it is `NotPersisted`, because then data that has
not synced is lost when the instance ends. On the web this includes its tab
entering the back-forward cache. Apps must not tell the user that data is
saved on the device when it is `Unknown`.

### Example

```ts
import { assertEqual } from "@evolu/common";
import type { DevicePersistence } from "@evolu/common/local-first";

// The notice for `await evolu.devicePersistence`, or null for none.
const persistenceNotice = (
  devicePersistence: DevicePersistence,
): string | null =>
  devicePersistence === "NotPersisted"
    ? "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab."
    : null;

assertEqual(persistenceNotice("Unknown"), null);
assertEqual(
  persistenceNotice("NotPersisted"),
  "Your data isn't kept on this device. Changes that haven't synced are lost when you close this tab.",
);
```

#### Inherited from

`Omit.devicePersistence`

---

<a id="getmutationsize"></a>

### getMutationSize

```ts
readonly getMutationSize: <TableName>(table: TableName, values: Partial<MutationValues<S[TableName], "update">>) => number & Brand<"NonNaN"> & Brand<"Finite"> & Brand<"Int"> & Brand<"NonNegative"> & Brand<"Positive">;
```

Defined in: [packages/common/src/local-first/Evolu.ts:733](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L733)

Returns the size of a mutation of `table` with `values`, measured as
[maxMutationSize](https://evolu.dev/docs/api-reference/common/local-first/Evolu/variables/maxMutationSize) describes.

Use it to check values that column Types do not bound before mutating, or
to show how much of the limit a mutation uses. It accepts any of the
table's columns, so the values of an insert, update, or upsert fit, and it
ignores `id` and `isDeleted`, which are not columns. Mutations of
local-only tables are measured too, although they are exempt from the
limit.

### Example

```ts
import {
  assertType,
  type Evolu,
  maxMutationSize,
  type NonEmptyTrimmedString100,
  type TestEvoluSchema,
} from "@evolu/common";

const fitsTodo = (
  evolu: Evolu<TestEvoluSchema>,
  title: NonEmptyTrimmedString100,
) => evolu.getMutationSize("todo", { title }) <= maxMutationSize;

assertType<ReturnType<typeof fitsTodo>, boolean>();
```

#### Inherited from

`Omit.getMutationSize`

---

<a id="getqueryrows"></a>

### getQueryRows

```ts
readonly getQueryRows: <R>(query: Query<D, R>) => QueryRows<R>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:917](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L917)

Get [QueryRows](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/QueryRows).

### Example

```ts
import {
  assertType,
  createQueryBuilder,
  testEvoluSchema,
  type TestEvoluSchema,
  type Evolu,
  type QueryRows,
} from "@evolu/common";

const createQuery = createQueryBuilder(testEvoluSchema);
const allTodos = createQuery((db) => db.selectFrom("todo").select("title"));
const getTodos = (evolu: Evolu<TestEvoluSchema>) =>
  evolu.getQueryRows(allTodos);

assertType<ReturnType<typeof getTodos>, QueryRows<typeof allTodos.Row>>();
```

#### Inherited from

`Omit.getQueryRows`

---

<a id="insert"></a>

### insert

```ts
readonly insert: DeviceMutation<D, "insert">;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2073](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2073)

[Evolu.insert](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#insert), as a [DeviceMutation](https://evolu.dev/docs/api-reference/common/local-first/Evolu/type-aliases/DeviceMutation).

---

<a id="loadqueries"></a>

### loadQueries

```ts
readonly loadQueries: <Q>(queries: [...Q[]]) => [...QueriesToQueryRowsPromises<Q>[]];
```

Defined in: [packages/common/src/local-first/Evolu.ts:847](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L847)

Load an array of [Query](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/Query) values and return an array of
[QueryRows](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/QueryRows) promises. It's like `queries.map(loadQuery)` but with
proper types for returned promises.

### Example

```ts
import {
  assertType,
  createQueryBuilder,
  testEvoluSchema,
  type TestEvoluSchema,
  testTodoId,
  type Evolu,
  type QueryRows,
} from "@evolu/common";

const createQuery = createQueryBuilder(testEvoluSchema);
const allTodos = createQuery((db) =>
  db.selectFrom("todo").select(["id", "title"]),
);
const firstTodo = createQuery((db) =>
  db.selectFrom("todo").select("title").where("id", "=", testTodoId),
);
const loadTodoQueries = (evolu: Evolu<TestEvoluSchema>) =>
  evolu.loadQueries([allTodos, firstTodo]);

assertType<
  ReturnType<typeof loadTodoQueries>,
  [
    Promise<QueryRows<typeof allTodos.Row>>,
    Promise<QueryRows<typeof firstTodo.Row>>,
  ]
>();
```

#### Inherited from

`Omit.loadQueries`

---

<a id="loadquery"></a>

### loadQuery

```ts
readonly loadQuery: <R>(query: Query<D, R>, options?: LoadQueryOptions) => Promise<QueryRows<R>>;
```

Defined in: [packages/common/src/local-first/Evolu.ts:805](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L805)

Load [Query](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/Query) and return a promise with [QueryRows](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/QueryRows).

The returned promise rejects with an [UnknownError](https://evolu.dev/docs/api-reference/common/Error/variables/UnknownError) when SQLite fails
the query, for example on a syntax error in a raw SQL fragment or on data
its SQL functions cannot read, such as `json_extract` on text that is not
JSON. Only that query fails: other queries load, mutations are stored, and
[EvoluErrorDep.evoluError](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluErrorDep#evoluerror) is not set. React's `useQuery` throws the
error to the nearest error boundary. If the database is refused, because it
refused startup or its worker failed, an uncached load rejects with an
[UnknownError](https://evolu.dev/docs/api-reference/common/Error/variables/UnknownError), the refusal itself or one wrapping it, and cached
loads stay pending until disposal resolves them with empty rows, so a
suspended component stays suspended. Cached loads that a deletion of the
database dropped stay pending, because the app replaces the instance; see
[Evolu.deleteDatabase](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#deletedatabase). An uncached load never stays pending past
disposal, also the disposal a deletion of the database causes: it resolves
with empty rows, which then say nothing about the database, and the
instance's methods throw. Observe [EvoluErrorDep.evoluError](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluErrorDep#evoluerror) to
display the refusal independently of query loading.

Cached loading is batched. Returned promises are cached while pending and
can be reused after they settle, which prevents redundant database queries
and supports React Suspense (stable references while pending). A mutation
or incoming sync invalidates the cache of an unsubscribed query, so its
next load reads again. A subscribed query keeps its cached result and the
subscription refreshes it, so a load can return rows that a pending refresh
is about to replace. When SQLite fails a refresh, the query keeps its rows,
its loads reject until a refresh succeeds, and its subscribers are
notified, so React's `useQuery` throws the error then. To skip the cache,
pass `cache: false`; see [LoadQueryOptions.cache](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/LoadQueryOptions#cache).

To subscribe a query for automatic updates, use
[Evolu.subscribeQuery](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#subscribequery).

### Example

```ts
import {
  assertType,
  createQueryBuilder,
  testEvoluSchema,
  type TestEvoluSchema,
  type Evolu,
  type QueryRows,
} from "@evolu/common";

const createQuery = createQueryBuilder(testEvoluSchema);
const allTodos = createQuery((db) => db.selectFrom("todo").selectAll());
const loadTodos = async (evolu: Evolu<TestEvoluSchema>) => {
  const rows = await evolu.loadQuery(allTodos);
  return rows;
};
const loadUncachedTodos = (evolu: Evolu<TestEvoluSchema>) =>
  evolu.loadQuery(allTodos, { cache: false });

assertType<
  ReturnType<typeof loadTodos>,
  Promise<QueryRows<typeof allTodos.Row>>
>();
assertType<
  ReturnType<typeof loadUncachedTodos>,
  Promise<QueryRows<typeof allTodos.Row>>
>();
```

#### Inherited from

`Omit.loadQuery`

---

<a id="name"></a>

### name

```ts
readonly name: string & Brand<"UrlSafeString"> & Brand<"Name">;
```

Defined in: [packages/common/src/local-first/Evolu.ts:523](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L523)

Evolu instance name is derived from [EvoluConfig.appName](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/EvoluConfig#appname),
[AppOwner](https://evolu.dev/docs/api-reference/common/local-first/Owner/interfaces/AppOwner)'s hash, and, for a memory database, a random instance ID;
see [AppName](https://evolu.dev/docs/api-reference/common/local-first/Evolu/variables/AppName).

#### Inherited from

`Omit.name`

---

<a id="requestsync"></a>

### requestSync

```ts
readonly requestSync: (ownerId: string & Brand<"Id"> & Brand<"OwnerId">) => void;
```

Defined in: [packages/common/src/local-first/Evolu.ts:1179](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L1179)

Requests a new synchronization round for an active [OwnerId](https://evolu.dev/docs/api-reference/common/local-first/Owner/variables/OwnerId).

Reconciles locally stored changes, including writes previously rejected
with [ProtocolQuotaError](https://evolu.dev/docs/api-reference/common/local-first/Protocol/interfaces/ProtocolQuotaError), through the owner's active transports. Call
this after the relay provider confirms additional quota is available. It
also checks again the changes this database skipped from the owner's
relays, and sends such a relay the changes received from other relays,
which otherwise wait for its next sync, such as after a reconnect. On a
relay that sends no heartbeats, a request is also how the shared worker
notices a connection that died without closing: an unanswered request
replaces it after the request timeout, ninety seconds unless it has grown,
as Liveness in the Shared module describes. Existing connections and
[Evolu.useOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#useowner) registrations are retained, including registrations
shared by multiple instances or tabs.

The owner must have an active writable registration in this database.
Unregistered or read-only owners are ignored. Requests are skipped while
all of the owner's transports are closed; they synchronize when they
reopen. Disposal drops locally buffered requests. Calling a disposed
instance throws, like other Evolu operations.

Returns immediately, without waiting for synchronization to complete. The
owner's routes in sync state show a failure, such as a quota rejection, as
`failure`, and a skipped change as `skippedError`.

### Example

```ts
import { assertType, type Evolu, type OwnerId } from "@evolu/common";

// Call after successfully increasing the affected owner's relay quota.
const onQuotaIncreased = (evolu: Evolu, ownerId: OwnerId) => {
  evolu.requestSync(ownerId);
};

assertType<typeof onQuotaIncreased, (evolu: Evolu, ownerId: OwnerId) => void>();
```

#### Inherited from

`Omit.requestSync`

---

<a id="subscribequery"></a>

### subscribeQuery

```ts
readonly subscribeQuery: (query: Query<D>) => (listener: Listener) => Unsubscribe;
```

Defined in: [packages/common/src/local-first/Evolu.ts:885](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L885)

Subscribe to [Query](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/Query) [QueryRows](https://evolu.dev/docs/api-reference/common/local-first/Query/type-aliases/QueryRows) changes.

Cached rows invalidated before subscription are refreshed. Subscribing
alone does not load a query that has no cached rows.

### Example

```ts
import {
  assertType,
  createQueryBuilder,
  testEvoluSchema,
  type TestEvoluSchema,
  type Evolu,
  type QueryRows,
  type Unsubscribe,
} from "@evolu/common";

const createQuery = createQueryBuilder(testEvoluSchema);
const allTodos = createQuery((db) => db.selectFrom("todo").select("title"));
const subscribeToTodos = (
  evolu: Evolu<TestEvoluSchema>,
  onRows: (rows: QueryRows<typeof allTodos.Row>) => void,
) =>
  evolu.subscribeQuery(allTodos)(() => {
    onRows(evolu.getQueryRows(allTodos));
  });

assertType<ReturnType<typeof subscribeToTodos>, Unsubscribe>();
```

#### Inherited from

`Omit.subscribeQuery`

---

<a id="update"></a>

### update

```ts
readonly update: DeviceMutation<D, "update">;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2075](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2075)

[Evolu.update](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#update), as a [DeviceMutation](https://evolu.dev/docs/api-reference/common/local-first/Evolu/type-aliases/DeviceMutation).

---

<a id="upsert"></a>

### upsert

```ts
readonly upsert: DeviceMutation<D, "upsert">;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2077](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2077)

[Evolu.upsert](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#upsert), as a [DeviceMutation](https://evolu.dev/docs/api-reference/common/local-first/Evolu/type-aliases/DeviceMutation).

---

<a id="useowner"></a>

### useOwner

```ts
readonly useOwner: (owner:
  | ShardOwner
  | SharedOwner
  | SharedReadonlyOwner, transports: readonly [OwnerWebSocketTransport, OwnerWebSocketTransport]) => UnuseOwner;
```

Defined in: [packages/common/src/local-first/Evolu.ts:2068](https://github.com/evoluhq/evolu/blob/aa223771feea747ffb1b0f5f3271574455633e37/packages/common/src/local-first/Evolu.ts#L2068)

[Evolu.useOwner](https://evolu.dev/docs/api-reference/common/local-first/Evolu/interfaces/Evolu#useowner), for an owner that isn't an AppOwner. The device
instance has no default transports, so it takes them.