Working with data
Writing data
Instant uses a Firebase-inspired interface for mutations. We call our mutation language InstaML
Creating data
We use the create action to create entities:
import { init, id } from '@instantdb/react'; // Visit https://instantdb.com/dash to get your APP_ID :) const APP_ID = '__APP_ID__'; const db = init({ appId: APP_ID }); // transact! 🔥 db.transact(db.tx.goals[id()].create({ title: 'eat' }));
This creates a new goal with the following properties:
- It's identified by a randomly generated id via the 
id()function. - It has an attribute 
titlewith valueeat. 
You can store strings, numbers, booleans, arrays, and objects as values. You can also generate values via functions. Below is an example for picking a random goal title.
db.transact( db.tx.goals[id()].create({ title: ['eat', 'sleep', 'hack', 'repeat'][Math.floor(Math.random() * 4)], }), );
Update data
The update action is used for updating entities. Suppose we had created the following goal
const eatId = id(); db.transact( db.tx.goals[eatId].update({ priority: 'top', lastTimeEaten: 'Yesterday' }), );
We eat some food and decide to update the goal. We can do that like so:
db.transact(db.tx.goals[eatId].update({ lastTimeEaten: 'Today' }));
This will only update the value of the lastTimeEaten attribute for entity eat.
Similar to NoSQL, you don't need to use the same schema for each entity in a namespace. After creating the previous goal you can run the following:
db.transact( db.tx.goals[id()].update({ priority: 'none', isSecret: true, value: 10, aList: [1, 2, 3], anObject: { foo: 'bar' }, }), );
update function works as create or update depending on whether the entity already exists or not (so called "upsert" mode). If entity doesn’t exist yet, calling update will create it, otherwise it will update.
To force “strict update” mode, pass { upsert: false } option:
db.transact( db.tx.goals[eatId].update({ lastTimeEaten: 'Today' }, { upsert: false }), );
Merge data
When you update an attribute, you overwrite it. This is fine for updating values of strings, numbers, and booleans. But if you use update to overwrite json objects you may encounter two problems:
- You lose any data you didn't specify.
 - You risk clobbering over changes made by other clients.
 
For example, imagine we had a game entity, that stored a state of favorite colors:
// User 1 saves {'0-0': 'red'} db.transact(db.tx.games[gameId].update({ state: { '0-0': 'red' } })); // User 2 saves {'0-1': 'blue'} db.transact(db.tx.games[gameId].update({ state: { '0-1': 'blue' } })); // 🤔 Uh oh! User 2 overwrite User 1: // Final State: {'0-1': 'blue' }
To make working with deeply-nested, document-style JSON values a breeze, we created merge. Similar to lodash's merge function, merge allows you to specify the slice of data you want to update:
// User 1 saves {'0-0': 'red'} db.transact(db.tx.games[gameId].merge({ state: { '0-0': 'red' } })); // User 2 saves {'0-1': 'blue'} db.transact(db.tx.games[gameId].merge({ state: { '0-1': 'blue' } })); // ✅ Wohoo! Both states are merged! // Final State: {'0-0': 'red', '0-1': 'blue' }
merge only merges objects. Calling merge on arrays, numbers, or booleans will overwrite the values.
Sometimes you may want to remove keys from a nested object. You can do so by calling merge with a key set to null. This will remove the corresponding property from the object.
Setting a key to undefined will have no effect. Set the key to null to remove the property.
// State: {'0-0': 'red', '0-1': 'blue' } db.transact(db.tx.games[gameId].merge({ state: { '0-1': null } })); // New State! {'0-0': 'red' }
Delete data
The delete action is used for deleting entities.
db.transact(db.tx.goals[eatId].delete());
You can generate an array of delete txs to delete all entities in a namespace
const { isLoading, error, data } = db.useQuery({ goals: {} }); const { goals } = data; // ... db.transact(goals.map((g) => db.tx.goals[g.id].delete()));
Calling delete on an entity also deletes its associations. So no need to worry about cleaning up previously created links.
Link data
link is used to create associations.
Suppose we create a goal and a todo.
db.transact([ db.tx.todos[workoutId].update({ title: 'Go on a run' }), db.tx.goals[healthId].update({ title: 'Get fit!' }), ]);
We can associate healthId with workoutId like so:
db.transact(db.tx.goals[healthId].link({ todos: workoutId }));
We could have done all this in one transact too via chaining transaction chunks.
db.transact([ db.tx.todos[workoutId].update({ title: 'Go on a run' }), db.tx.goals[healthId] .update({ title: 'Get fit!' }) .link({ todos: workoutId }), ]);
You can specify multiple ids in one link as well:
db.transact([ db.tx.todos[workoutId].update({ title: 'Go on a run' }), db.tx.todos[proteinId].update({ title: 'Drink protein' }), db.tx.todos[sleepId].update({ title: 'Go to bed early' }), db.tx.goals[healthId] .update({ title: 'Get fit!' }) .link({ todos: [workoutId, proteinId, sleepId] }), ]);
Links are bi-directional. Say we link healthId to workoutId
db.transact(db.tx.goals[healthId].link({ todos: workoutId }));
We can query associations in both directions
const { isLoading, error, data } = db.useQuery({ goals: { todos: {} }, todos: { goals: {} }, }); const { goals, todos } = data; console.log('goals with nested todos', goals); console.log('todos with nested goals', todos);
Unlink data
Links can be removed via unlink.
db.transact(db.tx.goals[healthId].unlink({ todos: workoutId }));
This removes links in both directions. Unlinking can be done in either direction so unlinking workoutId from healthId would have the same effect.
db.transact([db.tx.todos[workoutId].unlink({ goals: healthId })]);
We can unlink multiple ids too:
db.transact([ db.tx.goals[healthId].unlink({ todos: [workoutId, proteinId, sleepId] }), db.tx.goals[workId].unlink({ todos: [standupId, reviewPRsId, focusId] }), ]);
Lookup by unique attribute
If your entity has a unique attribute, you can use lookup in place of the id to perform updates.
import { lookup } from '@instantdb/react'; db.transact( db.tx.profiles[lookup('email', 'eva_lu_ator@instantdb.com')].update({ name: 'Eva Lu Ator', }), );
The lookup function takes the attribute as its first argument and the unique attribute value as its second argument.
When it is used in a transaction, the updates will be applied to the entity that has the unique value. If no entity has the value, then a new entity with a random id will be created with the value.
It can be used with update, delete, merge, link, and unlink.
Lookups in links
When used with links, it can also be used in place of the linked entity's id.
db.transact( db.tx.users[lookup('email', 'eva_lu_ator@instantdb.com')].link({ posts: lookup('number', 15), // using a lookup in place of the id }), );
Transacts are atomic
When you call db.transact, all the transactions are committed atomically. If any of the transactions fail, none of them will be committed.
Typesafety
By default, db.transact is permissive. When you save data, we'll create missing attributes for you:
db.tx.todos[workoutId].update({ // Instant will automatically create this attribute dueDate: Date.now() + 60 * 1000, });
As your app grows, you may want to start enforcing types. When you're ready, you can start using a schema. If your schema includes a todos.dueDate for example:
// instant.schema.ts const _schema = i.schema({ entities: { todos: i.entity({ // ... dueDate: i.date(), }), }, // ... }); // ...
Instant will enforce that todos.dueDate are actually dates, and you'll get some nice intellisense to boot:

Instant also comes with a few utility types, which can help you write abstractions over transact. For example, say you wanted to write a custom update function:
// Goal myCustomUpdate('todos', { dueDate: Date.now() });
You can use the UpdateParams utility to make sure arguments follow the schema:
import { UpdateParams } from '@instantdb/react'; import { AppSchema } from '../instant.schema.ts'; type EntityTypes = keyof AppSchema['entities']; function myCustomUpdate<EType extends EntityTypes>( etype: EType, args: UpdateParams<AppSchema, EType>, ) { // .. }
And the LinkParams utility do the same for links:
import { LinkParams } from '@instantdb/react'; import { AppSchema } from '../instant.schema.ts'; type EntityTypes = keyof AppSchema['entities']; function myCustomLink<EType extends EntityTypes>( etype: EType, args: LinkParams<AppSchema, EType>, ) { // .. }
To learn more about writing schemas, check out the Modeling Data section.
Batching transactions
If you have a large number of transactions to commit, you'll want to batch them to avoid hitting transaction limits and time outs.
Suppose we want to create 3000 goals. Here's how we can batch them into 30 transactions of 100 goals each.
const batchSize = 100; // doing 100 txs should be pretty safe const createGoals = async (total) => { let goals = []; const batches = []; // iterate through all your goals and create batches for (let i = 0; i < total; i++) { const goalNumber = i + 1; goals.push( db.tx.goals[id()].update({ goalNumber, title: `Goal ${goalNumber}` }), ); // We have enough goals to create a batch if (goals.length >= batchSize) { batches.push(goals); goals = []; // reset goals for the next batch } } // Add any remaining goals to the last batch if (goals.length) { batches.push(goals); } // Now that you have your batches, transact them for (const batch of batches) { await db.transact(batch); } };
Using the tx proxy object
db.tx is a proxy object which creates transaction chunks to be committed via db.transact. It follows the format
db.tx.NAMESPACE_LABEL[ENTITY_IDENTIFIER].ACTION(ACTION_SPECIFIC_DATA)
NAMESPACE_LABELrefers to the namespace to commit (e.g.goals,todos)ENTITY_IDENTIFIERis the id to look up in the namespace. This id must be a uuid and unique to the namespace. You can use theid()function to generate a uuid for convenience.ACTIONis one ofcreate,update,merge,delete,link,unlinkACTION_SPECIFIC_DATAdepends on the actioncreateandupdatetake in an object of information to commitmergetakes in an object to deep merge with the existing datadeleteis the only action that doesn't take in any data,linkandunlinktakes an object of label-entity pairs to create/delete associations