$ 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.

01 · REQUESTBrowserGET /hello
02 · RUSTYour handlerbackend.wasm
pages/hello/mod.rs
03 · PROPSJSON across the wiretyped by codegen
04 · REACT SSRYour componentserver.js
pages/hello/page.tsx
05 · HTMLStreamed + hydratedinteractive in the browser
rs/src/pages/hello/mod.rs
#[derive(Serialize)]
pub struct Props {
    pub message: String,
}

pub async fn handler(
    _req: ForteRequest<'_>,
) -> Result<Props> {
    Ok(Props {
        message: "hello from Rust".into(),
    })
}
props · serialized by forte-json
{
    "message": "hello from Rust"
}
fe/src/pages/hello/page.tsx
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.

my-app/ — after `forte add page blog/[slug]`
rs/src/
├─pages/blog/[slug]/mod.rsYOU WRITE
├─route_generated.rsGENERATED
fe/src/
├─pages/blog/[slug]/page.tsxYOU WRITE
├─pages/blog/[slug]/.props.tsGENERATED
├─actions/.generated/submit.tsGENERATED
├─paths.generated.tsGENERATED
YOU WRITE two files per pageGENERATED kept in sync on every build
~/my-app
$ forte add page blog/[slug]
created rs/src/pages/blog/[slug]/mod.rs
created fe/src/pages/blog/[slug]/page.tsx
$ forte dev
generated .props.ts · paths.generated.ts · router
ready — http://localhost:5173/blog/hello-world
$

$ 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.

forte devlocal libSQL under .forte/data/ — nothing to install
#[forte_sdk::test]in-memory database — fast, isolated, no Docker
forte deploymanaged database, injected by the fn0 runtime

Same code in all three. Only the environment changes.

docs/doc-db →
rs/src/db.rs
#[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

rs/src/actions/upload_avatar.rs
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 dev serves the same API from your local filesystem; tests use object_storage::memory().
docs/object-storage →

$ 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.

~/my-app
$ cargo binstall forte-cli
$ forte init my-app
my-app created
$ cd my-app && forte dev
dev server running
$