Skip to content

Quick Start

Build a complete HTTP handler in minutes. This guide covers the essential patterns you’ll use in every mik-sdk project.

  1. Create the project structure

    Terminal window
    cargo new --lib hello-handler
    cd hello-handler
  2. Configure Cargo.toml

    [package]
    name = "hello-handler"
    version = "0.1.0"
    edition = "2024"
    [lib]
    crate-type = ["cdylib"]
    [dependencies]
    mik-sdk = "0.1"
    [package.metadata.component]
    package = "hello:handler"
    [package.metadata.component.target.dependencies]
    "mik:core" = { path = "wit/deps/core" }
  3. Add WIT files

    Download the WIT interface:

    Terminal window
    mkdir -p wit/deps/core
    curl -L https://github.com/dufeutech/mik-sdk/releases/latest/download/core.wit \
    -o wit/deps/core/core.wit

    Create wit/world.wit:

    package hello:handler;
    world handler {
    include mik:core/handler-world;
    }
  4. Write your handler

    src/lib.rs
    #[allow(warnings)]
    mod bindings;
    use bindings::exports::mik::core::handler::{self, Guest, Response};
    use mik_sdk::prelude::*;
    // Define input types
    #[derive(Path)]
    pub struct HelloPath {
    pub name: String,
    }
    #[derive(Type)]
    pub struct HelloResponse {
    pub greeting: String,
    }
    // Define routes
    routes! {
    GET "/" => home,
    GET "/hello/{name}" => hello(path: HelloPath) -> HelloResponse,
    }
    // Implement handlers
    fn home(_req: &Request) -> Response {
    ok!({
    "message": "Welcome to mik-sdk!",
    "endpoints": ["/", "/hello/{name}"]
    })
    }
    fn hello(path: HelloPath, _req: &Request) -> Response {
    ok!({
    "greeting": format!("Hello, {}!", path.name)
    })
    }
  5. Build the component

    Terminal window
    cargo component build --release
  6. Get the bridge and compose

    Terminal window
    # Download the bridge (if you don't have it)
    curl -LO https://github.com/dufeutech/mik-sdk/releases/latest/download/mik-bridge.wasm
    # Compose handler with bridge
    wac plug mik-bridge.wasm \
    --plug target/wasm32-wasip2/release/hello_handler.wasm \
    -o service.wasm
  7. Run locally

    Terminal window
    wasmtime serve -S cli=y service.wasm
  8. Test your handler

    Terminal window
    curl http://localhost:8080/
    curl http://localhost:8080/hello/World
#[allow(warnings)]
mod bindings;

The bindings module is generated by cargo-component from your WIT files. It provides the typed interface for your handler.

#[derive(Path)]
pub struct HelloPath {
pub name: String, // Matches {name} in the route
}
#[derive(Type)]
pub struct HelloResponse {
pub greeting: String,
}
MacroPurpose
#[derive(Type)]JSON request/response body
#[derive(Path)]URL path parameters
#[derive(Query)]Query string parameters
routes! {
GET "/" => home,
GET "/hello/{name}" => hello(path: HelloPath) -> HelloResponse,
}

The routes! macro:

  • Maps HTTP methods and paths to handler functions
  • Extracts typed inputs (path, query, body)
  • Generates the Guest implementation
  • Generates OpenAPI schema (via cargo test __mik_write_schema)
fn hello(path: HelloPath, _req: &Request) -> Response {
ok!({ "greeting": format!("Hello, {}!", path.name) })
}

Handlers receive:

  • Typed inputs (path, query, body) - already parsed and validated
  • The raw &Request for accessing headers, raw body, etc.
#[derive(Query)]
pub struct SearchQuery {
pub q: Option<String>,
#[field(default = 1)]
pub page: u32,
#[field(default = 20, max = 100)]
pub limit: u32,
}
routes! {
GET "/search" => search(query: SearchQuery),
}
fn search(query: SearchQuery, _req: &Request) -> Response {
ok!({
"query": query.q,
"page": query.page,
"limit": query.limit
})
}
#[derive(Type)]
pub struct CreateUserInput {
#[field(min = 1, max = 100)]
pub name: String,
pub email: String,
}
routes! {
POST "/users" => create_user(body: CreateUserInput),
}
fn create_user(body: CreateUserInput, _req: &Request) -> Response {
// body is already parsed and validated
let id = random::uuid();
created!(format!("/users/{}", id), {
"id": id,
"name": body.name,
"email": body.email
})
}
fn get_user(path: Id, _req: &Request) -> Response {
let user_id = path.as_str();
// Guard macro for validation
guard!(!user_id.is_empty(), 400, "User ID required");
// Ensure macro for Option/Result
let user = ensure!(find_user(user_id), 404, "User not found");
ok!({ "user": user })
}
  • Routing - Deep dive into the routing system
  • Request - Access headers, body, and more
  • Responses - All response macros
  • Examples - Complete working examples