Skip to main content
The Query API provides types and methods for creating and executing queries against your Zero schema.

Query Types

Query

Represents a query that can be executed against the database.
type Query<TTable, TSchema, TReturn>
TTable
string
The table name this query operates on
TSchema
Schema
The schema defining the database structure
TReturn
any
The return type of the query results

QueryOrQueryRequest

Union type that accepts either a Query or a QueryRequest.
type QueryOrQueryRequest<TTable, TInput, TOutput, TSchema, TReturn, TContext>
Used throughout the API to accept either form of query definition.

Row

Represents a row from a table with all relationships resolved.
type Row<TTable, TSchema>

PullRow

Represents a row as returned from the database before transformation.
type PullRow<TTable, TSchema>

Query Options

RunOptions

Controls how queries are executed.
type RunOptions = 
  | {type: 'complete'}  // Wait for server sync (default)
  | {type: 'unknown'}   // Run with local data immediately
type
'complete' | 'unknown'
default:"complete"
  • 'complete': Waits for any pending data to sync before running the query
  • 'unknown': Runs immediately with whatever data is available locally

MaterializeOptions

Options for creating materialized views.
interface MaterializeOptions {
  ttl?: TTL;
}
ttl
TTL
Time to live for the materialized view. Controls how long query results are cached after the view is destroyed.Can be:
  • 'never': Query is removed immediately (default)
  • number: Seconds to keep the query cached
  • {seconds: number}: Object form specifying seconds

PreloadOptions

Options for preloading query data.
interface PreloadOptions {
  ttl?: TTL;
}
ttl
TTL
Time to live for the preloaded data. Same format as MaterializeOptions.ttl.

View Types

TypedView

A materialized view that stays synchronized with the database.
interface TypedView<TReturn> {
  data: TReturn;
  destroy(): void;
  addListener(listener: (data: TReturn, resultType: ResultType) => void): () => void;
  updateTTL(ttl: TTL): void;
}
data
TReturn
The current query results
destroy
() => void
Destroys the view and stops synchronization. Must be called when done to avoid memory leaks.
addListener
(listener) => () => void
Adds a listener that is called when the data changes. Returns an unsubscribe function.
const unsubscribe = view.addListener((data, resultType) => {
  console.log('Data updated:', data);
});

// Later...
unsubscribe();
updateTTL
(ttl: TTL) => void
Updates the time-to-live for this view

ViewFactory

Factory function for creating custom views.
type ViewFactory<TTable, TSchema, TReturn, T> = 
  (query: Query<TTable, TSchema, TReturn>) => T
Used with zero.materialize() to create custom view implementations:
class MyCustomView {
  constructor(query: Query) {
    // Custom implementation
  }
}

const customView = z.materialize(query, q => new MyCustomView(q));

Result Types

ResultType

Indicates the freshness of query results.
type ResultType = 'unknown' | 'complete' | 'error'
unknown
'unknown'
Results are from local cache, may be stale
complete
'complete'
Results are fresh from the server
error
'error'
Query encountered an error

HumanReadable

Transforms query results into a human-readable format with resolved relationships.
type HumanReadable<T>
Converts internal query result types to the format your application code uses.

Query Builder Types

SchemaQuery

Query builders for each table in the schema.
type SchemaQuery<S extends Schema> = {
  [K in keyof S['tables']]: QueryBuilder<K, S>
}
Provides typed query builders for each table:
const issuesQuery = z.query.issue
  .where('status', '=', 'open')
  .orderBy('createdAt', 'desc')
  .limit(10);
Note: z.query is deprecated. Use createBuilder() instead:
import { createBuilder } from '@rocicorp/zero';

const q = createBuilder(z.context, z.schema);
const issuesQuery = q.issue
  .where('status', '=', 'open')
  .orderBy('createdAt', 'desc')
  .limit(10);

ConditionalSchemaQuery

Query type that conditionally includes legacy query methods based on schema configuration.
type ConditionalSchemaQuery<S extends Schema> = 
  S['enableLegacyQueries'] extends true 
    ? SchemaQuery<S> 
    : {}

Query Execution Flow

  1. Create Query: Build a query using the query builder
    const query = q.users.where('active', '=', true);
    
  2. Execute Query: Choose execution method:
    • run(): Execute once
    • materialize(): Create live view
    • preload(): Cache for later
  3. Handle Results: Process the returned data
    const [users, details] = useQuery(query);
    if (details.type === 'complete') {
      // Data is fresh from server
    }
    

Error Handling

QueryErrorDetails

Provides error information when a query fails.
interface QueryErrorDetails {
  type: 'error';
  retry: () => void;
  refetch: () => void;
  error: {
    type: 'app' | 'server' | 'client';
    message: string;
    details?: unknown;
  };
}
type
'error'
Indicates this is an error result
retry
() => void
Function to retry the query after an error
refetch
() => void
Alias for retry
error.type
'app' | 'server' | 'client'
The type of error:
  • 'app': Error in application query code
  • 'server': Server-side error
  • 'client': Client-side error
error.message
string
Human-readable error message
error.details
unknown
Additional error details if available

QueryResultDetails

Provides metadata about successful query results.
type QueryResultDetails = 
  | {type: 'unknown'}
  | {type: 'complete'}
  | QueryErrorDetails
Returned alongside query data to indicate result freshness or errors.

Advanced Types

AnyQuery

Type-erased query that can represent any query.
type AnyQuery = Query<string, BaseDefaultSchema, unknown>

QueryInternals

Internal query representation (not typically used in application code).
interface QueryInternals {
  readonly hash: () => string;
  readonly format: Format;
  // Internal properties...
}

Build docs developers (and LLMs) love