Flutter InstantDB
API Reference

InstantDB API

Complete API reference for the core InstantDB class

The InstantDB class is the main entry point for interacting with your InstantDB database. It provides methods for initialization, queries, transactions, authentication, and real-time synchronization.

Initialization

InstantDB.init()

Initialize a new InstantDB instance.

static Future<InstantDB> init({
  required String appId,
  InstantConfig config = const InstantConfig(),
})

Parameters:

  • appId (String): Your InstantDB application ID
  • config (InstantConfig, optional): Configuration options

Returns: Future<InstantDB> - The initialized database instance

Example:

final db = await InstantDB.init(
  appId: 'your-app-id',
  config: const InstantConfig(
    syncEnabled: true,
    verboseLogging: false,
  ),
);

InstantConfig

Configuration options for InstantDB initialization.

class InstantConfig {
  const InstantConfig({
    this.persistenceDir,
    this.syncEnabled = true,
    this.baseUrl = 'https://api.instantdb.com',
    this.maxCacheSize = 50 * 1024 * 1024, // 50MB
    this.reconnectDelay = const Duration(seconds: 1),
    this.verboseLogging = false,
  });

  final String? persistenceDir;
  final bool syncEnabled;
  final String baseUrl;
  final int maxCacheSize;
  final Duration reconnectDelay;
  final bool verboseLogging;
}

