Skip to content

Zart is in active development — breaking API changes may occur despite our best efforts to keep contracts stable.

OpenAPI & Route Configuration

Zart’s admin API (zart-api) can automatically generate OpenAPI documentation and allows you to customize where it’s mounted in your HTTP stack.

Enable the openapi feature in your Cargo.toml:

[dependencies]
zart-api = { version = "0.2", features = ["openapi"] }

Use ApiServer::with_swagger_ui() to mount the interactive Swagger UI:

use zart_api::{ApiServer, AppState};
use axum::Router;
let durable = DurableScheduler::from_backend(&pg);
let app = Router::new()
.merge(ApiServer::with_swagger_ui(
AppState::new(durable),
"http://localhost:8080".to_string(), // server URL for OpenAPI spec
));

This mounts:

PathContent
/zart/admin/v1/docsSwagger UI (interactive documentation)
/zart/admin/v1/openapi.jsonRaw OpenAPI 3.0 spec

Navigate to http://localhost:8080/zart/admin/v1/docs to see:

  • All admin API endpoints with request/response schemas
  • Try-it-now functionality for testing API calls
  • Full schema documentation for ExecutionRecord, StepRecord, etc.

If you already use utoipa for your own API, you can nest Zart’s ZartApiDoc into your schema:

use utoipa::OpenApi;
use zart_api::ZartApiDoc;
#[derive(OpenApi)]
#[openapi(
paths(
// your own handlers...
),
nest(
(path = "/zart/admin/v1", api = ZartApiDoc)
)
)]
struct MyApiDoc;
// Serve combined docs
let doc = MyApiDoc::openapi();

By default, the admin API mounts at /zart/admin/v1. Use ApiServer::route_prefix() to customize this:

let app = ApiServer::new(AppState::new(durable))
.route_prefix("/api/v2/admin".to_string()) // now mounts at /api/v2/admin
.into_router();
PrefixMounts AtUse Case
/zart/admin/v1 (default)/zart/admin/v1/...Standard Zart deployment
/api/v1/admin/api/v1/admin/...Fits into existing API versioning
/internal/zart/internal/zart/...Internal-only admin paths
/admin/admin/...Simple standalone admin
let app = ApiServer::with_swagger_ui(
AppState::new(durable),
"http://localhost:8080".to_string(),
)
.route_prefix("/internal/admin".to_string())
.into_router();

Now:

  • API: http://localhost:8080/internal/admin/...
  • Swagger UI: http://localhost:8080/internal/admin/docs
  • OpenAPI spec: http://localhost:8080/internal/admin/openapi.json

use axum::Router;
use zart_api::{ApiServer, AppState};
use zart::DurableScheduler;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Setup
let pool = sqlx::PgPool::connect(&std::env::var("DATABASE_URL")?).await?;
let durable = Arc::new(DurableScheduler::from_backend(&pg));
// Build API server with Swagger + custom prefix
let app = ApiServer::with_swagger_ui(
AppState::new(durable),
"http://localhost:8080".to_string(),
)
.route_prefix("/api/v1".to_string())
.into_router();
// Merge with your own routes
let app = app.merge(your_own_routes());
// Start server
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await?;
Ok(())
}

FeatureDefaultEffect
openapioffEnables with_swagger_ui(), ZartApiDoc, and OpenAPI annotations

Enable in Cargo.toml:

zart-api = { version = "0.2", features = ["openapi"] }