Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CLI Guide

The CLI binary is named caelix.

Install it from crates.io:

cargo install caelix-cli

After installation, run commands with the caelix binary.

Run An Application

caelix run

This resets the terminal, clears its viewport and scrollback buffer, then delegates to cargo run in the current project. Use --watch to restart the application when files under src/ or Cargo.toml change:

caelix run --watch

Arguments after -- are passed directly to the application:

caelix run --watch -- --port 4000 --verbose

Validate An Application

caelix doctor

This runs the normal application binary as cargo run -- --doctor. Caelix completes application construction, module validation, provider construction, lifecycle startup hooks, and route/runtime setup, then runs shutdown hooks and exits without binding an HTTP port.

It returns a non-zero status when compilation, configuration, startup, or shutdown fails. Doctor mode intentionally permits normal startup side effects because it verifies the same initialization path as caelix run; the only skipped step is network binding and request serving.

Create An Application

caelix new demo-api

Pass --backend axum to generate a project that opts into Caelix’s Axum adapter and includes tower-http for native Tower layers:

caelix new demo-api --backend axum

The command creates:

  • Cargo.toml
  • AGENTS.md
  • src/main.rs
  • src/lib.rs
  • src/app.rs

The default generated Cargo.toml uses caelix = "0.0.32" from crates.io and the Actix backend. The Axum option disables default features and enables axum instead.

The generated AGENTS.md gives AI coding agents the app-level Caelix conventions: explicit module registration, provider/controller registration, injectable field shape, service-level cache behavior, and the usual cargo test check.

Generated src/main.rs starts the selected adapter:

use caelix::Application;
use demo_api::AppModule;

#[caelix::main]
async fn main() -> std::io::Result<()> {
    Application::new::<AppModule>()
        .await
        .map_err(|err| std::io::Error::other(err.message))?
        .listen("127.0.0.1:8080")
        .await
}

Generated src/app.rs defines an empty root module:

#![allow(unused)]
fn main() {
use caelix::{Module, ModuleMetadata};

pub struct AppModule;

impl Module for AppModule {
    fn register() -> ModuleMetadata {
        ModuleMetadata::new()
    }
}
}

Update Caelix

caelix update

This command reads the current caelix dependency from Cargo.toml, fetches the latest published caelix version from crates.io, and updates only that dependency when a newer version exists. It preserves existing Cargo.toml comments, formatting, dependency order, and feature settings.

After editing Cargo.toml, the CLI runs:

cargo update -p caelix

caelix update does not regenerate or re-sync scaffolded source files.

Generate A Feature Module

caelix g module users

This is equivalent to:

caelix generate module users

It creates:

  • src/users/mod.rs
  • src/users/service.rs
  • src/users/controller.rs

The generated module registers the service as a provider and the controller as a controller. The CLI prints manual registration steps for adding the module to the app.

Add the generated module to src/lib.rs:

#![allow(unused)]
fn main() {
pub mod users;
}

Then import it in the root module:

#![allow(unused)]
fn main() {
use crate::users::UsersModule;

impl Module for AppModule {
    fn register() -> ModuleMetadata {
        ModuleMetadata::new().import::<UsersModule>()
    }
}
}

Generate A Service

caelix g service users

This creates src/users/service.rs. If src/users/mod.rs does not exist, it creates a feature mod.rs that exports the service.

Generate A Controller

caelix g controller users

This creates src/users/controller.rs. If src/users/service.rs exists, the controller injects Arc<UsersService> and calls it. If the service does not exist, the controller is generated without that dependency and the CLI prints a note.

Generated controller with a service:

#![allow(unused)]
fn main() {
use std::sync::Arc;

use caelix::{Result, controller, injectable};

use super::UsersService;

#[injectable]
pub struct UsersController {
    service: Arc<UsersService>,
}

#[controller("/users")]
impl UsersController {
    #[get("")]
    pub async fn hello(&self) -> Result<String> {
        Ok(self.service.hello())
    }
}
}

Name Normalization

InputDirectoryRouteTypes
userssrc/users/usersUsersModule, UsersService, UsersController
auth-sessionsrc/auth_session/auth-sessionAuthSessionModule, AuthSessionService, AuthSessionController
Admin Userssrc/admin_users/admin-usersAdminUsersModule, AdminUsersService, AdminUsersController

Overwrite Behavior

The CLI refuses to overwrite existing generated files. If any target file already exists, the command returns an error like:

src/users/service.rs already exists; refusing to overwrite

For caelix g module users, all three target files must be missing: mod.rs, service.rs, and controller.rs.