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
- Inbound layer –
src/inbound/axum_router.rsdefines the router state and wire-up - API layer –
src/api/contains endpoint handlers and business logic integration - Server entry –
src/main.rsbinds the TCP listener and startsaxum::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 stateJson(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
FromRefimplementations enable clean handler signatures - Server startup follows a consistent
main.rspattern: 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →