Skip to content

Quickstart

Add Forge to the backend process, point it at a PostgreSQL database it can own, and call init() to get one client for all eight primitives. Forge does not create an HTTP server, install process signal handlers, or add frontend dependencies.

Terminal window
npm install forgelib

Forge keeps its own state in Postgres. It owns its forge_* tables, so give it a database (or its own schema) and keep your product tables separate. Development startup migrates automatically; production deployments call the migration API before starting the application.

Terminal window
# any Postgres works; create one for Forge to own
createdb myapp

Configuration lives in a forge.toml at your project root. It’s the single source of config: init() reads it and builds the runtime. String values can read the environment with ${VAR} or ${VAR:-default}, so the same file works in tests and in production.

forge.toml
[postgres]
url = "${DATABASE_URL:-postgres://localhost/myapp}"

Everything else has a convenient development default, so this is enough to start locally. Set forge.environment = "production" in production; automatic migration then defaults off. The full list of settings is in Configuration.

init() reads forge.toml, migrates the system database, and returns a client. The same calls look like this in each language. (Pick a language once and the tabs on this site follow your choice.)

import { ForgeClient } from "forgelib";
const forge = await ForgeClient.init(); // reads ./forge.toml, runs migrations
// auth: hash a password, open a session
const hash = await forge.hashPassword(password);
const token = await forge.createSession(userId);
// key/value: an atomic counter
const views = await forge.kvIncr(`views:${userId}`, 1);
// queue: hand work to the background
await forge.queue<{ to: string }>("emails").enqueue({ to: email });
// rate limit: 20 attempts per minute, keyed by email
const limit = await forge.rateLimitCheck("login", email, 20, 60);
if (!limit.allowed) throw new Error("slow down");

One shape difference is worth noting. Rust reaches each primitive through an accessor and uses option structs, like forge.kv().set(key, value, SetOpts::new()). JavaScript and Python keep the exact raw methods on the client (forge.kvSet(...), forge.kv_set(...)) for contract parity and add native JSON handles for application values. Go uses one concrete client with explicit option structs and context.Context on every blocking operation.

Enqueued jobs sit in the queue until an application-owned worker process starts a Forge worker. Schedules fire only when the application runs a scheduler loop. A normal deployment runs workers for the queues it owns, a scheduler loop, and periodic maintenance. Operations covers those roles, and Integrations shows HTTP, deployment, MCP, and observability boundaries.