Skip to main content

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.

Terminology

These terms appear throughout the docs and the framework source:

  • handler — the Rust async function you write, annotated with a macro like #[forge::query] or #[forge::mutation].
  • function — a handler once it is registered with the runtime. Forge uses "function" as the generic term for anything in the registry (queries, mutations, jobs, etc.).
  • RPC — the wire protocol. Queries and mutations are called over HTTP POST to /_api/rpc/{function_name}. The frontend bindings hide this detail.
  • query — a read-only handler (#[forge::query]). Participates in the reactivity pipeline; results are pushed to subscribers when the underlying data changes.
  • mutation — a write handler (#[forge::mutation]). Runs inside a database transaction; job and workflow dispatches are flushed after commit.
  • job — a background task (#[forge::job]). Dispatched from mutations, persisted in PostgreSQL, executed by the worker with retries.
  • cron — a scheduled handler (#[forge::cron]). Runs on a cron expression; leader-elected so it executes exactly once across the cluster.
  • workflow — a durable, resumable multi-step process (#[forge::workflow]). Steps survive restarts; signature-guarded to prevent incompatible resume.
  • daemon — a long-running service (#[forge::daemon]). Runs continuously; can be leader-elected or replicated across nodes.
  • webhook — an incoming HTTP callback handler (#[forge::webhook]). Validates signatures and runs inside the Forge runtime.

Key Files

forge.toml

Runtime configuration. Environment variables expand at startup.

[project]
name = "my-app"

[database]
url = "${DATABASE_URL}"

[gateway]
port = 9081

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.

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');

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/svelte package.
  • Dioxus bindings are backed by the forge-dioxus crate.

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.