Introduction
reflectapi is a library and a toolkit for writing web API services in Rust and generating compatible clients, delivering great development experience and efficiency.
Why reflectapi?
https://rustforgeconf.com/talks
Core Philosophy
- Rust code first definition of the API interface
- Full respect for all serde attributes
- Extensible at many places
Ready to Start?
Head over to Quick Start to build your first API with reflectapi!
Quick Start
This guide will have you up and running with reflectapi in under 5 minutes.
Prerequisites
- Rust 1.78.0 or later
- Basic familiarity with Rust and web APIs
Create a New Project
cargo new my-api
cd my-api
Add Dependencies
Add reflectapi to your Cargo.toml:
[dependencies]
reflectapi = { version = "0.15", features = ["builder", "axum"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
axum = "0.7"
Define Your API
Replace the contents of src/main.rs:
// This is a complete example for src/main.rs
// Add these dependencies to your Cargo.toml first:
// [dependencies]
// reflectapi = { version = "0.15", features = ["builder", "axum"] }
// serde = { version = "1.0", features = ["derive"] }
// serde_json = "1.0"
// tokio = { version = "1.0", features = ["full"] }
use reflectapi::{Builder, Input, Output};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Input, Output)]
struct User {
id: u32,
name: String,
email: String,
}
#[derive(Serialize, Deserialize, Input)]
struct CreateUserRequest {
name: String,
email: String,
}
// Handler functions need specific signatures for reflectapi
async fn create_user(_state: (), req: CreateUserRequest, _headers: ()) -> User {
// In a real app, you'd save to a database
User {
id: 1,
name: req.name,
email: req.email
}
}
async fn get_user(_state: (), id: u32, _headers: ()) -> Option<User> {
// In a real app, you'd query a database
if id == 1 {
Some(User {
id: 1,
name: "Alice".to_string(),
email: "alice@example.com".to_string(),
})
} else {
None
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Build the API schema
let builder = Builder::new()
.name("User API")
.description("A simple user management API")
.route(create_user, |route| {
route
.name("users.create")
.description("Create a new user")
})
.route(get_user, |route| {
route
.name("users.get")
.description("Get a user by ID")
});
let (schema, routers) = builder.build()?;
// Save schema for client generation
let schema_json = serde_json::to_string_pretty(&schema)?;
std::fs::write("reflectapi-schema.json", schema_json)?;
println!("✅ API schema generated at reflectapi-schema.json");
// Start the HTTP server
let app_state = (); // No state needed for this example
let axum_app = reflectapi::axum::into_router(app_state, routers, |_name, r| r);
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
println!("🚀 Server running on http://0.0.0.0:3000");
println!("📖 Ready to generate clients!");
axum::serve(listener, axum_app).await?;
Ok(())
}
Run Your API Server
cargo run
You should see:
✅ API schema generated at reflectapi-schema.json
🚀 Server running on http://0.0.0.0:3000
📖 Ready to generate clients!
🎉 Congratulations! You now have a running API server and generated client-ready schema.
Generate a Client
First, install the CLI:
cargo install reflectapi-cli
Then generate a TypeScript client:
mkdir clients
reflectapi-cli codegen --language typescript --schema reflectapi-schema.json --output clients/typescript
Use Your Generated Client
The generated TypeScript client will be fully typed:
import { client } from './clients/typescript';
const c = client('http://localhost:3000');
// Create a user - fully type-safe!
const newUser = await c.users.create({
name: 'Bob',
email: 'bob@example.com'
});
// Get a user
const user = await c.users.get(1);
That's it!
Installation
Get reflectapi up and running in minutes.
Basic Setup
cargo add reflectapi --features builder,axum,json,chrono
CLI Tool
Install the CLI tool to generate client libraries:
cargo install reflectapi-cli --help
Next Steps
- New users: Follow the Quick Start guide
Client Generation
reflectapi automatically generates type-safe client libraries for multiple programming languages from your Rust API server. This section covers how to generate and use clients in different languages.
Supported Languages
| Language | Status |
|---|---|
| TypeScript | ✅ Stable |
| Rust | ✅ Stable |
| Python | ✅ Experiemental |
Code Generation Workflow
See demo project setup https://github.com/thepartly/reflectapi/tree/main/reflectapi-demo
- Define your API server using
reflectapitraits and builder - Generate schema as JSON from your Rust application
- Run the CLI to generate client libraries
- Use the clients in your applications with full type safety
# Generate schema (from your Rust app)
cargo run # Your app should save reflectapi-schema.json
# Generate clients
cargo run reflectapi codegen --language typescript --schema reflectapi-schema.json --output clients/typescript
cargo run reflectapi codegen --language python --schema reflectapi-schema.json --output clients/python
cargo run reflectapi codegen --language rust --schema reflectapi-schema.json --output clients/rust
Common Features
All generated clients share these characteristics:
Type Safety
- Native type definitions for each language
- Compile-time or runtime type checking
- IDE support with autocompletion
Extensibility
- Default base client implementation is provided
- Which can be replaced or extended with features, such opentelemetry instrumentation or playwrite tracing
Error Handling
- Structured error types
- Network error handling
Async Support
- Modern async/await patterns
Documentation
- Generated from your Rust documentation
- Type information in IDE tooltips
HTTP Client Libraries
| Language | HTTP Library | Features |
|---|---|---|
| TypeScript | fetch API | Native browser/Node.js support |
| Python | httpx | Async/sync, HTTP/2, connection pooling |
| Rust | reqwest | Async, HTTP/2, TLS, middleware |
Serialization
| Language | Serialization | Validation |
|---|---|---|
| TypeScript | JSON.parse/stringify | Runtime type checking |
| Python | Pydantic | Schema validation, type coercion |
| Rust | JSON or MessagePack (serde) | Compile-time, zero-cost |