How the macro-inc/macro API is Designed and Structured: A Deep Dive into Axum-Based Microservices

The macro-inc/macro repository implements a modular HTTP API using the Axum web framework, where each microservice owns its own typed router state and declarative route definitions under services/<service_name>/.

This architecture powers a distributed platform where services communicate through well-defined HTTP boundaries. Understanding this design reveals how the team achieves separation of concerns, testability, and scalability across a large Rust workspace.


Core Architectural Pattern: Per-Service Axum Routers

The macro-inc/macro API structure centers on a repeatable pattern. Every service lives in its own crate under services/<service_name>/ and follows the same organizational conventions.

The Three-Layer Stack

  1. Inbound layer – src/inbound/axum_router.rs defines the router state and wire-up
  2. API layer – src/api/ contains endpoint handlers and business logic integration
  3. Server entry – src/main.rs binds the TCP listener and starts axum::serve

This consistency makes the macro-inc/macro API design predictable across dozens of services.


Router State: Typed Dependencies via Axum's State Extractor

Each service declares a router state struct that holds shared resources. The state implements Clone + Send + Sync and uses axum::extract::State for zero-cost dependency injection.

// From scheduled_action/src/inbound/axum_router.rs
pub struct ScheduledActionRouterState<S> {
    pub db: Arc<dyn ScheduledActionDb>,
    pub auth: Arc<dyn AuthService>,
    // ...
}

pub fn router<S>() -> Router<ScheduledActionRouterState<S>>
where
    S: Clone + Send + Sync + 'static,
{
    Router::new()
        .route("/health", get(health))
        .route("/actions", post(create_action))
        .route("/actions/:id", get(get_action).put(update_action).delete(delete_action))
}

The generic parameter S allows swapping implementations for testing without changing route definitions.


Route Registration: Declarative HTTP Verb Mapping

Routes map directly to handler functions using axum::routing macros. The macro-inc/macro API structure favors explicit, chainable registration over macro-heavy DSLs.

// From notification_service/src/api/user_notification.rs
pub fn router<S>() -> axum::Router<NotificationRouterState<S>>
where
    S: Clone + Send + Sync + 'static,
{
    Router::new()
        .route("/", get(list_typed_notifications::<S>))
        .route("/", post(bulk_get_typed_notifications_by_event_item_ids::<S>))
}

Chaining multiple verbs on the same path—.route("/actions/:id", get(...).put(...).delete(...))—keeps related operations visually grouped.


Handler Patterns: Extractors and State Access

Handlers use Axum's extractor system to access request parts and shared state. The pattern appears consistently across services like scheduled_action/src/api/create_action.rs.

use axum::{extract::{Json, State}, response::IntoResponse, http::StatusCode};

#[derive(Deserialize)]
struct CreateAction { /* fields */ }

async fn create_action(
    State(state): State<ScheduledActionRouterState>,
    Json(payload): Json<CreateAction>,
) -> impl IntoResponse {
    state.db.insert_action(payload).await?;
    (StatusCode::CREATED, Json("created"))
}

Key extraction points:

  • State(state): State<ScheduledActionRouterState> – grabs the typed router state
  • Json(payload): Json<CreateAction> – parses and validates the request body
  • Return type impl IntoResponse – flexible response composition

Shared Utilities: FromRef for State Subsetting

Services like static_file_service use context.rs to enable fine-grained state access. The FromRef trait lets handlers request only the state subset they need.

// Pattern from static_file_service/src/api/context.rs
impl FromRef<AppState> for DatabasePool {
    fn from_ref(state: &AppState) -> Self {
        state.db.clone()
    }
}

This decouples handler signatures from the full router state, improving compile-time boundaries and test isolation.


Server Startup: Composing Routers with Middleware

Each service's src/main.rs finalizes the macro-inc/macro API design by layering middleware and binding to the network.

// From static_file_service/src/api/mod.rs
let app = router.into_make_service();
axum::serve(listener, app).await.unwrap();

Production services typically add:

  • Logging/tracing middleware
  • Authentication extractors
  • Rate limiting
  • CORS configuration

The into_make_service() conversion bridges Axum's router to hyper's service interface.


Key Services and Their API Entry Points

Service Core API File Responsibility
Scheduled Action scheduled_action/src/inbound/axum_router.rs CRUD for timed automation tasks
Notification notification_service/src/api/user_notification.rs User notification lifecycle
Unfurl unfurl_service/src/api/unfurl/mod.rs URL preview generation
Static File static_file_service/src/api/mod.rs Presigned URLs and file metadata
Search Processing search_processing/src/api/internal/mod.rs Background indexing jobs
Worker Trigger worker_trigger/src/service/mod.rs ECS task orchestration

Summary

  • macro-inc/macro builds APIs with Axum's typed router pattern, not generic framework abstractions
  • Each service owns its router state struct with Arc<dyn Trait> fields for swappable dependencies
  • Routes register via explicit HTTP verb chains in Router::new().route(...) calls
  • State extractors and FromRef implementations enable clean handler signatures
  • Server startup follows a consistent main.rs pattern: build router, add middleware, axum::serve

This design scales across service boundaries while keeping individual crates maintainable and testable in isolation.


Frequently Asked Questions

What web framework does macro-inc/macro use for its API?

The repository uses Axum as its primary web framework. Every service builds its HTTP layer through Axum's Router type, state extractors, and middleware system. This choice appears consistently from scheduled_action to static_file_service.

How does macro-inc/macro handle shared state between handlers?

Services define a router state struct (e.g., ScheduledActionRouterState) containing Arc-wrapped trait objects. Handlers receive this via State(state): State<YourStateType> parameters. The context.rs pattern in services like static_file_service enables subset extraction via FromRef implementations.

Where are API routes defined in the macro-inc/macro codebase?

Route definitions live in service-specific inbound modules, typically src/inbound/axum_router.rs or src/api/ subdirectories. The router() function in these files assembles all endpoints for that service using axum::routing methods like get, post, put, and delete.

Can handlers in macro-inc/macro access only part of the router state?

Yes. Through Axum's FromRef mechanism, handlers can declare narrower state types in their signatures. The context.rs file in static_file_service/src/api/ demonstrates this pattern, allowing individual handlers to request just DatabasePool or Config rather than the full AppState.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →