#indexed-db #future #idb

indexed_db_futures

Future bindings for IndexedDB via web_sys

13 unstable releases (5 breaking)

0.6.0 Oct 27, 2024
0.5.0 Jul 26, 2024
0.4.2 Jul 26, 2024
0.4.1 Oct 29, 2023
0.1.0 Jul 25, 2021

#23 in WebAssembly

Download history 3078/week @ 2024-08-21 3867/week @ 2024-08-28 4126/week @ 2024-09-04 3123/week @ 2024-09-11 3028/week @ 2024-09-18 3020/week @ 2024-09-25 2773/week @ 2024-10-02 2471/week @ 2024-10-09 2999/week @ 2024-10-16 2369/week @ 2024-10-23 2805/week @ 2024-10-30 2376/week @ 2024-11-06 3207/week @ 2024-11-13 3146/week @ 2024-11-20 4245/week @ 2024-11-27 2237/week @ 2024-12-04

13,316 downloads per month
Used in 31 crates (9 directly)

MIT license

265KB
6.5K SLoC

Wraps the web_sys Indexed DB API in a Future-based API and removes the pain of dealing with JS callbacks or JSValue in Rust.

master CI badge crates.io badge docs.rs badge dependencies badge

Goals & features:

  • Shield you from having to interact with web_sys or js_sys APIs - this should feel like a native Rust API.
  • Integrate with serde, but don't require it - as a rule of thumb, you'll use serde-serialisable types when working with JS objects & bypass serde for Javascript primitives.
  • Implement Stream where applicable - cursors and key cursors have this at the time of writing.
  • Implement a more Rust-oriented API - for example, transactions will roll back by default unless explicitly committed to allow you to use ?s.
use indexed_db_futures::database::Database;
use indexed_db_futures::prelude::*;
use indexed_db_futures::transaction::TransactionMode;

#[derive(serde::Serialize, serde::Deserialize)]
struct MySerdeType(u8, String);

async fn main() -> indexed_db_futures::OpenDbResult<()> {
    let db = Database::open("my_db")
        .with_version(2u8)
        .with_on_upgrade_needed(|event, db| {
            match (event.old_version(), event.new_version()) {
                (0.0, Some(1.0)) => {
                    db.create_object_store("my_store")
                        .with_auto_increment(true)
                        .build()?;
                }
                (prev, Some(2.0)) => {
                    if prev == 1.0 {
                        let _ = db.delete_object_store("my_store");
                    }

                    db.create_object_store("my_other_store").build()?;
                }
                _ => {}
            }

            Ok(())
        })
        .await?;

    // Populate some data
    let transaction = db
        .transaction("my_other_store")
        .with_mode(TransactionMode::Readwrite)
        .build()?;

    let store = transaction.object_store("my_other_store")?;

    store
        .put("a primitive value that doesn't need serde")
        .await?;

    // awaiting individual requests is optional - they still go out
    store.put(MySerdeType(10, "foos".into())).serde()?;

    // Unlike JS, transactions ROLL BACK INSTEAD OF COMMITTING BY DEFAULT
    transaction.commit().await?;

    // Read some data
    let transaction = db.transaction("my_other_store").build()?;
    let store = transaction.object_store("my_other_store")?;
    let Some(mut cursor) = store.open_cursor().await? else {
        // `None` is returned if the cursor is empty
        return Ok(());
    };

    loop {
        match cursor.next_record_ser::<MySerdeType>().await {
            Ok(Some(record)) => handle_record(record),
            Ok(None) => break,
            Err(e) => handle_error(e),
        }
    }

    Ok(())
}

Head over to the docs for a proper introduction!

Dependencies

~10–18MB
~238K SLoC