ZEON256 ZEON256
← BACK

Announcing satay-rs: A Sans-IO Rust OpenAPI client generator with typed builders, validation newtypes, and transport-agnostic adapters

ZEON256
ZEON256 2026-06-05 2 min read
rust openapi sans-io sdk
Target Audience Rust developers SDK library maintainers API integrators
AI Summary
Announcing satay-rs, a Sans-IO Rust OpenAPI client generator inspired by rusty-s3. Motivated by the maintenance burden of supporting blocking vs async and multiple HTTP backends in lta-rs, satay-rs generates high-quality Rust from OpenAPI 3.1 specs with typed builders, validation newtypes for constrained strings, numbers, and arrays, and automatic integer width deduction from bounds. Includes optional reqwest and ureq transport adapters, and walks through an example calling LTA's Datamall bus arrival API.

I would like to announce the arrival of satay-rs, a Sans-IO Rust OpenAPI client generator with typed builders, validation newtypes, and transport-agnostic adapters. If you are writing SDK libraries, you will only have to maintain the OpenAPI spec without having to worry about generating high quality Rust and no more maintaining client backends that might have different execution style (blocking vs async). No more maintaining implementation for different clients like reqwest vs surf or whatever you are using!

Motivation

I’ve been bitten by the sans-io bug after I used rusty-s3 and got so inspired by it that I told myself that all SDK clients that I am gonna make will be sans-io. After maintaining lta-rs with multiple different implementation for blocking, async, different client backends, sans-io approach is the most reasonable one. This will significantly reduce maintainence time as well as bugs cos we no longer have to depend on those backends! Another reason why I started this project is because there isn’t any OpenAPI generator for Rust that generates adequately high quality Rust code that is nice to use from a DX perspective and I want to change that.

Features

  • Generates from OpenAPI 3.1 documents
  • Sans-IO design from the ground up, with optional transport adapters for reqwest and ureq
  • Validation newtypes for OpenAPI string, number, integer, and array constraints
  • Automatic number type deduction from specified bounds (i.e. if maximum is less than u8::MAX, the generated type will be a u8 newtype instead of u64)

Example usage of the generated client

Here you will be calling LTA’s datamall API for bus arrival timings at a particular bus stop.

Rust
include!(concat!(env!("OUT_DIR"), "/satay_generated.rs"));

use generated::{Api, GetBusArrivalResponse};
use satay_reqwest::{ReqwestActionExt, reqwest};
use std::{env, error::Error};

use crate::generated::BusServiceNumber;

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let api = Api::new().account_key(env::var("LTA_ACCOUNT_KEY")?);

    let client = reqwest::Client::new();
    let response = api
        .get_bus_arrival(83139)
        .service_no(BusServiceNumber::try_new("15")?)
        .send_with(&client)
        .await?;

    match response {
        GetBusArrivalResponse::Ok(arrival) => {
            println!("{:?}", arrival);
        }
        GetBusArrivalResponse::UnexpectedStatus(status, body) => {
            eprintln!(
                "unexpected status {status}: {}",
                String::from_utf8_lossy(&body)
            );
        }
    }

    Ok(())
}

Download now

You can download the latest version of satay-rs from crates.io and the source code is available on GitHub. If you have any questions, suggestions, or want to contribute, feel free to open an issue or a pull request on the GitHub repository. Happy coding!