Properties:

  • persistenceDir (String?): Custom directory for SQLite database storage
  • syncEnabled (bool): Enable real-time synchronization (default: true)
  • baseUrl (String): Custom API endpoint URL (default: https://api.instantdb.com)
  • maxCacheSize (int): Maximum size of the local database cache in bytes (default: 50MB)
  • reconnectDelay (Duration): Delay between reconnection attempts (default: 1 second)
  • verboseLogging (bool): Enable detailed debug logging (default: false)

Core Properties

isReady

A reactive signal indicating if the database is initialized and ready for use.

ReadonlySignal<bool> get isReady

Example:

if (db.isReady.value) {
  print('Database is ready');
}

isOnline

A reactive signal indicating if the database is currently connected to the sync server.

ReadonlySignal<bool> get isOnline

Example:

Watch((context) {
  final online = db.isOnline.value;
  return Text(online ? 'Connected' : 'Disconnected');
});

appId

Get the application ID for this database instance.

String get appId

Returns: String - The application ID

Example:

print('Database app ID: ${db.appId}');

auth

Access the authentication manager.

AuthManager get auth

Returns: AuthManager - The authentication manager instance

Example:

final currentUser = db.auth.currentUser.value;
if (currentUser != null) {
  print('User: ${currentUser.email}');
}

presence

Access the presence manager for real-time collaboration.

PresenceManager get presence

Returns: PresenceManager - The presence manager instance

Example:

final room = db.presence.joinRoom('chat-room');
await room.setPresence({'status': 'online'});

syncEngine

Access the internal synchronization engine. Note that most synchronization logic is handled automatically through db.query and db.transact.

SyncEngine get syncEngine

Returns: SyncEngine - The sync engine instance

Query Methods

query()

Create a reactive query that updates automatically when data changes. This is the preferred method for getting data.

Signal<QueryResult> query(Map<String, dynamic> query, {bool syncedOnly = false})

Parameters:

  • query (Map<String, dynamic>): The InstaQL query object
  • syncedOnly (bool, optional): If true, only returns entities that sync to cloud (excludes local-only entities)

Returns: Signal<QueryResult> - A reactive signal containing query results

Example:

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

subscribeQuery()

Alias for query(), kept for API compatibility.

Signal<QueryResult> subscribeQuery(Map<String, dynamic> query)

Example:

final todosSignal = db.subscribeQuery({
  'todos': {
    'where': {'completed': false},
    'orderBy': {'createdAt': 'desc'},
    'limit': 20,
  },
});

// Use in reactive widgets
Watch((context) {
  final result = todosSignal.value;
  final todos = result.data?['todos'] ?? [];
  return TodoList(todos: todos);
});

queryOnce()

Execute a one-time query without creating a subscription.

Future<QueryResult> queryOnce(Map<String, dynamic> query, {bool syncedOnly = false})

Parameters:

  • query (Map<String, dynamic>): The InstaQL query object
  • syncedOnly (bool, optional): If true, only returns entities that sync to cloud

Returns: Future<QueryResult> - The query result

Example:

final result = await db.queryOnce({
  'users': {
    'where': {'role': 'admin'},
    'limit': 5,
  },
});

final adminUsers = result.data?['users'] ?? [];
print('Found ${adminUsers.length} admin users');

Transaction Methods

transact()

Execute a transaction with a list of operations or transaction chunk.

Future<TransactionResult> transact(dynamic transaction)

Parameters:

  • transaction (List<Operation> or TransactionChunk): The operations to execute

Returns: Future<TransactionResult> - The transaction result

Example:

// With list of operations
await db.transact([
  ...db.create('todos', {
    'id': db.id(),
    'text': 'Learn InstantDB',
    'completed': false,
  }),
]);

// With transaction chunk (new tx API)
await db.transact(
  db.tx['todos'][todoId].update({'completed': true})
);

transactChunk() (Deprecated)

Execute a transaction chunk. Use transact() instead.

@Deprecated('Use transact() instead')
Future<TransactionResult> transactChunk(TransactionChunk chunk)

create()

Create a new entity operation.

List<Operation> create(String entityType, Map<String, dynamic> data)

Parameters:

  • entityType (String): The type of entity to create
  • data (Map<String, dynamic>): The entity data

Returns: List<Operation> - List containing the create operation

Example:

final operations = db.create('posts', {
  'id': db.id(),
  'title': 'Hello World',
  'content': 'This is my first post',
  'authorId': userId,
  'createdAt': DateTime.now().millisecondsSinceEpoch,
});

await db.transact(operations);

update()

Create an update entity operation.

Operation update(String entityId, Map<String, dynamic> data)

Parameters:

  • entityId (String): The ID of the entity to update
  • data (Map<String, dynamic>): The data to update

Returns: Operation - The update operation

Example:

await db.transact([
  db.update(postId, {
    'title': 'Updated Title',
    'updatedAt': DateTime.now().millisecondsSinceEpoch,
  }),
]);

delete()

Create a delete entity operation.

Operation delete(String entityId)

Parameters:

  • entityId (String): The ID of the entity to delete

Returns: Operation - The delete operation

Example:

await db.transact([
  db.delete(postId),
]);

merge()

Create a deep merge operation.

Operation merge(String entityId, Map<String, dynamic> data)

Parameters:

  • entityId (String): The ID of the entity to merge
  • data (Map<String, dynamic>): The data to deep merge

Returns: Operation - The merge operation

Example:

await db.transact([
  db.merge(userId, {
    'preferences': {
      'theme': 'dark',
      'notifications': {'email': false},
    },
  }),
]);

Create a link operation between entities.

Operation link(String fromId, String linkName, String toId)

Parameters:

  • fromId (String): The source entity ID
  • linkName (String): The name of the link relationship
  • toId (String): The target entity ID

Returns: Operation - The link operation

Example:

await db.transact([
  db.link(userId, 'posts', postId),
]);

Create an unlink operation between entities.

Operation unlink(String fromId, String linkName, String toId)

Parameters:

  • fromId (String): The source entity ID
  • linkName (String): The name of the link relationship
  • toId (String): The target entity ID

Returns: Operation - The unlink operation

Example:

await db.transact([
  db.unlink(userId, 'posts', postId),
]);

Transaction API (tx namespace)

tx

Access the new transaction API namespace for fluent operations.

TransactionNamespace get tx

Returns: TransactionNamespace - The transaction namespace

Example:

// Fluent API for complex operations
await db.transact(
  db.tx['users'][userId]
    .update({'name': 'New Name'})
    .link({'posts': [postId]})
    .merge({
      'preferences': {'theme': 'dark'},
    })
);

Utility Methods

id()

Generate a new UUID for entity IDs.

String id()

Returns: String - A new UUID string

Example:

final newId = db.id();
print('Generated ID: $newId'); // e.g., "123e4567-e89b-12d3-a456-426614174000"

await db.transact([
  ...db.create('items', {
    'id': newId,
    'name': 'New Item',
  }),
]);

lookup()

Create a lookup reference for referencing entities by attribute instead of ID.

LookupRef lookup(String entityType, String attribute, dynamic value)

Parameters:

  • entityType (String): The type of entity to lookup
  • attribute (String): The attribute to match against
  • value (dynamic): The value to match

Returns: LookupRef - A lookup reference object

Example:

// Reference user by email instead of ID
await db.transact([
  ...db.create('posts', {
    'id': db.id(),
    'title': 'Hello World',
    'authorId': lookup('users', 'email', 'john@example.com'),
  }),
]);

// Use in queries
final posts = db.subscribeQuery({
  'posts': {
    'where': {
      'author': lookup('users', 'email', 'john@example.com'),
    },
  },
});

Authentication Helpers

getAuth()

Get the current authentication state (one-time check).

AuthUser? getAuth()

Returns: AuthUser? - The current user, or null if not authenticated

Example:

final currentUser = db.getAuth();
if (currentUser != null) {
  print('Logged in as: ${currentUser.email}');
} else {
  print('Not logged in');
}

subscribeAuth()

Get a stream of authentication state changes.

Stream<AuthUser?> subscribeAuth()

Returns: Stream<AuthUser?> - A stream of auth user state

Example:

db.subscribeAuth().listen((user) {
  if (user != null) {
    print('User signed in: ${user.email}');
  } else {
    print('User signed out');
  }
});

getAnonymousUserId()

Get or generate an anonymous user ID for presence and collaboration.

String getAnonymousUserId()

Returns: String - An anonymous user ID

Example:

final anonymousId = db.getAnonymousUserId();
final room = db.presence.joinRoom('public-room', initialPresence: {
  'userId': anonymousId,
  'userName': 'Guest ${anonymousId.substring(0, 4)}',
});

Lifecycle Methods

dispose()

Clean up database resources and connections.

Future<void> dispose()

Returns: Future<void>

Example:

@override
void dispose() {
  super.dispose();
  // Clean up database when app is disposed
  db.dispose();
}

Error Handling

All InstantDB methods can throw InstantException for database-related errors:

try {
  await db.transact([
    ...db.create('invalid', {}), // Missing required fields
  ]);
} on InstantException catch (e) {
  print('InstantDB Error: ${e.code}');
  print('Message: ${e.message}');
  
  // Handle specific error types
  switch (e.code) {
    case 'validation_error':
      // Handle validation errors
      break;
    case 'network_error':
      // Handle network issues
      break;
    case 'auth_error':
      // Handle authentication errors
      break;
  }
} catch (e) {
  print('Unexpected error: $e');
}

Complete Example

Here's a complete example showing common InstantDB operations:

class TodoService {
  final InstantDB db;

  TodoService(this.db);

  // Get reactive todos
  Signal<QueryResult> getTodos({bool? completed}) {
    return db.subscribeQuery({
      'todos': {
        if (completed != null) 'where': {'completed': completed},
        'orderBy': {'createdAt': 'desc'},
      },
    });
  }

  // Create a new todo
  Future<void> createTodo({
    required String text,
    bool completed = false,
  }) async {
    await db.transact([
      ...db.create('todos', {
        'id': db.id(),
        'text': text,
        'completed': completed,
        'createdAt': DateTime.now().millisecondsSinceEpoch,
        'userId': db.auth.currentUser.value?.id,
      }),
    ]);
  }

  // Update todo using new tx API
  Future<void> updateTodo(String todoId, {String? text, bool? completed}) async {
    final updates = <String, dynamic>{};
    if (text != null) updates['text'] = text;
    if (completed != null) updates['completed'] = completed;
    updates['updatedAt'] = DateTime.now().millisecondsSinceEpoch;

    await db.transact(
      db.tx['todos'][todoId].update(updates)
    );
  }

  // Delete todo
  Future<void> deleteTodo(String todoId) async {
    await db.transact([db.delete(todoId)]);
  }

  // Get todos by user email (using lookup)
  Signal<QueryResult> getTodosByUser(String email) {
    return db.subscribeQuery({
      'todos': {
        'where': {
          'user': lookup('users', 'email', email),
        },
      },
    });
  }

  // Bulk operations
  Future<void> markAllCompleted() async {
    final result = await db.queryOnce({
      'todos': {'where': {'completed': false}},
    });
    
    final todos = (result.data?['todos'] as List? ?? [])
        .cast<Map<String, dynamic>>();

    final operations = todos.map((todo) => 
      db.update(todo['id'], {'completed': true})
    ).toList();

    if (operations.isNotEmpty) {
      await db.transact(operations);
    }
  }
}

Next Steps

Explore specific API areas:

On this page