Skip to Content
QueriesBasics

InstantDB uses InstaQL, a simple yet powerful query language designed for real-time applications. All queries are reactive by default, meaning your UI automatically updates when data changes.

Simple Queries

Fetch All Records

Query all records of a specific entity type:

// Get all todos final todosQuery = db.query({'todos': {}}); // Access the data Watch((context) { final result = todosQuery.value; if (result.isLoading) { return const CircularProgressIndicator(); } if (result.hasError) { return Text('Error: ${result.error}'); } final todos = result.data!['todos'] as List; return ListView.builder( itemCount: todos.length, itemBuilder: (context, index) => TodoTile(todo: todos[index]), ); });

Filter with Where Clauses

Filter data using where conditions:

// Get completed todos only final completedTodos = db.query({ 'todos': { 'where': {'completed': true}, }, }); // Get todos created today final today = DateTime.now(); final startOfDay = DateTime(today.year, today.month, today.day); final todaysTodos = db.query({ 'todos': { 'where': { 'createdAt': {'\$gte': startOfDay.millisecondsSinceEpoch}, }, }, });

Sorting and Limiting

Control the order and number of results:

// Get latest 10 todos, sorted by creation date final latestTodos = db.query({ 'todos': { '\$': { 'order': {'createdAt': 'desc'}, 'limit': 10, }, }, }); // Multiple sort fields final sortedTodos = db.query({ 'todos': { '\$': { 'order': [ {'priority': 'desc'}, {'createdAt': 'asc'}, ], }, }, });

Query Operators

Comparison Operators

OperatorDescriptionExample
\$eqEqual (default){'status': 'active'}
\$neqNot equal{'status': {'\$neq': 'deleted'}}
\$gtGreater than{'score': {'\$gt': 100}}
\$gteGreater than or equal{'age': {'\$gte': 18}}
\$ltLess than{'price': {'\$lt': 50}}
\$lteLess than or equal{'quantity': {'\$lte': 10}}

Array Operators

OperatorDescriptionExample
\$inValue in array{'category': {'\$in': ['work', 'personal']}}
\$ninValue not in array{'status': {'\$nin': ['deleted', 'archived']}}

String Operators

OperatorDescriptionExample
\$containsContains substring{'title': {'\$contains': 'urgent'}}
\$startsWithStarts with{'email': {'\$startsWith': 'admin'}}
\$endsWithEnds with{'filename': {'\$endsWith': '.pdf'}}

React-Style Query Syntax

Flutter InstantDB supports React-style query syntax for compatibility:

final query = db.query({ 'todos': { '\$': { 'where': {'completed': false}, 'order': {'createdAt': 'desc'}, 'limit': 20, }, }, });

Both syntaxes are supported and can be used interchangeably.

Reactive Queries

Using InstantBuilder

The recommended way to use queries in widgets:

InstantBuilder( query: { 'todos': { 'where': {'userId': currentUserId}, }, }, builder: (context, result) { if (result.isLoading) { return const CircularProgressIndicator(); } if (result.hasError) { return Text('Error: ${result.error}'); } final todos = result.data!['todos'] as List; return TodosList(todos: todos); }, )

Using InstantBuilderTyped

For better type safety, use the typed version:

InstantBuilderTyped<List<Map<String, dynamic>>>( query: {'todos': {}}, transformer: (data) { final todos = (data['todos'] as List).cast<Map<String, dynamic>>(); // Apply client-side sorting or filtering if needed todos.sort((a, b) => b['createdAt'].compareTo(a['createdAt'])); return todos; }, builder: (context, todos) { return ListView.builder( itemCount: todos.length, itemBuilder: (context, index) => TodoTile(todo: todos[index]), ); }, )

One-time Queries

For non-reactive queries that execute once:

// Execute query once and get result final result = await db.queryOnce({'todos': {}}); if (result.hasData) { final todos = result.data!['todos'] as List; print('Found ${todos.length} todos'); }

Complex Queries

Multiple Conditions

Combine multiple where conditions:

final complexQuery = db.query({ 'todos': { 'where': { 'completed': false, 'priority': {'\$in': ['high', 'urgent']}, 'dueDate': {'\$lte': DateTime.now().millisecondsSinceEpoch}, 'assignee': {'\$neq': null}, }, }, });

Nested Conditions

Use logical operators for complex conditions:

final nestedQuery = db.query({ 'todos': { 'where': { '\$or': [ {'priority': 'urgent'}, { '\$and': [ {'priority': 'high'}, {'dueDate': {'\$lte': DateTime.now().millisecondsSinceEpoch}}, ], }, ], }, }, });

Query Performance

Indexing

Ensure your frequently queried fields are indexed in your InstantDB schema:

// This will perform better if 'userId' is indexed final userTodos = db.query({ 'todos': { 'where': {'userId': currentUserId}, }, });

Pagination

Use limit and offset for large datasets:

final page1 = db.query({ 'todos': { '\$': { 'limit': 20, 'offset': 0, }, }, }); final page2 = db.query({ 'todos': { '\$': { 'limit': 20, 'offset': 20, }, }, });

Error Handling

Handle query errors gracefully:

InstantBuilder( query: {'todos': {}}, errorBuilder: (context, error) { return Column( children: [ const Icon(Icons.error, color: Colors.red), Text('Failed to load todos: $error'), ElevatedButton( onPressed: () { // Retry logic }, child: const Text('Retry'), ), ], ); }, builder: (context, result) { // Success case final todos = result.data!['todos'] as List; return TodosList(todos: todos); }, )

Next Steps

Learn more about advanced querying features: