CLI Reference

Ferro provides a powerful CLI tool for project scaffolding, code generation, database management, and development workflow automation.

Installation

cargo install ferro-cli

Or build from source:

git clone https://github.com/ferroframework/ferro
cd ferro/ferro-cli
cargo install --path .

Project Commands

ferro new

Create a new Ferro project with the complete directory structure.

# Interactive mode (prompts for project name)
ferro new

# Direct creation
ferro new my-app

# Skip git initialization
ferro new my-app --no-git

# Non-interactive mode (uses defaults)
ferro new my-app --no-interaction

Options:

OptionDescription
--no-interactionSkip prompts, use defaults
--no-gitDon't initialize git repository

Generated Structure:

my-app/
├── src/
│   ├── main.rs
│   ├── bootstrap.rs
│   ├── routes.rs
│   ├── controllers/
│   │   └── mod.rs
│   ├── middleware/
│   │   ├── mod.rs
│   │   └── cors.rs
│   ├── models/
│   │   └── mod.rs
│   ├── migrations/
│   │   └── mod.rs
│   ├── events/
│   │   └── mod.rs
│   ├── listeners/
│   │   └── mod.rs
│   ├── jobs/
│   │   └── mod.rs
│   ├── notifications/
│   │   └── mod.rs
│   ├── tasks/
│   │   └── mod.rs
│   ├── seeders/
│   │   └── mod.rs
│   └── factories/
│       └── mod.rs
├── frontend/
│   ├── src/
│   │   ├── pages/
│   │   │   └── Home.tsx
│   │   ├── layouts/
│   │   │   └── Layout.tsx
│   │   ├── types/
│   │   │   └── inertia.d.ts
│   │   ├── app.tsx
│   │   └── main.tsx
│   ├── package.json
│   ├── tsconfig.json
│   ├── vite.config.ts
│   └── tailwind.config.js
├── Cargo.toml
├── .env
├── .env.example
└── .gitignore

Development Commands

ferro serve

Start the development server with hot reloading for both backend and frontend.

# Start both backend and frontend
ferro serve

# Custom ports
ferro serve --port 8080 --frontend-port 5173

# Backend only (no frontend dev server)
ferro serve --backend-only

# Frontend only (no Rust compilation)
ferro serve --frontend-only

# Skip TypeScript type generation
ferro serve --skip-types

Options:

OptionDefaultDescription
--port3000Backend server port
--frontend-port5173Frontend dev server port
--backend-onlyfalseRun only the backend
--frontend-onlyfalseRun only the frontend
--skip-typesfalseDon't regenerate TypeScript types

What it does:

  1. Starts the Rust backend with cargo watch for hot reloading
  2. Starts the Vite frontend dev server
  3. Watches Rust files to regenerate TypeScript types automatically
  4. Proxies frontend requests to the backend

ferro generate-types

Generate TypeScript type definitions from Rust InertiaProps structs.

ferro generate-types

This scans your Rust code for structs deriving InertiaProps and generates corresponding TypeScript interfaces in frontend/src/types/.

Code Generators

All generators follow the pattern ferro make:<type> <name> [options].

ferro make:controller

Generate a controller with handler methods.

# Basic controller
ferro make:controller UserController

# Resource controller with CRUD methods
ferro make:controller PostController --resource

# API controller (JSON responses)
ferro make:controller Api/ProductController --api

Options:

OptionDescription
--resourceGenerate index, show, create, store, edit, update, destroy methods
--apiGenerate API-style controller (JSON responses)

Generated file: src/controllers/user_controller.rs

#![allow(unused)]
fn main() {
use ferro::{handler, Request, Response, json_response};

#[handler]
pub async fn index(req: Request) -> Response {
    // TODO: Implement
    json_response!({ "message": "index" })
}

#[handler]
pub async fn show(req: Request) -> Response {
    // TODO: Implement
    json_response!({ "message": "show" })
}

// ... additional methods for --resource
}

ferro make:middleware

Generate middleware for request/response processing.

ferro make:middleware Auth
ferro make:middleware RateLimit

Generated file: src/middleware/auth.rs

#![allow(unused)]
fn main() {
use ferro::{Middleware, Request, Response, Next};
use async_trait::async_trait;

pub struct Auth;

#[async_trait]
impl Middleware for Auth {
    async fn handle(&self, request: Request, next: Next) -> Response {
        // TODO: Implement middleware logic
        next.run(request).await
    }
}
}

ferro make:action

Generate a single-action class for complex business logic.

ferro make:action CreateOrder
ferro make:action ProcessPayment

Generated file: src/actions/create_order.rs

#![allow(unused)]
fn main() {
use ferro::FrameworkError;

pub struct CreateOrder;

impl CreateOrder {
    pub async fn execute(&self) -> Result<(), FrameworkError> {
        // TODO: Implement action
        Ok(())
    }
}
}

ferro make:event

Generate an event struct for the event dispatcher.

ferro make:event UserRegistered
ferro make:event OrderPlaced

Generated file: src/events/user_registered.rs

