Docs
API
useQuery

useQuery

useQuery React Hook performs a database query and returns rows and firstRow props that are automatically updated when data changes. It takes two callbacks, a Kysely (opens in a new tab) type-safe SQL query builder, and a filterMap helper for rows filtering and ad-hoc migrations.

ℹ️

Note that useQuery uses React Suspense.

Examples

The most simple example:

const { rows } = useQuery(
  (db) => db.selectFrom("todo").selectAll(),
  (row) => row
);

If you mouse hover over rows, you will see that all columns except Id are nullable regardless of the database Schema.

There are two good reasons for that. The first is the local-first app database schema can be changed anytime, but already-created data can't because it's not feasible to migrate all local data. The second reason is that sync messages can arrive in any order in distributed systems.

The remedy for nullability is ad-hoc filtering and mapping via filterMap helper. This example filters out rows with falsy titles:

const { rows } = useQuery(
  (db) => db.selectFrom("todo").selectAll(),
  ({ title, ...rest }) => title && { title, ...rest }
);

A real app would filterMap all versions of the table schema defined by a union of types, therefore safely enforced by the TypeScript compiler.

The next example shows the usage of columns that Evolu automatically adds to all tables. Those columns are: createdAt, createdBy, updatedAt, and isDeleted.

const { rows } = useQuery(
  (db) =>
    db
      .selectFrom("todoCategory")
      .select(["id", "name"])
      .where("isDeleted", "is not", E.cast(true))
      .orderBy("createdAt"),
  ({ name, ...rest }) => name && { name, ...rest }
);

Note E.cast usage. It's Evolu's helper to cast booleans and dates that SQLite does not support natively.