Introduction

5-Minute Quickstart

Get a microservice running in 5 minutes with automatic health checks and observability.

Prerequisites

  • Rust 1.70+ installed
  • Basic familiarity with async Rust
  • 5 minutes of your time

Step 1: Create a New Project (30 seconds)

cargo new my-api
cd my-api

Step 2: Add Dependencies (30 seconds)

Add acton-service to your Cargo.toml:

[dependencies]

Step 3: Write Your First Service (2 minutes)

Replace the contents of src/main.rs:

use acton_service::prelude::*;

// Your first handler
async fn hello() -> &'static str {
    "Hello from acton-service!"
}

#[tokio::main]
async fn main() -> Result<()> {
    // Build versioned routes - versioning is enforced!
    let routes = VersionedApiBuilder::new()
        .with_base_path("/api")
        .add_version(ApiVersion::V1, |router| {
            router.route("/hello", get(hello))
        })
        .build_routes();

    // Build and serve - config and tracing are automatic!
    ServiceBuilder::new()
        .with_routes(routes)
        .build()
        .serve()
        .await
}

Step 4: Run It! (30 seconds)

cargo run

You should see:

   Compiling my-api v0.1.0
    Finished dev [unoptimized + debuginfo] target(s)
     Running `target/debug/my-api`

Step 5: Test It! (1 minute)

Open another terminal and test your endpoints:

# Your versioned API
curl http://localhost:8080/api/v1/hello
# Output: Hello from acton-service!

# Automatic health check (for Kubernetes)
curl http://localhost:8080/health
# Output: {"status":"healthy"}

# Automatic readiness check
curl http://localhost:8080/ready
# Output: {"status":"ready"}

What Just Happened?

You created a microservice with these features enabled by default:

Type-enforced API versioning - Compiler prevents unversioned routes

Automatic health checks - /health and /ready endpoints for Kubernetes

Automatic logging - Structured JSON logs

Automatic tracing - OpenTelemetry tracing configured

Graceful shutdown - Signal handling (SIGTERM, SIGINT)

Default configuration - Works with sensible defaults

What's Next?

Add More Endpoints

async fn get_user(Path(id): Path<u64>) -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "id": id,
        "name": "Alice"
    }))
}

// Add to your router:
.add_version(ApiVersion::V1, |router| {
    router
        .route("/hello", get(hello))
        .route("/users/{id}", get(get_user))  // New!
})

Add a Second Version

// V2 with improved response
async fn hello_v2() -> Json<serde_json::Value> {
    Json(serde_json::json!({
        "message": "Hello from V2!",
        "version": "2.0"
    }))
}

// Add V2 to your routes:
.add_version(ApiVersion::V1, |router| {
    router.route("/hello", get(hello))
})
.add_version(ApiVersion::V2, |router| {
    router.route("/hello", get(hello_v2))  // New version!
})

Try the Examples

The repository includes comprehensive examples:

# Clone the repo
git clone https://github.com/Govcraft/acton-service
cd acton-service

# Run the simple API example
cargo run --manifest-path=acton-service/Cargo.toml --example simple-api

# Run the users API example (shows deprecation)
cargo run --manifest-path=acton-service/Cargo.toml --example users-api

# Run the dual HTTP+gRPC example
cargo run --manifest-path=acton-service/Cargo.toml --example ping-pong --features grpc

Common First Questions

Q: Why can't I just use Router::new()? A: acton-service enforces versioning at compile time. All routes must be versioned to prevent breaking changes from slipping into production.

Q: How do I add middleware? A: See the Tutorial for middleware examples.

Q: How do I connect to a database? A: Add the database feature and see the Tutorial.

Q: Can I disable versioning? A: No. If you need unversioned routes, use Axum directly. acton-service is opinionated about API evolution.

Next Steps

Need Help?


You're now ready to explore acton-service features.

Previous
What is acton-service?