CLI Reference
Complete guide to the Aim CLI tool.
Installation
dart install aim_cliCommands
aim create
Create a new Aim framework project.
Usage:
aim create <project_name>Options:
| Option | Description | Default |
|---|---|---|
--target | Project target: server, workers, supabase or functions | server |
--firebase-project | Firebase project id written to .firebaserc (target: functions only — silently ignored for every other target). Pass an empty value to skip .firebaserc and bind a project later with firebase use --add | prompts when a terminal is attached |
Example:
aim create my_app
cd my_appThis command:
- Creates a new directory with the project name
- Generates project structure (bin/, lib/, test/)
- Creates
pubspec.yamlwith Aim dependencies - Generates a basic server in
bin/server.dart - Prints the next steps (
dart pub get,aim dev)
With --target workers:
aim create my_worker --target workers
cd my_workerThis scaffolds a Cloudflare workerd project instead:
lib/main.dart- Dart entry point, ends withapp.serveWorkers()src/index.mjs- JavaScript Worker entry pointwrangler.jsonc- Wrangler configuration
The Worker name in wrangler.jsonc is the project name with underscores (_) replaced by hyphens (-), e.g. my_worker becomes my-worker. See Cloudflare Workers for the full setup.
With --target supabase:
aim create my_api --target supabase
cd my_apiThis scaffolds a Supabase Edge Functions project:
lib/main.dart- Dart entry point, ends withapp.serveDeno(basePath: 'my_api')supabase/functions/my_api/index.ts- the function's Deno entry pointsupabase/config.toml-project_idplus astatic_filesdeclaration for the compiled wasm
See Supabase Edge Functions for the full setup, including why the app needs basePath.
--target edge is no longer accepted; use --target workers. A project whose pubspec.yaml still has aim.target: edge from before this split fails aim dev/aim build with an error naming the new target and dependency (workers/aim_workers).
With --target functions:
aim create my_api --target functions
cd my_apiThis scaffolds a Cloud Functions for Firebase project, flat in one directory:
firebase.json- Firebase config, with"source": "."next topubspec.yamlbin/server.dart- hands the app tofirebase_functions'onRequestlib/src/server.dart-createApp(), your routestest/<project_name>_test.dart- a starter testREADME.md- prerequisites, dev loop, and deploy steps for this project.gitignore.firebaserc- written only when a Firebase project id was given
It prompts for a Firebase project id (Firebase project id (leave empty to set it up later): ) unless --firebase-project is passed. An empty answer skips .firebaserc; with no terminal attached and no --firebase-project, the prompt is skipped the same way. See Cloud Functions for Firebase for the full setup.
aim dev
Start the development server with hot reload support.
Usage:
aim dev [options]Options:
| Option | Short | Description | Default |
|---|---|---|---|
--entry | -e | Server entry point | bin/server.dart |
--host | Ignored (reserved) | — | |
--port | -p | Port passed to wrangler dev (target: workers only; rejected for target: supabase and target: functions) | 8787 (wrangler default) |
--hot-reload | Enable hot reload | true | |
--no-hot-reload | Disable hot reload | ||
--watch | Directories to watch (comma-separated) | lib,bin; lib for target: workers and target: supabase |
Examples:
# Start with default settings
aim dev
# Custom entry point
aim dev --entry bin/api.dart
# Disable hot reload
aim dev --no-hot-reload
# Watch additional directories
aim dev --watch lib,bin,routes
# Custom port
aim dev --port 3000How it works:
- Watches specified directories for file changes
- Automatically restarts the server when files are modified
- Preserves terminal output history
- Loads environment variables from
pubspec.yaml
With target: workers:
- Compiles the entry point to WebAssembly (
dart compile wasm) - Starts
npx wrangler@4 devto run the compiled Worker locally --portis passed through to wrangler- Changes under
lib/(or the watched directories) trigger a recompile; wrangler reloads the updated wasm automatically --no-hot-reloaddisables file watching entirelyaim.envis ignored (with a warning) — configure vars and bindings inwrangler.jsoncinstead- Requires Node.js to be installed (for
npx)
With target: supabase:
- Compiles the entry point to WebAssembly (
dart compile wasm) intosupabase/functions/<name>/, where<name>is the pubspec'sname - Checks whether the local Supabase stack is running (
supabase status) and runssupabase startitself when it is not, before startingsupabase functions serve <name> --no-verify-jwt - The first run on a fresh checkout takes a few minutes:
supabase startbrings up Postgres, auth and storage, and applies this project's migrations andseed.sqlto the local database - The stack is left running when
aim devexits, including on Ctrl-C; runsupabase stopto stop it --portis rejected; the function's port comes fromsupabase/config.toml- Changes under
lib/trigger a recompile;supabase functions servepicks up the rebuilt wasm without being restarted aim.envis ignored (with a warning) — set variables withsupabase secrets setorsupabase/functions/.envinstead- Requires the Supabase CLI (2.7.0+) and a running Docker daemon
With target: functions:
- Starts
firebase emulators:start --only functions - The emulator runs
build_runner watchitself, so edits are picked up whileaim devkeeps running, without the CLI adding a second rebuild loop —--hot-reloadand--watchhave no effect --entry/aim.entryhave no effect either: Firebase resolves the entry point itself, fromfirebase.jsonand its own convention--hostis silently ignored--portis rejected; set the port infirebase.jsonunderemulators.functions.portinsteadaim.envis passed to the emulator process, which the function process it spawns inherits- Falls back to a
demo-project id (derived from the package name) when there's no.firebaserc - Requires the Firebase CLI, logged in, with
firebase experiments:enable dartfunctionsrun once
aim build
Compile the server for production deployment.
Usage:
aim build [options]Options:
| Option | Short | Description | Default |
|---|---|---|---|
--entry | -e | Server entry point | bin/server.dart or pubspec.yaml |
--output | -o | Output file path | build/server |
Examples:
# Build with default settings
aim build
# Custom entry point
aim build --entry bin/api.dart
# Custom output path
aim build --output dist/my-server
# Both custom
aim build --entry bin/api.dart --output dist/api-serverOutput:
🔨 Compiling for production...
📁 Entry point: bin/server.dart
📦 Output: build/server
✅ Build successful!
📦 Executable: build/server
Next steps:
# Run locally
./build/server
# Build Docker image
docker build -t my-app .With target: workers:
- Compiles the entry point to WebAssembly (
dart compile wasm) - Output is
build/workers/main.wasmandbuild/workers/main.mjs(withCompiledAppalready exported) --outputis a directory (defaultbuild/workers), not a file path- Next step:
npx wrangler@4 deploy
With target: supabase:
- Compiles the entry point to WebAssembly (
dart compile wasm) - Output is
supabase/functions/<name>/main.wasmandmain.mjs, where<name>is the pubspec'sname --outputis a directory (defaultsupabase/functions/<name>), not a file path- Next step:
supabase functions deploy <name>, not--use-api—--use-apiskips the bundling step that placesmain.wasmnext to the deployedindex.ts, which is whatstatic_filesinsupabase/config.tomldepends on
With target: functions:
- Does nothing — prints a message and exits;
firebase deploy --only functionscompiles on your machine and uploads the result --entry/aim.entryand--outputare silently ignored: there is nothing to compile and nowhere to write it
Configuration
pubspec.yaml
Configure Aim CLI behavior in your pubspec.yaml:
name: my_app
description: My Aim application
dependencies:
aim_server: ^0.4.0
# Aim CLI configuration
aim:
# Entry point (optional)
entry: bin/server.dart
# Environment variables (optional)
env:
PORT: "8080"
HOST: "0.0.0.0"
DATABASE_URL: "postgresql://localhost/mydb"
JWT_SECRET: ${JWT_SECRET} # Expand from system environmentEnvironment Variables
The aim.env section supports multiple formats:
1. Static values:
aim:
env:
PORT: "8080"
ENV: "development"2. Environment variable expansion:
aim:
env:
DATABASE_URL: ${DATABASE_URL} # Reads from system environment
JWT_SECRET: ${JWT_SECRET}
API_KEY: ${API_KEY}3. Default values:
aim:
env:
PORT: ${PORT:8080} # Use $PORT, fallback to 8080
HOST: ${HOST:0.0.0.0} # Use $HOST, fallback to 0.0.0.0
DATABASE_URL: ${DB_URL:postgresql://localhost/dev}4. Mixed:
aim:
env:
PORT: ${PORT:3000} # With default
DATABASE_URL: ${DATABASE_URL} # Required (no default)
DEBUG: "true" # Static valueSupported formats:
$VAR_NAME- Simple expansion${VAR_NAME}- Braces expansion${VAR_NAME:default}- With default value
When you run aim dev, these variables are:
- Read from
pubspec.yaml - Expanded using system environment variables
- Merged with existing environment
- Passed to your application
Example 1 - With defaults:
# pubspec.yaml
aim:
env:
PORT: ${PORT:3000} # Defaults to 3000
DATABASE_URL: ${DB_URL:postgresql://localhost/dev}
DEBUG: ${DEBUG:false}# Terminal (no environment variables set)
aim dev
# Your application receives:
# PORT=3000
# DATABASE_URL=postgresql://localhost/dev
# DEBUG=falseExample 2 - Override defaults:
# pubspec.yaml
aim:
env:
PORT: ${PORT:3000}
DATABASE_URL: ${DB_URL:postgresql://localhost/dev}# Terminal (with environment variables)
export PORT=8080
export DB_URL="postgresql://production/mydb"
aim dev
# Your application receives:
# PORT=8080
# DATABASE_URL=postgresql://production/mydbDevelopment Workflow
1. Create Project
aim create my_api
cd my_api2. Development
# Start dev server with hot reload
aim dev
# Edit files in lib/ or bin/
# Server automatically restarts3. Build for Production
# Compile to executable
aim build
# Test the build
./build/server
# Deploy
scp build/server user@server:/app/Tips & Tricks
Quick Project Setup
aim create my_app && cd my_app && aim devDevelopment with Custom Port
# pubspec.yaml
aim:
env:
PORT: "3000"aim dev
# Server runs on port 3000Multiple Environments
# Development
aim dev
# Production build
ENV=production aim build
./build/serverDebugging Without Hot Reload
aim dev --no-hot-reload
# Use Dart debugger in IDEWatch Specific Directories
aim dev --watch lib,bin,routes,configCustom Entry Points
Useful for microservices:
# API server
aim dev --entry bin/api.dart --port 8080
# Admin server
aim dev --entry bin/admin.dart --port 8081Troubleshooting
"pubspec.yaml not found"
Make sure you're in the project root directory:
cd my_project
aim dev"Entry point not found"
Check that the entry file exists:
ls bin/server.dart
# or specify custom entry
aim dev --entry bin/api.dartHot Reload Not Working
- Check you're editing files in watched directories (
lib/,bin/) - Try disabling and re-enabling:bash
aim dev --no-hot-reload # Test without aim dev # Re-enable
Port Already in Use
# Find and kill process
lsof -i :8080
kill -9 <PID>
# Or use different port
aim dev --port 3000Environment Variables Not Loading
Check your pubspec.yaml formatting:
aim:
env:
PORT: "8080" # Must be quoted
DEBUG: "true" # Booleans as stringsMake sure system variables are exported:
export DATABASE_URL="postgresql://localhost/db"
aim devDatabase Commands
Aim CLI provides database migration tools powered by aim_orm.
aim db:generate
Generate migration SQL from schema changes.
Usage:
aim db:generate [options]Options:
| Option | Description | Default |
|---|---|---|
--name | Migration name | Auto-generated timestamp |
--path | Path to table definitions | aim.database.schema, or lib/schema |
Example:
aim db:generate --name add_users_tableThis command:
- Compares your current schema definitions with the last migration
- Detects added/removed tables, columns, indexes, and constraints
- Generates both UP and DOWN SQL migrations
- Creates a file in the
db/migrations/directory
Output:
db/migrations/
└── 20250121_120000_add_users_table.sqlFor detailed usage, see Migrations Guide.
aim db:migrate
Apply pending migrations to the database.
Usage:
aim db:migrate [options]Options:
| Option | Description |
|---|---|
--target | Apply up to specific migration |
Example:
# Apply all pending migrations
aim db:migrate
# Apply up to specific migration
aim db:migrate --target 20250121_120000_add_users_tableaim db:rollback
Rollback applied migrations.
Usage:
aim db:rollback [options]Options:
| Option | Description | Default |
|---|---|---|
--step | Number of migrations to rollback | 1 |
--target | Rollback to specific migration |
Example:
# Rollback last migration
aim db:rollback
# Rollback last 3 migrations
aim db:rollback --step 3
# Rollback to specific migration
aim db:rollback --target 20250121_100000_initialaim db:reset
Drop the database, recreate it, and apply every migration from scratch. Connects to the admin postgres database to drop/create the target database named in aim.database.url, then replays all migration files.
Usage:
aim db:reset [options]Options:
| Option | Description | Default |
|---|---|---|
--force, -f | Skip the confirmation prompt | prompts |
Example:
aim db:reset # asks "Are you sure? [y/N]" first
aim db:reset --force # for CI or scriptsDestructive
This drops the database named in aim.database.url and everything in it, then recreates it and reapplies every migration. There is no undo.
aim db:status
Show migration status.
Usage:
aim db:statusOutput:
Migration Status:
[✓] 20250121_100000_initial Applied: 2025-01-21 10:00:00
[✓] 20250121_110000_add_posts_table Applied: 2025-01-21 11:00:00
[ ] 20250121_120000_add_comments Pending
Applied: 2 / Total: 3Database Configuration
Configure database connection in pubspec.yaml. See Configuration for every key.
aim:
database:
url: ${DATABASE_URL:postgresql://localhost:5432/mydb}Or use environment variables:
export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
aim db:migrateNext Steps
- Read the Quick Start guide
- Learn about Routing
- Explore Middleware
- Learn about Migrations
