inngest-rs

22
3
22
4
Rust
public


Write durable functions in Rust via the Inngest SDK.
Read the documentation and get started in minutes.

crates.io
discord
twitter


Inngest Rust SDK - Alpha

Inngest’s SDK adds durable functions to Rust in a few lines of code. Using this SDK, you can write background jobs, and workflows
as step functions without the need to setup queueing infrastructure.

We currently support the following web frameworks:

If there are other frameworks you like to see, feel free to submit an issue, or add to the roadmap.

Supported features

Features Support
step.run :white_check_mark:
step.sleep :white_check_mark:
step.wait_for_event :white_check_mark:
step.invoke :white_check_mark:
step.send_event :white_check_mark:
CancelOn :white_check_mark:
Failure handler :white_check_mark:
Retry controls :white_check_mark:
Middleware :x:
Connect :x:

Getting Started

[dependencies]
inngest = "0.1"

Examples

use axum::{
  routing::get,
  Router
};
use tokio::net::TcpListener;

#[tokio::main]
async fn main() {
    let client = Inngest::new("rust-app");
    let mut inngest_handler = Handler::new(&client);
    inngest_handler.register_fns(vec![
         hello_fn(&client).into(),
         step_run_fn(&client).into(),
    ]);

    let inngest_state = Arc::new(inngest_handler);

    let app = Router::new()
        .route("/", get(|| async { "OK!\n" }))
        .route(
            "/api/inngest",
            get(serve::axum::introspect)
                .put(serve::axum::register)
                .post(serve::axum::invoke),
        )
        .with_state(inngest_state);

    let listener = TcpListener::bind("[::]:3000").await.unwrap();

    axum::serve(listener, app.into_make_service())
        .await
        .unwrap();
}

#[derive(Serialize, Deserialize, Debug)]
struct HelloEventData {
    msg: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct StepRunEventData {
    name: String,
    data: u8,
}

fn hello_fn(client: &Inngest) -> ServableFn<HelloEventData, Error> {
    client.create_function(
        FunctionOpts::new("hello-func").name("Hello func"),
        Trigger::event("test/hello"),
        |input: Input<HelloEventData>, step: StepTool| async move {
            let step_res = into_dev_result!(
                step.run("fallible-step-function", || async move {
                    // if even, fail
                    if input.ctx.attempt % 2 == 0 {
                        return Err(UserLandError::General(format!(
                            "Attempt {}",
                            input.ctx.attempt
                        )));
                    }

                    Ok(json!({ "returned from within step.run": true }))
                }).await
            )?;

            step.sleep("sleep-test", Duration::from_secs(3))?;

            let evt: Option<Event<Value>> = step.wait_for_event(
                "wait",
                WaitForEventOpts {
                    event: "test/wait".to_string(),
                    timeout: Duration::from_secs(60),
                    if_exp: None,
                },
            )?;

            Ok(json!({ "hello": true, "evt": evt, "step": step_res }))
        },
    )
}

fn step_run_fn(client: &Inngest) -> ServableFn<StepRunEventData, Error> {
    client.create_function(
        FunctionOpts::new("step-run").name("Step run"),
        Trigger::event("test/step-run"),
        |_input: Input<StepRunEventData>, _step: StepTool| async move {
            Ok(json!({ "step": true }))
        },
    )
}

The handler can register functions with different event payload types on the same app. When batching them with register_fns(...), convert each function with .into() as shown above.

Function definitions also support sync metadata such as cancel, idempotency, batch_events, rate_limit, debounce, priority, concurrency, throttle, singleton, and timeouts.

Failure handlers use a Rust-style on_failure(...) method on the returned ServableFn:

fn hello_with_failure_fn(client: &Inngest) -> ServableFn<HelloEventData, Error> {
    client
        .create_function(
            FunctionOpts::new("hello-func"),
            Trigger::event("test/hello"),
            |input: Input<HelloEventData>, _step: StepTool| async move {
                Ok(json!({ "hello": input.event.data.msg }))
            },
        )
        .on_failure(
            |input: Input<FunctionFailureEvent<HelloEventData>>, _step: StepTool| async move {
                Ok(json!({
                    "failed_run_id": input.event.data.run_id,
                    "message": input.event.data.error.message,
                    "original": input.event.data.event.data.msg,
                }))
            },
        )
}
v0.3.3[beta]