Skip to Content
We are live but in Staging πŸŽ‰

Rust

The Rust compile path is a managed runtime β€” you write an annotated handler and the platform compiles it to a native Linux binary (or a Wasm component) and runs it. The compiled artifact is small and starts fast. The platform owns the HTTP server, the listening port, and the readiness probe; you only write the handler.

Handler contract

Annotate a function named handler with #[ignite::main]:

#[ignite::main] async fn handler(req: Request, ctx: Context) -> Result<impl IntoResponse>

A synchronous fn handler(...) is also accepted. The #[ignite::main] macro provides the HTTP server, request decoding, and context wiring.

A minimal real handler (from code_sources/rust-hello/src/lib.rs):

use ignite::prelude::*; use serde_json::json; #[ignite::main] async fn handler(req: Request, ctx: Context) -> Result<impl IntoResponse> { let message: String = req .field("message") .unwrap_or_else(|_| "Hello from Rust!".to_string()); Ok(json!({ "message": message, "execution_id": ctx.execution_id(), "function_id": ctx.function_id(), })) }

Context

Context exposes methods (not fields):

MethodReturns / does
ctx.execution_id()&str β€” this invocation’s ID
ctx.function_id()&str β€” the app ID, in "org/func" form
ctx.env()environment access
ctx.temp_store()per-execution scratch store
ctx.stream()streaming response handle

Input & output

The request body is parsed as JSON into Request. Read it with:

  • `req.field::<T>("key")` β€” a single typed field
  • `req.body::<T>()` β€” the whole body deserialized into T
  • req.raw() β€” the raw bytes
  • HTTP accessors: req.method(), req.path(), req.query_param(), req.header()

Return any impl IntoResponse (for example `ignite::json!({...})`); it is serialized to JSON and returned as HTTP 200. An Err return or a panic yields HTTP 500 with a JSON body of the form `{error}`.

Dependencies

Declare crates in Cargo.toml at the archive root. The compiler auto-injects ignite, serde (with derive), serde_json, and tokio if they are absent, so you can omit them.

For the ignite dependency:

  • In a standalone project, use `ignite = "*"` β€” the compile pod swaps in the bundled SDK at build time.
  • The in-repo fixture instead uses `ignite = { workspace = true }`.

Project layout

Ship an archive (zip / tar.gz) with Cargo.toml at the root. The #[ignite::main]-annotated handler lives where you’d expect Cargo to find it β€” conventionally src/lib.rs or src/main.rs (the sample fixture is code_sources/rust-hello/src/lib.rs). There is no environment-variable entrypoint override.

Logging

Use eprintln! (stderr) for logs in the serve loop β€” not println!.

What it compiles to

The target is selected with --rust-target native|wasm:

  • rust-native β†’ a native Linux binary, default target x86_64-unknown-linux-gnu (dynamically linked).
  • rust-wasm β†’ wasm32-wasip2 (a Component Model module that runs under Wasmtime).

Toolchain: Rust 1.93.

Deploy

Rust needs an explicit compile step before publishing:

dodil ignite app create hello --runtime rust --rust-target native dodil ignite draft save org:hello --code ./ dodil ignite draft compile org:hello dodil ignite draft publish org:hello dodil ignite invoke org:hello -p '{"message": "hi there"}'

See also