#![allow(unused)]
fn main() {
use ferro_events::Event;

#[derive(Debug, Clone, Event)]
pub struct UserRegistered {
    pub user_id: i64,
}
}

ferro make:listener

Generate a listener that responds to events.

ferro make:listener SendWelcomeEmail
ferro make:listener NotifyAdmins

Generated file: src/listeners/send_welcome_email.rs

#![allow(unused)]
fn main() {
use ferro_events::{Listener, Event};
use async_trait::async_trait;

pub struct SendWelcomeEmail;

#[async_trait]
impl<E: Event + Send + Sync> Listener<E> for SendWelcomeEmail {
    async fn handle(&self, event: &E) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // TODO: Implement listener
        Ok(())
    }
}
}

ferro make:job

Generate a background job for queue processing.

ferro make:job ProcessImage
ferro make:job SendEmail

Generated file: src/jobs/process_image.rs

#![allow(unused)]
fn main() {
use ferro_queue::{Job, JobContext};
use serde::{Deserialize, Serialize};
use async_trait::async_trait;

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProcessImage {
    pub image_id: i64,
}

#[async_trait]
impl Job for ProcessImage {
    async fn handle(&self, ctx: &JobContext) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // TODO: Implement job
        Ok(())
    }
}
}

ferro make:notification

Generate a multi-channel notification.

ferro make:notification OrderShipped
ferro make:notification InvoiceGenerated

Generated file: src/notifications/order_shipped.rs

#![allow(unused)]
fn main() {
use ferro_notifications::{Notification, Notifiable, Channel};

pub struct OrderShipped {
    pub order_id: i64,
}

impl Notification for OrderShipped {
    fn via(&self) -> Vec<Channel> {
        vec![Channel::Mail, Channel::Database]
    }
}
}

ferro make:migration

Generate a database migration file.

ferro make:migration create_posts_table
ferro make:migration add_status_to_orders

Generated file: src/migrations/m20240115_143052_create_posts_table.rs

#![allow(unused)]
fn main() {
use sea_orm_migration::prelude::*;

#[derive(DeriveMigrationName)]
pub struct Migration;

#[async_trait::async_trait]
impl MigrationTrait for Migration {
    async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        // TODO: Implement migration
        Ok(())
    }

    async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
        // TODO: Implement rollback
        Ok(())
    }
}
}

ferro make:inertia

Generate an Inertia.js page component with TypeScript types.

ferro make:inertia Dashboard
ferro make:inertia Users/Profile

Generated files:

  • frontend/src/pages/Dashboard.tsx
  • src/controllers/ (props struct)

ferro make:task

Generate a scheduled task.

ferro make:task CleanupExpiredSessions
ferro make:task SendDailyReport

Generated file: src/tasks/cleanup_expired_sessions.rs

#![allow(unused)]
fn main() {
use ferro::scheduling::{Task, Schedule};
use async_trait::async_trait;

pub struct CleanupExpiredSessions;

#[async_trait]
impl Task for CleanupExpiredSessions {
    fn schedule(&self) -> Schedule {
        Schedule::daily()
    }

    async fn handle(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // TODO: Implement task
        Ok(())
    }
}
}

ferro make:seeder

Generate a database seeder.

ferro make:seeder UserSeeder
ferro make:seeder ProductSeeder

Generated file: src/seeders/user_seeder.rs

#![allow(unused)]
fn main() {
use ferro::database::Seeder;
use async_trait::async_trait;

pub struct UserSeeder;

#[async_trait]
impl Seeder for UserSeeder {
    async fn run(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // TODO: Seed data
        Ok(())
    }
}
}

ferro make:factory

Generate a model factory for testing.

ferro make:factory UserFactory
ferro make:factory PostFactory

Generated file: src/factories/user_factory.rs

#![allow(unused)]
fn main() {
use ferro::testing::Factory;
use fake::{Fake, Faker};

pub struct UserFactory;

impl Factory for UserFactory {
    type Model = user::Model;

    fn definition(&self) -> Self::Model {
        // TODO: Define factory
        todo!()
    }
}
}

ferro make:error

Generate a custom error type.

ferro make:error PaymentFailed
ferro make:error ValidationError

ferro make:scaffold

Generate a complete CRUD scaffold: model, migration, controller, and Inertia pages.

# Basic scaffold
ferro make:scaffold Post

# With field definitions
ferro make:scaffold Post title:string content:text published:bool

# Complex example
ferro make:scaffold Product name:string description:text price:float stock:integer

Field Types:

TypeRust TypeDatabase Type
stringStringVARCHAR(255)
textStringTEXT
integeri32INTEGER
biginti64BIGINT
floatf64DOUBLE
boolboolBOOLEAN
datetimeDateTimeTIMESTAMP
dateDateDATE
uuidUuidUUID

Generated Files:

src/
├── models/post.rs           # SeaORM entity
├── migrations/m*_create_posts_table.rs
├── controllers/post_controller.rs
frontend/src/pages/
├── posts/
│   ├── Index.tsx
│   ├── Show.tsx
│   ├── Create.tsx
│   └── Edit.tsx

Database Commands

ferro migrate

Run all pending migrations.

