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

> Explore the design and structure of the macro-inc/macro API. Learn how Axum-based microservices use typed router state and declarative routes for modularity.

- Repository: [Macro/macro](https://github.com/macro-inc/macro)
- Tags: deep-dive
- Published: 2026-08-15

---

**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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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.

```rust
// 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.

```rust
// 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`](https://github.com/macro-inc/macro/blob/main/scheduled_action/src/api/create_action.rs).

```rust
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`](https://github.com/macro-inc/macro/blob/main/context.rs) to enable fine-grained state access. The `FromRef` trait lets handlers request only the state subset they need.

```rust
// 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`](https://github.com/macro-inc/macro/blob/main/src/main.rs) finalizes the **macro-inc/macro API design** by layering middleware and binding to the network.

```rust
// 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`](https://github.com/macro-inc/macro/blob/main/scheduled_action/src/inbound/axum_router.rs) | CRUD for timed automation tasks |
| **Notification** | [`notification_service/src/api/user_notification.rs`](https://github.com/macro-inc/macro/blob/main/notification_service/src/api/user_notification.rs) | User notification lifecycle |
| **Unfurl** | [`unfurl_service/src/api/unfurl/mod.rs`](https://github.com/macro-inc/macro/blob/main/unfurl_service/src/api/unfurl/mod.rs) | URL preview generation |
| **Static File** | [`static_file_service/src/api/mod.rs`](https://github.com/macro-inc/macro/blob/main/static_file_service/src/api/mod.rs) | Presigned URLs and file metadata |
| **Search Processing** | [`search_processing/src/api/internal/mod.rs`](https://github.com/macro-inc/macro/blob/main/search_processing/src/api/internal/mod.rs) | Background indexing jobs |
| **Worker Trigger** | [`worker_trigger/src/service/mod.rs`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`](https://github.com/macro-inc/macro/blob/main/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`.