How StarkForge Works
The big picture of configuration, collections, feature modules and the path a request takes through the framework
StarkForge turns a description of your backend into a running Express app. This page follows that conversion from the first line of your code to the moment a response leaves the server, so you know what each part of the framework does before you read the detailed pages.
The Big Picture
- 1
- You describe your backend with options and a list of collections.
new Forgechecks the options and builds the services that everything shares.- Each collection has a
type, and the feature module for that type turns its routes into Express routes. build()assembles the middleware and mounts every collection under/api/v1.start()connects to the database and starts listening.
This is the smallest program that goes through all five steps:
// src/server.ts
import { Forge, collection, fields } from "@starklabs/forge";
const products = collection({
type: "crud",
route: "products",
model: "Product",
schema: {
name: fields.requiredString,
price: fields.requiredNumber,
},
routes: [
{ method: "get", path: "/", handler: "readAll", auth: "public" },
{ method: "get", path: "/:id", handler: "read", auth: "public" },
],
});
const forge = new Forge({ collections: [products] });
await forge.start();The collection is step 1 and new Forge is step 2. The key type: "crud" selects the CRUD module in step 3, which turns the two route entries into GET /api/v1/products and GET /api/v1/products/:id. The call to start() performs steps 4 and 5.
Collections and Feature Modules
A collection describes one resource or feature. Its type decides which feature module handles it. The module knows the handlers of that type, checks that every route of the collection is valid and registers the routes on an Express router.
| Type | What its module provides |
|---|---|
crud | Database resources: read, list, create, update, delete and file handlers |
auth | Sign-up, sign-in, sessions, one-time codes, magic links, social sign-in and two-factor codes |
account | The signed-in user's own profile and role |
health | A health check |
You write the crud collections yourself. The other types come built in: when you set builtinConfig.auth or builtinConfig.account, StarkForge generates the auth or account collection for you, and it adds a health collection automatically unless you define your own. Generated collections are ordinary collections. They pass through the same pipeline as yours, with the same limits and the same error format, and forge.collections lists all of them, generated ones first.
The Request Pipeline
Every request travels through the same stages, in this order:
- 1
- A request id is attached. It is returned in the
X-Request-Idheader and repeated asrequestIdin every error. - Secure headers are set.
- CORS decides whether a browser on another origin may call the API.
- The request is logged, when
logRequestsis on. - The response is compressed.
- The global rate limit counts the request.
- JSON and form bodies are parsed, up to
bodyLimit, and cookies are read. - The input sanitizer removes keys that start with
$, keys that contain a dot and the key__proto__. - The origin guard refuses state-changing browser requests from sites that are not in your front-end URLs.
- The router picks the route. Credential routes pass the stricter rate limit, then the authenticator checks the access level and the roles, and routes with files parse the upload.
- The handler validates the body, does its work and writes the response.
- A request id is attached. It is returned in the
A request that matches no route ends in the not-found handler. An error thrown at any stage ends in the central error handler. Both answer with the same JSON error shape, which the Errors page describes.
| Stage | What it protects against |
|---|---|
| Secure headers | Common browser-side attacks, through the headers that helmet sets |
| CORS and origin guard | Requests from websites you did not allow |
| Rate limits | Floods and password guessing |
| Body limit | Oversized requests |
| Input sanitizer | NoSQL operator injection and prototype pollution |
| Authenticator | Callers without the right access level or role |
| Validation | Missing, malformed or unexpected body fields |
| Error handler | Internal details leaking into error responses |
Services and Dependencies
new Forge builds the objects that the modules share once: the validated configuration, the logger, the data source, token signing, password hashing, encryption, email, uploads, the authenticator and more. They are collected in forge.services and handed to each feature module explicitly. There are no global singletons, so several Forge instances can run in one process, which the test helpers rely on.
The second constructor argument replaces any of these services with your own. Its keys are logger, dataSource, emailProvider, storageProviders and fetch, which social sign-in uses to call the provider. Most apps never need it. Tests use it to swap MongoDB, email delivery and file storage for in-memory fakes.
Build, Start and Stop
| Method | What it does |
|---|---|
build() | Assembles the middleware, mounts every collection and returns the Express app. It does not connect to the database or listen, and calling it twice is safe. Use it when you wrap the app yourself |
start() | Calls build(), connects to the database, listens on port, prints the banner when logRequests is on and returns the http.Server. A second call returns the same server |
stop() | Stops accepting connections, waits for requests in progress and closes the database connection |
use(module) | Registers a custom feature module, or replaces a built-in one. It must run before build() or start(), or it throws a ConfigurationError |
forge.app is the Express app itself. On SIGINT (Ctrl + C) and on SIGTERM, which hosts send when they stop a container, start() has installed handlers that call stop() and then exit the process, so a deployment ends without cutting off requests. stop() removes those handlers again, and they are not installed in the test environment.
Where to Go Next
Routes
Declare endpoints, access levels and per-route settings.
Authentication
Add sign-up, sign-in and sessions.
:::