Project Anatomy
A Forge project builds into a Rust binary with an optional embedded frontend.
Directory Structure
my-app/
├── forge.toml # Configuration
├── Cargo.toml # Rust dependencies
├── .env # Environment variables
├── sqlx.toml # sqlx offline-mode config
│
├── src/
│ ├── main.rs # Application entry point
│ ├── functions/ # Queries, mutations, jobs, workflows
│ │ ├── mod.rs
│ │ └── todos.rs
│ └── schema/ # Data models
│ ├── mod.rs
│ └── todo.rs
│
├── migrations/ # SQL migrations (numbered, run automatically)
│ └── 0001_todos.sql
├── .sqlx/ # Offline query cache generated by `forge migrate prepare`
│
└── frontend/ # Optional frontend target
├── src/
│ ├── routes/ # SvelteKit pages
│ ├── lib/
│ │ └── forge/ # SvelteKit bindings (`forge generate`)
│ └── forge/ # Dioxus bindings (`forge generate --target dioxus`)
├── package.json # Present for SvelteKit frontends
├── Cargo.toml # Present for Dioxus frontends
└── Dioxus.toml # Present for Dioxus frontends
forge new uses explicit template ids. Choose with-svelte/* for SvelteKit frontends and with-dioxus/* for Dioxus frontends.
Key Files
forge.toml
Runtime configuration. Environment variables expand at startup.
[project]
name = "my-app"
[database]
url = "${DATABASE_URL}"
[gateway]
port = 8080
src/functions/
Your backend logic. Each file can contain any combination of:
// Query - read data
#[forge::query(tables = ["todos"])]
pub async fn list_todos(ctx: &QueryContext) -> Result<Vec<Todo>> { ... }
// Mutation - write data
#[forge::mutation]
pub async fn create_todo(ctx: &MutationContext, input: CreateTodoInput) -> Result<Todo> { ... }
// Job - background work
#[forge::job]
pub async fn send_email(ctx: &JobContext, email: String) -> Result<()> { ... }
// Cron - scheduled tasks
#[forge::cron("0 9 * * *")]
pub async fn daily_report(ctx: &CronContext) -> Result<()> { ... }
// Workflow - durable multi-step processes
#[forge::workflow]
pub async fn onboarding(ctx: &WorkflowContext, user_id: Uuid) -> Result<()> { ... }
// Daemon - persistent services
#[forge::daemon]
pub async fn event_processor(ctx: &DaemonContext) -> Result<()> { ... }
// Webhook - incoming HTTP callbacks
#[forge::webhook(path = "/hooks/stripe")]
pub async fn stripe_webhook(ctx: &WebhookContext) -> Result<WebhookResult> { ... }
src/schema/
Data models with #[forge::model] generate frontend types automatically: TypeScript for SvelteKit targets and Rust types for Dioxus targets. The macro handles derive attributes internally, so place #[forge::model] before any #[derive(...)]:
#[forge::model]
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Todo {
pub id: Uuid,
pub title: String,
pub completed: bool,
pub created_at: DateTime<Utc>,
}
migrations/
SQL files prefixed with numbers. Run automatically on startup with distributed locking.
-- @up
CREATE TABLE todos (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title TEXT NOT NULL,
completed BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
SELECT forge_enable_reactivity('todos');
-- @down
SELECT forge_disable_reactivity('todos');
DROP TABLE IF EXISTS todos;
The forge_enable_reactivity() function sets up LISTEN/NOTIFY triggers for real-time subscriptions.
sqlx.toml and .sqlx/
Forge templates scaffold sqlx.toml with offline mode enabled so sqlx::query! and sqlx::query_as!
can be checked without a live database in CI once the cache is prepared.
Refresh the cache after changing SQL or migrations:
forge migrate prepare
Generated frontend bindings
Run forge generate after adding or modifying functions.
// SvelteKit target: frontend/src/lib/forge
import { listTodos$, createTodo, updateTodo, deleteTodo } from "$lib/forge";
import type { Todo, CreateTodoInput } from "$lib/forge";
// Dioxus target: frontend/src/forge
use crate::forge::use_list_todos;
- SvelteKit bindings are backed by the
@forge-rs/sveltepackage. - Dioxus bindings are backed by the
forge-dioxuscrate.
Build Output
cargo build --release
Produces a single binary in target/release/my-app. Enable embedded-frontend feature to bundle the frontend build into the binary for single-artifact deployment.