Skip to content

Responses

mik-sdk provides a rich set of macros for building HTTP responses with proper status codes, headers, and JSON bodies.

#[allow(warnings)]
mod bindings;
use bindings::exports::mik::core::handler::{self, Guest, Response};
use mik_sdk::prelude::*;

The Response type comes from the bindings module. All response macros (ok!, created!, error!, etc.) are included in mik_sdk::prelude::*.

The most common response for successful requests:

fn get_user(path: Id, _req: &Request) -> Response {
ok!({
"id": path.as_str(),
"name": "Alice",
"email": "alice@example.com"
})
}

Output:

{ "id": "123", "name": "Alice", "email": "alice@example.com" }

Variables work directly in ok! without explicit type hints:

fn handler(_req: &Request) -> Response {
let name = "Alice".to_string();
let age: i32 = 30;
let active = true;
let tags: Vec<&str> = vec!["admin", "user"];
let score: Option<f64> = Some(95.5);
ok!({
"name": name, // String -> JSON string
"age": age, // i32 -> JSON integer
"active": active, // bool -> JSON boolean
"tags": tags, // Vec -> JSON array
"score": score // Option -> value or null
})
}

For resource creation, includes a Location header:

fn create_user(body: CreateInput, _req: &Request) -> Response {
let id = random::uuid();
created!(format!("/users/{}", id), {
"id": id,
"name": body.name
})
}

Response headers include:

Location: /users/550e8400-e29b-41d4-...

For async processing requests:

fn queue_job(body: JobInput, _req: &Request) -> Response {
let job_id = random::uuid();
// Queue the job...
accepted!()
}

For successful operations with no response body:

fn delete_user(path: Id, _req: &Request) -> Response {
// Delete the user...
no_content!()
}
fn old_endpoint(_req: &Request) -> Response {
redirect!("/api/v2/users")
}

For other redirect codes, use the full error! macro with a Location header.

Convenient shortcuts for common error responses:

// 400 Bad Request
fn handler(_req: &Request) -> Response {
bad_request!("Invalid email format")
}
// 403 Forbidden
fn protected(_req: &Request) -> Response {
forbidden!("Access denied")
}
// 404 Not Found
fn get_item(path: Id, _req: &Request) -> Response {
not_found!("Item not found")
}
// 409 Conflict
fn create_item(_req: &Request) -> Response {
conflict!("Item already exists")
}

For detailed error responses following RFC 7807:

fn handler(_req: &Request) -> Response {
error! {
status: 400,
title: "Validation Error",
detail: "The request body contains invalid data"
}
}

Output:

{
"type": "about:blank",
"title": "Validation Error",
"status": 400,
"detail": "The request body contains invalid data"
}
fn handler(_req: &Request) -> Response {
error! {
status: status::UNPROCESSABLE_ENTITY,
title: "Validation Error",
detail: "Email address is invalid",
problem_type: "urn:problem:validation",
instance: "/users/123",
meta: {
"field": "email",
"code": "invalid_format"
}
}
}

Output:

{
"type": "urn:problem:validation",
"title": "Validation Error",
"status": 422,
"detail": "Email address is invalid",
"instance": "/users/123",
"field": "email",
"code": "invalid_format"
}

Return early if a condition is false:

fn create_user(body: CreateInput, _req: &Request) -> Response {
guard!(!body.name.is_empty(), 400, "Name is required");
guard!(body.name.len() <= 100, 400, "Name too long");
guard!(body.email.contains('@'), 400, "Invalid email");
// Continue with valid data...
ok!({ "created": true })
}

If any guard fails, returns:

{
"type": "about:blank",
"title": "Bad Request",
"status": 400,
"detail": "Name is required"
}

Unwrap Option or Result, or return an error:

fn get_user(path: Id, _req: &Request) -> Response {
// From Option
let user = ensure!(find_user(path.as_str()), 404, "User not found");
// From Result
let data = ensure!(parse_data(), 400, "Invalid data");
ok!({ "user": user.name })
}
fn find_user(id: &str) -> Option<User> {
// ...
}
fn parse_data() -> Result<Data, Error> {
// ...
}

Use predefined constants for clarity:

use mik_sdk::prelude::status;
fn handler(_req: &Request) -> Response {
error! {
status: status::NOT_FOUND,
title: "Not Found",
detail: "Resource does not exist"
}
}

Available constants:

ConstantValueDescription
status::OK200OK
status::CREATED201Created
status::ACCEPTED202Accepted
status::NO_CONTENT204No Content
status::MOVED_PERMANENTLY301Moved Permanently
status::FOUND302Found (redirect)
status::NOT_MODIFIED304Not Modified
status::TEMPORARY_REDIRECT307Temporary Redirect
status::PERMANENT_REDIRECT308Permanent Redirect
status::BAD_REQUEST400Bad Request
status::UNAUTHORIZED401Unauthorized
status::FORBIDDEN403Forbidden
status::NOT_FOUND404Not Found
status::METHOD_NOT_ALLOWED405Method Not Allowed
status::NOT_ACCEPTABLE406Not Acceptable
status::CONFLICT409Conflict
status::GONE410Gone
status::UNPROCESSABLE_ENTITY422Unprocessable Entity
status::TOO_MANY_REQUESTS429Too Many Requests
status::INTERNAL_SERVER_ERROR500Internal Server Error
status::NOT_IMPLEMENTED501Not Implemented
status::BAD_GATEWAY502Bad Gateway
status::SERVICE_UNAVAILABLE503Service Unavailable
status::GATEWAY_TIMEOUT504Gateway Timeout
MacroStatusUse Case
ok!({ ... })200Successful response with JSON
created!(loc, { ... })201Resource created
accepted!()202Async processing accepted
no_content!()204Success, no body
redirect!(url)302Redirect to URL
bad_request!(msg)400Invalid request
forbidden!(msg)403Access denied
not_found!(msg)404Resource not found
conflict!(msg)409Conflict with state
error! { ... }anyFull RFC 7807 error