Skip to main content
Queries allow you to read data from your Convex database. They are reactive and automatically update when data changes.

Defining queries

query

Define a public query function that can be called from clients.
PropertyValidators
Argument validation object using validators from convex/values. Maps argument names to their validators.
Validator
Return value validator. Helps catch bugs and provides better type safety.
function
required
The implementation function that receives a QueryCtx and validated arguments.
QueryCtx
Query context with read-only database access.
object
Validated arguments matching the args validator.

internalQuery

Define an internal query that can only be called from other Convex functions, not directly from clients.
Internal queries are useful for:
  • Queries called from actions or scheduled functions
  • Shared query logic that shouldn’t be exposed to clients
  • Administrative queries

Query context

QueryCtx

The context object passed to query handlers.
DatabaseReader
Read-only database interface. See Database API for details.
Auth
Authentication interface to get the current user’s identity.
StorageReader
Read-only file storage interface. See Storage API for details.
function
Call another query function within the same read snapshot.
Note: Often you can extract shared logic into a helper function instead. runQuery incurs overhead of running argument and return value validation.

Function shorthand

You can also define queries using function shorthand syntax:
This syntax is more concise but doesn’t provide argument or return value validation.

Best practices

Use indexes for efficient queries

Always use .withIndex() instead of .filter() when querying by specific fields. Filters scan all documents, while indexes efficiently skip non-matching documents.

Add argument validation

For security, always add argument validation to public queries in production apps.

Limit result sets

Use .take(n), .first(), .unique(), or pagination instead of .collect() when result sets can grow unbounded.

Queries are reactive

When used with useQuery on the client, queries automatically re-run when their results change.