use axum::{extract::State, routing::get, Json, Router};
use serde_json::{json, Value};
use std::sync::Arc;
use axum_apcore::{ap_handler, ApContext, AxumApcore};
async fn get_user(
State(_apcore): State<Arc<AxumApcore>>,
ApContext(ctx): ApContext,
axum::extract::Path(id): axum::extract::Path<String>,
) -> Json<Value> {
tracing::info!(trace_id = %ctx.trace_id, user_id = %id, "Handling get_user");
Json(json!({
"id": id,
"name": "Alice",
"trace_id": ctx.trace_id,
}))
}
async fn list_users() -> Json<Value> {
Json(json!({
"users": [
{"id": "1", "name": "Alice"},
{"id": "2", "name": "Bob"},
]
}))
}
async fn create_user(Json(body): Json<Value>) -> Json<Value> {
Json(json!({
"id": "3",
"name": body.get("name").and_then(|v| v.as_str()).unwrap_or("unknown"),
"created": true,
}))
}
async fn health() -> Json<Value> {
Json(json!({"status": "ok"}))
}
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
ap_handler! {
method: "GET",
path: "/api/users/:id",
handler: get_user,
description: "Get a user by ID",
tags: ["users"],
input_schema: json!({"type": "object", "properties": {"id": {"type": "string"}}, "required": ["id"]}),
output_schema: json!({"type": "object", "properties": {"id": {"type": "string"}, "name": {"type": "string"}}}),
}
ap_handler! {
method: "GET",
path: "/api/users",
handler: list_users,
description: "List all users",
tags: ["users"],
input_schema: json!({"type": "object"}),
output_schema: json!({"type": "object", "properties": {"users": {"type": "array"}}}),
}
ap_handler! {
method: "POST",
path: "/api/users",
handler: create_user,
description: "Create a new user",
tags: ["users"],
input_schema: json!({"type": "object", "properties": {"name": {"type": "string"}}, "required": ["name"]}),
output_schema: json!({"type": "object", "properties": {"id": {"type": "string"}, "created": {"type": "boolean"}}}),
}
let apcore = Arc::new(AxumApcore::new());
let router = Router::new()
.route("/api/users/{id}", get(get_user))
.route("/api/users", get(list_users).post(create_user))
.route("/health", get(health))
.with_state(apcore.clone());
apcore
.init_app(&router)
.await
.expect("Failed to init apcore");
let modules = apcore.list_modules();
tracing::info!(?modules, "Registered modules");
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::info!("Listening on http://0.0.0.0:3000");
axum::serve(listener, router).await.unwrap();
}