Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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!