Skip to content

useQuery

import { useQuery } from '@xnet/react'
import { TaskSchema } from './schema'
function TaskList() {
const { data: tasks, loading } = useQuery(TaskSchema, {
where: { status: 'todo' },
orderBy: { createdAt: 'desc' },
limit: 20
})
if (loading) return <p>Loading...</p>
return (
<ul>
{tasks.map((t) => (
<li key={t.id}>{t.title}</li>
))}
</ul>
)
}

useQuery has three overloads:

// Overload 1: List all nodes of a schema
function useQuery<P>(schema: DefinedSchema<P>): QueryListResult<P>
// Overload 2: Get a single node by ID
function useQuery<P>(schema: DefinedSchema<P>, id: string): QuerySingleResult<P>
// Overload 3: Query with filters
function useQuery<P>(schema: DefinedSchema<P>, filter: QueryFilter<P>): QueryListResult<P>

The second argument determines the overload: pass a string for single-node lookup, an object for filtered queries, or omit it to get all nodes.

ParameterTypeDescription
schemaDefinedSchema<P>The schema to query. Created with defineSchema().
idstring(Overload 2) Node ID to fetch.
filterQueryFilter<P>(Overload 3) Filter, sort, and pagination options.
FieldTypeDefaultDescription
wherePartial<InferCreateProps<P>>Filter by property values. Strict equality matching.
includeDeletedbooleanfalseInclude soft-deleted nodes in results.
orderByRecord<string, 'asc' | 'desc'>Sort by any property, createdAt, or updatedAt.
limitnumberMaximum results to return.
offsetnumberSkip N results (for pagination).
FieldTypeDescription
dataFlatNode<P>[]The query results. Properties are spread to top level.
loadingbooleantrue during initial load, false once data is available.
errorError | nullAny error during query execution.
reload() => Promise<void>Force re-fetch from store. Rarely needed — data auto-updates.
FieldTypeDescription
dataFlatNode<P> | nullThe node, or null if not found or deleted.
loadingbooleantrue during initial load.
errorError | nullAny error during query execution.
reload() => Promise<void>Force re-fetch from store.

Every node includes system fields (managed automatically) plus your schema properties:

interface FlatNode<P> {
id: string
schemaId: string // e.g., 'xnet://my-app/Task'
createdAt: number // Auto-set at creation time
createdBy: string // DID of creator
updatedAt: number // Auto-updated on every change
updatedBy: string // DID of last modifier
deleted: boolean
// ... plus all your schema properties
}

You can filter and sort by any of these system fields without defining them in your schema.

The simplest query — returns every node of a schema:

const { data: tasks } = useQuery(TaskSchema)
// tasks: FlatNode<typeof TaskSchema._properties>[]
const { data: activeTasks } = useQuery(TaskSchema, {
where: { status: 'doing' },
orderBy: { updatedAt: 'desc' },
limit: 50
})
const { data: task } = useQuery(TaskSchema, taskId)
// task: FlatNode | null

When id is null or undefined, use a guard:

function TaskDetail({ id }: { id: string | null }) {
// Only query when id is available
const { data: task } = useQuery(TaskSchema, id ?? '')
if (!id || !task) return <p>No task selected</p>
return <h1>{task.title}</h1>
}
const [page, setPage] = useState(0)
const PAGE_SIZE = 20
const { data: tasks } = useQuery(TaskSchema, {
orderBy: { createdAt: 'desc' },
limit: PAGE_SIZE,
offset: page * PAGE_SIZE
})
  • useMutate — write data that useQuery reads
  • useNode — when you need a Yjs document for one node
  • defineSchema — define the schemas you query