Reading and Writing Data
Read and writing data with Indesely, just like IndexedDB, starts with a transaction. Once a transaction is started, you may read or write data to the database depending on the kind of transaction.
Starting Transactions
Transactions come in three flavors; read-only, read/write, and upgrade. You can read data in any type of transaction. But, you write data, you will need a read/write or upgrade transaction.
INFO
Upgrade transactions are only available during migrations, and can perform the same functions as read/write transactions. You can read more about them and Migrations.
To start a transaction, you will call either Database.read
or Database.change
with a list of object store you will interact with. For example:
// Start a read-only transaction.
await db.read(['employees'], async (trx) => {
/* ... */
});
// Start a read/write transaction.
await db.change(['employees'], async (trx) => {
/* ... */
});
Any attempt to interact with any store other than the ones specified will result in an error.
Interacting with IndexedDB using Indesely is done using a fluent query API much like that of Kysely. This abstracts some aspects of IndexedDB into easier to read code.
Inserting and Updating Records
Before you can read any data from the database, you must first put some data in it. Inserting data is handle by the an update query started by call update
on the transaction. This may only be done in a read/write or upgrade transaction.
db.change(['employees'], async (trx) => {
trx.update('employees');
});
Adding data
To add new data, you would call add
to finish the update query.
await db.change(['employees'], async (trx) => {
// If key is from `id`
await trx.update('employees').add({ id: 4, name: 'River Tam' });
// or; if the key must be manually specified.
await trx.update('employees').add({ name: 'River Tam' }, 4);
});
With add, no other record may share the same key. So, calling add with a duplicate key, manual or otherwise, will result in an error.
Updating data
With IndexedDB, you can add new records and update existing ones using the same function. This is typically called an upsert. To upsert records, you call put
to finish the query.
await db.change(['employees'], async (trx) => {
// If key is from `id`
await trx.update('employees').put({ id: 5, name: 'Kaywinnet Lee Frye' });
// or; if the key must be manually specified.
await trx.update('employees').put({ name: 'Kaywinnet Lee Frye' }, 5);
// Updating employee 5
// If key is from `id`
await trx.update('employees').put({ id: 5, name: 'Kaywinnet Lee Tam' });
// or; if the key must be manually specified.
await trx.update('employees').put({ name: 'Kaywinnet Lee Tam' }, 5);
});
Unlike add
, put
will replace a record if one already exists with the same key. But keep in mind; IndexedDB, unlike SQL, does not have a dedicated update operation that will fail if not such record exists.
Checkout the Update Query Builder API Reference for more information.
Reading Records and Keys
Once you have data in your database, you can now read that data. Reading data is done through a select query, started by call selectFrom
. This may only be done in any transaction.
await db.read(['employees'], async (trx) => {
trx.selectFrom('employees');
});
You can get data a few different way from an object store. For example, you can query all records on the current store using getAll
.
const employee = await db.read(['employees'], async (trx) => {
return await trx.selectFrom('employees').getAll();
});
You can also stream all the records using a cursor
or async iterator function such as stream
.
await db.read(['employees'], async (trx) => {
for await (const record of trx.selectFrom('employees').stream()) {
dataTable.add(record):
}
});
But, if you want to get specific records, or a range of records; you will need to constrain your query based on the store's key or indices.
Querying on Keys and Indices
To constrain a query, you will need to add a where clause to it. Doing so allows you to query one specific records, or records that or within a certain range of keys. For example, to select only one employee by their key. You will need to call whereKey
or where
with the =
operator. Then, execute the query with getFirst
.
const employee = await db.read(['employees'], async (trx) => {
return await trx.selectFrom('employees').whereKey('=', 5).getFirst();
});
You can also get a range of records based on a key or index. This can be done by with any of the relative comparison or range operators. For example, to get any employee with a salary greater than $80K.
const employees = await db.read(['employees'], async (trx) => {
return await trx.selectFrom('employees').where('salary', '>', 80_000).getAll();
});
Or maybe you can get those in a range between and including $80K and 125K.
const employees = await db.read(['employees'], async (trx) => {
return await trx.selectFrom('employees').where('salary', '[]', 80_000, 125_000).getAll();
});
To learn about more where clause operators, see the Where Operators reference.
Once a index where clause is added to the query, the records it retrieves will be sorted by the keys of that index. But, if you want to sort by an index without adding a where clause, you can use the by
clause.
const employees = await db.read(['employees'], async (trx) => {
return await trx.selectFrom('employees').by('salary').getAll();
});
Keep in mind, you can only use getFirst
and similar calls after a where clause has been added to the query. getAll
, stream
, and similar calls are available at any point. You cannot add additional where clauses to a query. Also, the by
clause is actually a no-comparison where clause, and cannot be mixed with other where clauses.
To see more selection functionality, checkout the Select Query Builder API Reference.
Deleting Records
When you need to remove data from your stores, you just have to use a delete query by calling deleteFrom
on the transaction. You can either delete everything
in a store.
await db.change(['employees'], async (trx) => {
await trx.deleteFrom('employees').everything();
});
You can also execute a delete on a specific key or range of keys using whereKey
. This uses the same operators as the selection where, but immediately executes the deletion once called.
await db.change(['employees'], async (trx) => {
await trx.deleteFrom('employees').whereKey('=', 4);
});
To learn more about, see the Delete Query Builder API Reference.
What's Next?
- Learn how to list and delete database by reading Managing Database.