Creates a new Rustwing project.
rustwing new my_appGenerates a complete workspace with an API server, a DB/LLM-backed worker tick loop, and a frontend directory.
The scaffold includes .rustwing-version, recording the CLI, framework, and
template versions used to create it.
Run this read-only diagnostic from a generated project root. It checks scaffold version drift, the SQLx/auth dependency baseline, generator wiring markers, migration structure, and required configuration documentation. Structural failures return a non-zero status; dependency version differences are reported as warnings so applications retain control of their upgrade schedule.
rustwing doctorPreviews a conservative framework upgrade. The default command is a dry run:
rustwing upgradeApply it explicitly with:
rustwing upgrade --applyThe apply flow runs cargo update -p rustwing, verifies the project with
cargo check, and refreshes .rustwing-version only after compilation passes.
It does not rewrite generated source, migrations, or application-owned files.
Runs the API server (cargo run --bin api).
rustwing runMust be run from the project root.
Generates new code within an existing project.
Generates a full REST resource: model, DTOs, service functions, SQLx-native repository glue, handlers, router entries, OpenAPI metadata, and migration.
rustwing g resource product \
--fields 'title:string:required:length(1,255)' \
--fields 'price:f64:required:range(0.0,9999.0)' \
--fields 'inventory:i32:required:range(0,10000)' \
--fields 'description:string:optional:none'Generated resource handlers call a generated service module first. The service owns validation, pagination normalization, and business logic extension points; repositories stay focused on database access.
Use --scope for resources that are owned by or nested under another record.
rustwing g resource comment \
--scope ticket_id \
--fields 'ticket_id:uuid:required' \
--fields 'body:string:required'This generates nested routes like /tickets/{ticket_id}/comments, create/update DTOs that do not accept ticket_id from the body, services that receive ticket_id from the route, and scoped SQLx repository helpers.
Scopes can be repeated:
rustwing g resource note \
--tenant org_id \
--scope ticket_id \
--fields 'org_id:uuid:required' \
--fields 'ticket_id:uuid:required' \
--fields 'body:string:required'This generates routes like /orgs/{org_id}/tickets/{ticket_id}/notes and helpers such as find_by_org_id_and_ticket_id.
Use --tenant as a clearer alias for a SaaS tenant scope. It behaves like a first --scope value.
rustwing g resource ticket \
--tenant org_id \
--fields 'org_id:uuid:required' \
--fields 'subject:string:required:length(1,255)' \
--fields 'assigned_member_id:uuid:optional'Normal single-tenant CRUD remains the default. Scoped mode is opt-in and each scope field must be present in --fields.
Scoped mode generates:
- Nested routes like
/orgs/{org_id}/tickets - Create/update DTOs that do not accept scope fields from the request body
- Service functions that receive scope IDs from the route
- Scoped repository helpers such as
find_by_org_id,find_by_org_id_and_id,update_by_org_id_and_id, anddelete_by_org_id_and_id - A migration index matching the generated scope columns
Scope keeps generated SQL within the requested tenant or parent, but it does not authorize the caller. Add a service policy that verifies the authenticated actor's membership or access before calling the scoped repository.
Exports the generated app OpenAPI document. The command compiles and runs the API binary in OpenAPI export mode, so it does not require DATABASE_URL or a live database connection.
rustwing g openapiDefault output:
openapi/openapi.jsonFlags:
| Flag | Behavior |
|---|---|
--output <path> |
Write the OpenAPI document to a custom path |
--check |
Exit non-zero if the output file is stale |
--stdout |
Print the OpenAPI JSON to stdout |
CI example:
rustwing g openapi --checkGenerates a typed frontend fetch client from the current OpenAPI document.
rustwing g client typescriptDefault output:
frontend/generated/Generated files:
types.ts
client.ts
index.tsThe client supports base URL configuration, bearer token injection, typed request/response bodies, typed route params, and generated resource methods such as api.tickets.create({ orgId, body }).
Use --output <dir> to change the destination.
Generates a domain model, repository trait impl, and migration — no HTTP layer.
rustwing g model tag --fields 'name:string:required'| Type | Rust type | SQL type |
|---|---|---|
string / text |
String |
TEXT |
int / i32 |
i32 |
INTEGER |
i64 |
i64 |
BIGINT |
float / f64 |
f64 |
DOUBLE PRECISION |
bool |
bool |
BOOLEAN |
uuid |
Uuid |
UUID |
datetime |
DateTime<Utc> |
TIMESTAMPTZ |
json / jsonb |
serde_json::Value |
JSONB |
ref |
Uuid |
UUID REFERENCES <table> (id) |
name:type:required|optional[:validator]
Optional fields are emitted as Option<T> in domain, create, and insert
types. Update DTOs retain configured validators. The names id, created_at,
and updated_at are reserved because Rustwing manages them.
The ref type auto-derives the foreign table name from the field name (user_id → users).
For nullable PATCH semantics, use rustwing::prelude::Nullable<T> manually in update DTOs when a field needs three states:
- missing field = do not change
- JSON
null= set the database column toNULL - value = set the database column to that value
Add #[serde(default)] to the DTO field so missing input maps to Nullable::Missing.
| Hint | Generates |
|---|---|
length(1,255) |
#[validate(length(min = 1, max = 255))] |
range(0.0,9999.0) |
#[validate(range(min = 0.0, max = 9999.0))] |
email |
#[validate(email)] |
url |
#[validate(url)] |
none |
No validator |
| omitted | Type-based default (String → length, numbers → range) |