$ forte
Every request hits Rust first
Forte is the full-stack framework built on fn0. Your Rust handler receives the request and returns Props as JSON; React server-renders them into HTML and hydrates on the client. One repo, one deploy command.
$ request lifecycle
Rust computes, React renders
A page is one Rust handler and one React component. In between flows a single JSON document — the Props. Forte generates the TypeScript types for it, so the compiler catches what used to be a runtime bug.
pages/hello/mod.rs
pages/hello/page.tsx
#[derive(Serialize)] pub struct Props { pub message: String, } pub async fn handler( _req: ForteRequest<'_>, ) -> Result<Props> { Ok(Props { message: "hello from Rust".into(), }) }
{ "message": "hello from Rust" }
import type { Props } from "./.props"; // ^ generated from the Rust Props type export default function Hello(props: Props) { return <h1>{props.message}</h1>; }
Follow message across the three panels — one field, one type, checked end to end.
Need plain JSON instead of a page? Handlers under rs/src/apis/ skip the SSR step and return the response directly at /api/….
$ file conventions
The directory is the router
Drop a handler in rs/src/pages/ and a component in fe/src/pages/ — Forte generates everything between them: the router, the Zod-typed props, the path helpers. Change the Rust Props and the TypeScript follows; drift becomes a compile error, not a bug report.
$ doc-db
A database with no setup step
Declare a struct, mark its keys, and #[forte_doc] derives typed get / put / query / delete — plus optimistic transactions. No connection strings, no migrations, nothing to provision.
Same code in all three. Only the environment changes.
docs/doc-db →#[forte_doc] pub struct User { #[pk] pub id: String, pub name: String, } // write UserPut(user).send_with(&db).await?; // read let user = UserGet { id }.send_with(&db).await?; // transaction with conflict retry db.trx(|trx| async move { let mut user = trx .get(UserGet { id }) .await? .ok_or_else(|| anyhow!("not found"))?; user.name = "New Name".into(); trx.commit(()) }).await;
$ object storage
let bucket = object_storage::bucket(); bucket.put("avatars/42.png", bytes).await?; // hand the browser a direct download link let url = bucket .presigned_get_url( "avatars/42.png", Duration::from_secs(3600), ) .await?;
Files, without the S3 ceremony
Every project gets its own private bucket, wired in with zero configuration.
- ▸Credentials never enter your code — the fn0 runtime signs every request out of band.
- ▸Presigned URLs let browsers upload and download directly, without routing through your app.
- ▸
forte devserves the same API from your local filesystem; tests useobject_storage::memory().
$ and the rest
Everything a service needs
Typed server actions
Write a Rust handler with Input / Output; the browser gets a generated, Zod-validated TypeScript client. await submit({ message }) — that's the whole integration.
docs/forte/actions →Queue & cron tasks
Background jobs with enqueue!, scheduled tasks with a cron.yaml. Same repo, same types, deployed together.
docs/forte/cli →Testing built in
#[forte_sdk::test] runs handlers against the in-memory database and storage — fast, isolated, no Docker required.
docs/forte/testing →$ getting started
From zero to dev server
forte dev gives you hot reload for React and automatic rebuilds for Rust. When it works, forte deploy ships it to fn0 Cloud.
fn0.dev — the page you're reading — runs on Forte.