Getting Started
Puerto is a CLI tool that scaffolds production-ready Rust backends with Domain-Driven Design architecture. From install to running API in under a minute.
Prerequisites
- Rust stable (1.85+) — install via rustup
- Cargo (included with Rust)
- Optional: PostgreSQL + SQLx CLI for database projects
Installation
Install the Puerto CLI from crates.io:
cargo install puerto Verify the installation:
puerto --version New Project
Create a new Puerto workspace. The interactive wizard asks for your project name, whether you need database support, and whether to include the Greeting demo entity.
puerto new my-app For non-interactive use (CI/scripts):
puerto new --name my-app --db --no-demo Generated structure:
Generate Scaffold
Scaffold a complete DDD entity across all layers. Puerto creates the domain model, repository trait, use cases, infrastructure adapter, and presentation routes — wired together automatically.
puerto generate scaffold User What gets created:
Entity Fields
Define typed fields on your entities. The type system flows through every DDD layer — domain model, Params, DTOs, repository rows, and SQL migrations all derive from the field list.
From the CLI:
puerto generate scaffold Product name:String price:i64! sku:String
Append ! after a type to mark it as unique (e.g., sku:String! generates a unique DB constraint). Fields are persisted to puerto.toml and used by all generators.
Or edit puerto.toml directly:
Supported types:
| Rust Type | SQL Type | OpenAPI |
|---|---|---|
| String | TEXT | string |
| i64 | BIGINT | integer(int64) |
| bool | BOOLEAN | boolean |
| f64 | DOUBLE | number(double) |
| Uuid | UUID | string(uuid) |
| DateTime<Utc> | TIMESTAMPTZ | string(date-time) |
| Option<T> | nullable | T? |
| Vec<String> | TEXT[] | array[string] |
| HashMap<String, String> | JSONB | object |
Every entity always includes id, created_at, updated_at, deleted, and deleted_at as system fields — these are generated automatically and should not be added to puerto.toml.
If entity.fields is empty or absent, entities default to a single name: String field for backward compatibility.
Validate your configuration:
puerto validate
Checks entity names (PascalCase), field names (snake_case), field types (against the type registry), duplicate entities and fields, and warns about Option<T> fields marked unique.
Layer Generators
Generate one DDD layer at a time. Ideal for AI-assisted development — keeping each agent focused on a single layer prevents context drift and makes compiler errors immediately actionable.
puerto generate domain Product puerto generate application Product puerto generate repository Product puerto generate presentation Product
Each command validates the prior step completed and prints a Next: hint. Use puerto generate scaffold when you want all layers at once.
Add a Use Case
Add a single use case to an existing entity. Puerto generates the trait, implementation, and unit tests.
puerto generate use-case User create Database (SQLx)
Puerto has first-class SQLx + Postgres support. Enable it when creating a project — Puerto infers the choice for every subsequent scaffold automatically.
puerto new --name my-app --db puerto generate scaffold User puerto generate migration create_users_table
Puerto reads puerto.toml to decide: db projects get a PgUserRepository with a connection pool and auto-created migration; non-db projects get InMemoryUserRepository. No flag needed on every scaffold.
Architecture
Puerto enforces a strict dependency rule: dependencies only point inward. The domain layer has zero external dependencies.
IDE Snippets
Every puerto new project ships with 23 Puerto-adapted Rust snippets for Zed, VS Code, and nvim+LuaSnip. No configuration needed — files are written automatically and loaded by your editor from the project root.
Zed
Snippets are written to .zed/snippets/rust.json. Zed loads project-local snippets automatically — no further setup.
VS Code
Snippets are written to .vscode/puerto.code-snippets. VS Code picks up workspace snippet files automatically.
nvim + LuaSnip
The VS Code file uses TextMate format, which LuaSnip can load directly. Add to your config:
require("luasnip.loaders.from_vscode").lazy_load({ paths = {"./.vscode"} }) Available snippets
domain-model Entity struct + Props + new(props) + from_repository() domain-errors thiserror enum with machine-readable error codes repository-trait RepositoryTrait + mockall mock in pub mod mocks domain-use-case Params struct + UseCaseTrait app-use-case UseCaseImpl with LoggerTrait injection + unit tests included persistence-entity DB row struct (FromRow) + TryFrom/From conversions persistence-repo PgEntityRepository — find_by_id + save with SQLx macros should-do-test #[tokio::test] with Arrange / Act / Assert comments sqlx-test #[sqlx::test(migrations = "migrations")] — real Postgres pool sqlx-repo-test-module Full integration test module for PgEntityRepository object-mother Object Mother builder: random(), with_name(), build() + 9 more: lib.rs blocks, poem DTOs, response enums, error mappers, API struct
Switching IDE or regenerating
Run puerto generate snippets from your project root at any time to write both IDE files. Use --ide to target a specific editor:
puerto generate snippets puerto generate snippets --ide zed puerto generate snippets --ide vscode