shuttle-hq / shuttle

Build & ship backends without writing any infrastructure files.
https://shuttle.rs
Apache License 2.0
6.04k stars 250 forks source link

[Feature]: Add a Shuttle KV Store #944

Closed morlinbrot closed 8 months ago

morlinbrot commented 1 year ago

Describe the feature

Both Deno and Vercel recently added a KV Store to their offering which I believe would be a very useful option for Shuttle too, especially in the context of shuttle-next. A simple, built-in KV Store API goes a long way in enabling spinning up a web application quickly and would go a long way in making onboarding to Shuttle very easy, IMHO.

I do realise that there is shuttle-persist already but I think backing this with a proper db engine would make it more durable and portable. In this context, this request could be regarded as an upgrade to the existing shuttle-persist.

The above mentioned services use different engines to provide the functionality, afaik:

We are currently in the process of adding SQLite support, and having Redis support will probably also be a great feature to have in the future, too. In any case, I think a simpler, built-in KV Store abstraction would be a nice addition, especially for attracting new users to Shuttle.

Let me know what you think, I'd be glad to spend some time on this after adding the SQLite support.

Suggestion or Example of how the feature would be used

I love the simplicity of Deno's KV API so here's an adaptation of that plus the existing persist example:

#[derive(Deserialize, Serialize)]
struct User {
    name: String,
}

async fn do_stuff(State(state): State<Arc<AppState>>) -> impl IntoResponse {
    // Open the default database.
    let kv = await state.kv.openKv().await;

    // Persist an object at the users/alice key.
    kv.set::<User>(["users", "alice"], { name: "Alice" }).await.unwrap();

    // Read back this key.
    let res: KvEntry<User> = kv.get(["users", "alice"]).await.unwrap();
    info!(res.key) // [ "users", "alice" ]
    info!(res.value); // Some(User { name: "Alice" })

    // Delete the key.
    kv.delete(["users", "alice"]).await.unwrap();

    // Reading back the key now returns null.
    let res2: KvEntry<User> = kv.get(["users", "alice"]).await.unwrap();
    info!(res2.key); // [ "users", "alice" ]
    info!(res2.value); // None
}

#[derive(Clone)]
pub struct AppState {
    kv: shuttle_kv::KvInstance,
}

#[shuttle_runtime::main]

async fn axum(
    #[shuttle_kv::Kv] kv: shuttle_kv::KvInstance
) -> shuttle_axum::ShuttleAxum {
    let state = Arc::new(AppState { kv });

    let router = Router::new().route("/", get(do_stuff)).with_state(state);

    Ok(router.into())
}

Duplicate declaration

KaviiSuri commented 1 year ago

I would love to contribute to this, is it okay if I pick this issue?

morlinbrot commented 1 year ago

Hi @KaviiSuri, as mentioned in the OP, i'm currently working on adding SQLite support to shuttle, I think it would be worth waiting for that to land before starting work on this one.

Also, I think there is some deliberation by the Shuttle team needed before we can get started here, esp. for figuring out how this would relate to shuttle-persist and which engine should be used to power the functionality.

KaviiSuri commented 1 year ago

No issues! Really excited about the project, feel free to let me know if there's anything I can contribute to!

jonaro00 commented 8 months ago

Closing in favour of #1567 #1565 #1321