ferro migrate

ferro migrate:rollback

Rollback the last batch of migrations.

ferro migrate:rollback

ferro migrate:status

Show the status of all migrations.

ferro migrate:status

Output:

+------+------------------------------------------------+-------+
| Ran? | Migration                                       | Batch |
+------+------------------------------------------------+-------+
| Yes  | m20240101_000001_create_users_table            | 1     |
| Yes  | m20240101_000002_create_posts_table            | 1     |
| No   | m20240115_143052_add_status_to_posts           |       |
+------+------------------------------------------------+-------+

ferro migrate:fresh

Drop all tables and re-run all migrations.

ferro migrate:fresh

Warning: This is destructive and will delete all data.

ferro db:sync

Synchronize the database schema and generate entity files.

# Sync entities from existing database
ferro db:sync

# Run migrations first, then sync
ferro db:sync --migrate

Options:

OptionDescription
--migrateRun pending migrations before syncing

This command:

  1. Discovers the database schema (tables, columns, types)
  2. Generates SeaORM entity files in src/models/entities/
  3. Creates user-friendly model wrappers with the Ferro Model API

ferro db:query

Execute a raw SQL query against the database.

# Simple SELECT query
ferro db:query "SELECT * FROM users LIMIT 5"

# Query with conditions
ferro db:query "SELECT id, name, email FROM users WHERE active = true"

# Count query
ferro db:query "SELECT COUNT(*) FROM posts"

Features:

  • Reads DATABASE_URL from .env file
  • Supports SQLite and PostgreSQL databases
  • Displays results in a formatted table
  • Handles NULL values gracefully
  • Shows row count after results

Example Output:

+-----+-------+-------------------+
| 1   | Alice | alice@example.com |
| 2   | Bob   | bob@example.com   |
+-----+-------+-------------------+

→ 2 row(s)

Use Cases:

  • Quick data inspection during development
  • Debugging database state
  • Verifying migration results
  • Ad-hoc queries without external tools

Docker Commands

ferro docker:init

Initialize Docker configuration files.

ferro docker:init

Generated files:

  • Dockerfile
  • docker-compose.yml
  • .dockerignore

ferro docker:compose

Manage Docker Compose services.

# Start services
ferro docker:compose up

# Stop services
ferro docker:compose down

# Rebuild and start
ferro docker:compose up --build

Scheduling Commands

ferro schedule:run

Run scheduled tasks that are due.

ferro schedule:run

This executes all tasks whose schedule indicates they should run now. Typically called by a system cron job every minute:

* * * * * cd /path/to/project && ferro schedule:run >> /dev/null 2>&1

ferro schedule:work

Start the scheduler worker for continuous task execution.

ferro schedule:work

This runs in the foreground and checks for due tasks every minute. Useful for development or container deployments.

ferro schedule:list

Display all registered scheduled tasks.

ferro schedule:list

Output:

+---------------------------+-------------+-------------------+
| Task                      | Schedule    | Next Run          |
+---------------------------+-------------+-------------------+
| CleanupExpiredSessions    | Daily 00:00 | 2024-01-16 00:00  |
| SendDailyReport           | Daily 09:00 | 2024-01-15 09:00  |
| PruneOldNotifications     | Weekly Mon  | 2024-01-22 00:00  |
+---------------------------+-------------+-------------------+

Storage Commands

Create a symbolic link from public/storage to storage/app/public.

ferro storage:link

This allows publicly accessible files stored in storage/app/public to be served via the web server.

AI-Assisted Development

ferro mcp

Start the Model Context Protocol (MCP) server for AI-assisted development.

ferro mcp

The MCP server provides introspection tools that help AI assistants understand your Ferro application structure, including routes, models, controllers, and configuration.

ferro boost:install

Install AI development boost features.

ferro boost:install

This sets up configuration for enhanced AI-assisted development workflows.

Command Summary

CommandDescription
newCreate a new Ferro project
serveStart development server
generate-typesGenerate TypeScript types
make:controllerCreate a controller
make:middlewareCreate middleware
make:actionCreate an action class
make:eventCreate an event
make:listenerCreate a listener
make:jobCreate a background job
make:notificationCreate a notification
make:migrationCreate a migration
make:inertiaCreate an Inertia page
make:taskCreate a scheduled task
make:seederCreate a database seeder
make:factoryCreate a model factory
make:errorCreate a custom error
make:policyCreate an authorization policy
make:scaffoldCreate complete CRUD scaffold
migrateRun migrations
migrate:rollbackRollback migrations
migrate:statusShow migration status
migrate:freshFresh migrate (drop all)
db:syncSync database schema
db:queryExecute raw SQL query
docker:initInitialize Docker files
docker:composeManage Docker Compose
schedule:runRun due scheduled tasks
schedule:workStart scheduler worker
schedule:listList scheduled tasks
storage:linkCreate storage symlink
mcpStart MCP server
boost:installInstall AI boost features

Environment Variables

The CLI respects these environment variables:

VariableDescription
DATABASE_URLDatabase connection string
APP_ENVApplication environment (development, production)
RUST_LOGLogging level