Forge Backend Infrastructure
website·Indexed 31 days ago
https://tryforge.devAn in-process infrastructure library providing eight backend primitives—including Key-value stores, Queues, Pub/sub, Blob storage, Auth, and Rate limiting—with a versioned contract across Rust, JavaScript, Python, and Go. Forge offers cross-language API stability, PostgreSQL-centered production models, and integration recipes for frameworks like Axum, Hono, Express, FastAPI, and Django ASGI.
What's inside Forge
- Forge provides a unified contract across Rust, JavaScript, Python, and Go to access eight core backend primitives. It uses PostgreSQL as the shared durable backend, though memory can be used for process-local testing and filesystems or S3-compatible stores can be used for blobs.
Understand Forge's cross-language API stability policy
Starting with version 1.1.0, Forge coordinates releases across Rust, JavaScript, Python, and Go. Within the 1.x release cycle, Forge guarantees that public APIs will not be removed or renamed, documented behavior or error classifications will not change, and configuration interpretations will remain consistent. Breaking changes are reserved for the 2.0 release. Deprecations are announced in release notes and documentation, and where practical, will trigger warnings for at least one release before being removed at the next compatibility boundary.Understand Forge architectural scope and ownership
Forge is an in-process infrastructure library for application backends (Rust, JS, Python, Go) that uses PostgreSQL as the durable system of record. It provides bounded primitives but does not manage application-level concerns.
Forge owns:
- Primitive APIs and cross-language contracts
- PostgreSQL/memory behavior, filesystem and S3 blob adapters
- Schema, migrations, validation, and runtime diagnostics
- Queue leases, retry state, dead-letter operations, and worker mechanics
- Bounded CloudEvents, trace-context, and invalidation encodings
- Server-side config caches and invalidation publication
The Application owns:
- Protocol surfaces (HTTP, RPC, GraphQL, MCP)
- Business tables, domain transactions, and authorization policy
- Deployment order, process signals, and pool budgets
- Worker handlers, side-effect idempotency, and audit records
- Transport delivery, telemetry export, and secret storage
- Frontend frameworks and client-side state/refetch logic
Understand Forge Event Delivery architecture
Forge is a backend library that encodes bounded invalidation hints. It does not manage HTTP routes, WebSockets, or client-side refetching. The standard flow is: the backend commits authoritative state, publishes an invalidation hint, and an application-owned transport forwards that hint to connected clients. Clients receiving a hint should then fetch the authoritative state again. The system is designed so that loss, duplication, delay, or reordering of hints does not affect correctness.Understand Forge primitive behavioral contracts
Forge maintains consistent method shapes across different backends, but durability and process scope vary by backend. The behavioral contract for primitives defines the guarantees for consistency, ordering, duplication, expiry, retries, outages, and multi-process interactions. For a runtime view of these constraints, developers should check thebackendCapabilitiesof the specific backend being used.Schedule delayed tasks and cron jobs with Forge
Forge allows you to schedule one-time delayed tasks using
schedule_at(or.at()in Rust) and recurring tasks usingschedule_cron(or.cron()in Rust).Important: Schedules only fire when the scheduler is ticked. The loop that ticks the scheduler is also the recommended location to run Forge's maintenance sweep. Refer to the Operations documentation for the specific loop implementation required on every replica.
# Schedule a one-time task to expire a link in one hour await forge.schedule_at(now_ms + 3_600_000, "expire", json.dumps({"slug": slug})) # Schedule a daily report at midnight await forge.schedule_cron("nightly-report", "0 0 * * *", "reports", "{}")use std::time::{Duration, SystemTime}; use forgelib::ScheduleOpts; // Schedule a one-time task to expire a link in one hour let when = SystemTime::now() + Duration::from_secs(3600); forge.schedule().at(when, "expire", payload.into(), ScheduleOpts::new()).await?; // Schedule a daily report at midnight forge.schedule().cron("nightly-report", "0 0 * * *", "reports", b"{}".to_vec().into(), ScheduleOpts::new()).await?;Manage user sessions and authentication middleware
UsevalidateSessionin middleware to resolve a bearer token or session cookie to a user ID. To log a user out, userevokeSessionto invalidate the token immediately so it no longer resolves to a user.// Rust Example // Middleware: resolve bearer token to a session, else 401 let Some(session) = forge.auth().validate_session(bearer).await? else { return unauthorized(); }; let user_id = session.user_id; // Logout: revoke the session so the token stops resolving forge.auth().revoke_session(bearer).await?;Export Forge metrics to Prometheus
Forge provides built-in metrics for operations, pool use, retries, queue/worker state, and more. Use
renderPrometheus()(or language equivalent) to emit Prometheus-formatted text. This does not require a separate collector or exporter installation.Note: Forge uses fixed low-cardinality labels. High-cardinality data like user IDs, job IDs, or model outputs are never used as labels or span attributes to prevent metric explosion.
Implement API keys and one-time tokens
API-key secrets are returned once; Forge stores only the hash. Implement bounded expiries, explicit scopes, and minimal non-secret metadata. Enforce owner and scope checks in the application policy after verification.
One-time tokens must be purpose-scoped, expiring, hashed, and atomically consumed. Use them for opaque return state or record IDs rather than authorization policy. Tokens cannot be replayed for different purposes or recovered once consumed.
Offload slow work to a background queue
Improve request latency by performing only lightweight operations (like incrementing a counter) inline and offloading heavy processing to a queue. Workers lease jobs from the queue and must acknowledge (ack) success or negatively acknowledge (nack) failure to trigger a retry.// In the request handler: cheap now, the rest later await forge.kvIncr(`clicks:${slug}`, 1); await forge.queue<{ slug: string } ("clicks").enqueue({ slug }, { maxAttempts: 3 }); // In the worker: process the job await forge.worker<{ slug: string }>( "clicks", async (job) => { await handle(forge, job.payload); });Implement typing indicators and user presence
Typing indicators are transient and should be published to a topic without being stored. User presence is managed by setting a KV key with a short TTL (e.g., 30 seconds) that is refreshed via a heartbeat endpoint.// Typing indicator (Live only) app.post("/api/rooms/:room/typing", async (c) => { await forge.topic(`chat:${c.req.param("room")}`).publish({ type: "typing", userId }); return c.body(null, 204); }); // Presence heartbeat app.post("/api/heartbeat", async (c) => { const userId = await requireUser(c); await forge.kvSet(`presence:${userId}`, "1", 30); // expires in 30s await forge.topic("presence").publish({ userId, online: true }); return c.body(null, 204); });Implement user authentication and session management with Forge
Forge provides primitives for password hashing, session creation, and rate limiting. Useforge.rate_limit_checkto prevent brute-force attacks on signup/login,forge.hash_passwordandforge.verify_passwordfor security, andforge.create_sessionto manage user state. Useforge.kv_setwith thenx=True(set if not present) parameter to ensure email uniqueness during registration.# Example Signup Logic @api.post("/api/signup", status_code=201) async def signup(request: Request, body: Credentials) -> dict[str, Any]: forge = request.app.state.forge email = body.email.strip().lower() # Rate limiting limit = await forge.rate_limit_check("links-auth", email, 20, 60.0, True) if not limit.allowed: raise HTTPException(429, "too many attempts") user = {"id": str(uuid.uuid4()), "email": email, "password_hash": await forge.hash_password(body.password)} # Atomic check if email exists if not await forge.kv_set(user_email_key(email), json.dumps(user), None, True): raise HTTPException(409, "email already registered") token = await forge.create_session(user["id"], 1800.0, 604800.0) return {"token": token, "user": {"id": user["id"], "email": email}}