DomainRuntimedocs
Working with data
GuideWhat works todayReviewed 2026-09-22

Reading data

Queries that stay live: filters, ordering, pagination and nested links.

The query language on this page runs in production today. The db client (@domainruntime/react) is planned; the block below shows the client you use today.

You read with a query: an object that names what you want. The result has the same shape.

const { isLoading, error, data } = db.useQuery({ orders_order: {} });
// data → { orders_order: [{ id: "…", status: "open", total: 120, placedAt: … }, …] }

useQuery is live. When any matching object changes — from this tab, another user, or an action — the component re-renders with the new result.

If a query returns nothing, check the entity's view rule: hidden objects are left out without an error.

Today

Use the InstantDB client pointed at your environment:

import { init } from "@instantdb/react";
import schema from "@/instant.schema";

const env = process.env.NEXT_PUBLIC_DOMAIN_RUNTIME_URL!;   // https://<handle>.domainruntime.cloud
export const db = init({
  appId: process.env.NEXT_PUBLIC_DOMAIN_ENVIRONMENT_ID!,
  apiURI: env,
  websocketURI: `${env.replace(/^http/, "ws")}/session`,
  schema,
});
await db.auth.signInWithToken(userToken);   // see Users

Queries, useQuery and subscribeQuery work as on this page. Writes do not: the environment rejects client transactions.

Filtering

db.useQuery({ orders_order: { $: { where: { status: "open" } } } });
FilterMatches
{ status: "open" }equal
{ status: { $ne: "cancelled" } }not equal
{ status: { $in: ["open", "held"] } }any of the values
{ total: { $gt: 100 } }$gt, $gte, $lt, $lte — the attribute must be indexed and typed
{ notes: { $isNull: true } }missing value
{ "owner.id": { $isNull: true } }objects with no linked owner
{ reference: { $like: "ORD-2026%" } }a pattern, case-sensitive; $ilike ignores case. Indexed attribute.
{ "customer.email": "a@acme.com" }a value on a linked object
{ id: orderId }one object

Several keys in one where must all match. For anything else, use and and or:

db.useQuery({
  orders_order: {
    $: {
      where: {
        or: [{ status: "open" }, { and: [{ status: "held" }, { total: { $gt: 1000 } }] }],
      },
    },
  },
});

Ordering and limits

db.useQuery({
  orders_order: { $: { order: { placedAt: "desc" }, limit: 20 } },
});

order needs an indexed, typed attribute.

Pagination

Cursors stay stable while data changes underneath. Use them for lists people scroll through:

const { data, pageInfo } = db.useQuery({
  orders_order: { $: { order: { placedAt: "desc" }, first: 20 } },
});

// next page
db.useQuery({
  orders_order: { $: { order: { placedAt: "desc" }, first: 20, after: pageInfo.orders_order.endCursor } },
});
// previous page: last: 20, before: pageInfo.orders_order.startCursor

pageInfo.<entity> carries startCursor, endCursor, hasNextPage and hasPreviousPage.

For a fixed page number, limit and offset also work: { limit: 20, offset: 40 }. That page shifts if objects are added before it.

Follow links by nesting them:

db.useQuery({
  orders_customer: {
    $: { where: { email: "a@acme.com" } },
    orders: {
      $: { order: { placedAt: "desc" }, limit: 5 },
      invoices: {},
    },
  },
});
// data.orders_customer[0].orders[0].invoices

Each level takes its own $ options.

Selecting fields

db.useQuery({ orders_order: { $: { fields: ["status", "total"] } } });

Outside React

const stop = db.subscribeQuery({ orders_order: {} }, (result) => {
  if (result.data) render(result.data.orders_order);
});

const { data } = await db.queryOnce({ orders_order: { $: { limit: 1 } } });

Inside an action

Actions read with runtime.query, under the caller's permissions:

async execute({ input, runtime }) {
  const { orders_order } = await runtime.query({
    orders_order: { $: { where: { id: input.orderId } }, owner: {} },
  });
}

Today: const db = await runtime.db(); const { orders_order } = await db.query({ … });

Next

On this page