@molecule/api-server-default-express
v1.0.1
Published
Default Express server factory: bonds setup, DB migrations, body/cookie/cors middleware, /api router mount, /health endpoint, 401 normalization, optional HTTPS via self-signed pem certs. Extracts 80-line server.ts shipped by 79 fleet apps.
Readme
@molecule/api-server-default-express
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
@molecule/api-server-default-express — drop-in Express server
factory used by the molecule fleet's api/src/server.ts.
createServerFactory({ setupBonds, runMigrations, getRouter })
returns a (port?) => Promise<server> function that runs
migrations, wires bonds, mounts router + middleware, and starts
an HTTP (or self-signed HTTPS for local dev) listener.
Quick Start
import { createServerFactory } from '@molecule/api-server-default-express'
// In your app's api/src/server.ts these come from the scaffolded files:
// import { setupBonds } from './bonds/index.js'
// import { runMigrations } from './scripts/migrate.js'
const create = createServerFactory({
setupBonds,
runMigrations,
// Router loads lazily AFTER setupBonds() so bond-conditional route
// maps see fully-registered providers. The module must export
// `router`. In your app: getRouter: () => import('./App/router.js')
getRouter: async () => ({ router }),
})
// Runs migrations → wires bonds → mounts middleware + router at /api →
// listens on PORT (default 4000). The scaffolded server.ts exports
// `create` and invokes it when the file is run directly.
await create()Type
feature
Installation
npm install @molecule/api-server-default-express @molecule/api-error-tracking @molecule/api-logger @molecule/api-middleware-body-parser @molecule/api-middleware-cookie-parser @molecule/api-middleware-cors @molecule/api-secrets express
npm install -D @types/expressAPI
Interfaces
CreateServerOptions
Options for createServerFactory.
interface CreateServerOptions {
/** App-specific bond wiring (resolves secrets + wires providers). */
setupBonds: () => Promise<void>
/** DB migration runner (typically the `createMigrator()`-bound function). */
runMigrations: () => Promise<void>
/**
* Lazy router import. Loaded AFTER `setupBonds()` so bond-conditional
* route maps see fully-registered providers at module-evaluation time.
*/
getRouter: () => Promise<{ router: express.Router }>
/**
* Optional hook to mount middleware AFTER cors+cookieParser but
* BEFORE the body parser. Use this for routes that need their own
* multipart streaming (file uploads via busboy) — the body parser's
* `files: 0` config would silently consume the multipart stream.
*/
preBodyParser?: (app: express.Express) => Promise<void> | void
/**
* Optional hook called after `setupBonds()` but before the router
* import. Use for additional one-shot setup (e.g. entitlements
* tier-registry registration that runs after the bonds are wired).
*/
postBondsSetup?: () => Promise<void> | void
/**
* Optional hook to mount middleware on `/api` BEFORE the canonical
* `app.use('/api', router)` mount. Use for app-specific authed
* content handlers (`/api`-prefixed) that need to run before the
* resource router.
*/
preApiRouter?: (app: express.Express) => Promise<void> | void
}TaggedError
A deliberately-tagged molecule error mapped to a real HTTP status by the API.
interface TaggedError {
/** HTTP status to return (e.g. 503 for a missing provider config). */
statusCode: number
/** Machine-readable key the app/IDE maps to a friendly message. */
errorKey: string
/** Human-readable message. */
message: string
}Functions
classifyTaggedError(error)
Classify a thrown value for the API error middleware. Returns a {@link TaggedError}
ONLY for values deliberately tagged by molecule with BOTH a numeric statusCode
AND a string errorKey — e.g. a provider's config-missing throw (statusCode: 503,
errorKey: 'config.notConfigured'). These are expected, actionable conditions a
user must resolve (a missing STRIPE_SECRET_KEY is theirs to set, not a server bug),
so the middleware surfaces the real status + errorKey instead of an opaque 500 — the
app/IDE can then show "configure X to enable this feature".
Requiring BOTH fields is deliberate: it keeps arbitrary library errors that merely
carry a .statusCode (e.g. an AWS SDK error) from being silently surfaced with a
status molecule never chose. Returns null for everything else (→ default 500 path).
function classifyTaggedError(error: unknown): TaggedError | nullerror— The thrown value caught by the error middleware.
Returns: The classified tagged error, or null if it isn't a molecule-tagged error.
createServerFactory(opts)
Returns an Express server-creation function bound to the given
setupBonds / runMigrations / router loaders. The returned create
builds the canonical molecule fleet server:
- Migrations run first, then bonds, then router import.
- Global browser-security headers ({@link securityHeadersMiddleware}) applied before any router (anti-clickjacking + nosniff + referrer baseline).
bodyParser/cookieParser/corsmiddleware applied.- Router mounted at
/api. /healthendpoint with{ status: 'ok', timestamp }.- Bare-string
Unauthorized/Unauthorized.errors normalized to 401. - HTTPS in dev if
process.env.HTTPSis set, using self-signed certs from optional dependencypem. process.on('uncaughtException')+unhandledRejectionregistered on first call (idempotent across multiplecreate()invocations).
function createServerFactory(
opts: CreateServerOptions,
): (port?: number) => Promise<express.Express | https.Server>errorMiddleware(error, req, res, _next)
Terminal Express error middleware for the canonical molecule fleet server.
Resolves a thrown value to exactly one of three sanitized responses and NEVER
delegates to Express's built-in finalhandler:
- Bare-string
Unauthorized/Unauthorized.→401with the string body (so authSelf-style middleware routes to 401 instead of a 500 page). - A deliberately-tagged molecule error ({@link classifyTaggedError}) → its real
statusCode+{ error, errorKey }JSON (expected, user-actionable config conditions, e.g. a missingSTRIPE_SECRET_KEY→ 503config.notConfigured). - EVERYTHING else (untagged library throws, null derefs, driver errors) → a
generic
500 { error: 'Internal Server Error' }, logged server-side AND reported to the bonded error tracker (@molecule/api-error-tracking'scaptureException, a documented no-op when no tracker is bonded).
Only case 3 is captured: cases 1–2 (401s, tagged config-missing 503s, and any other tagged 4xx/5xx) are expected, user-actionable conditions — not defects — so reporting them would drown real faults in noise.
Case 3 is the security-critical branch: it is safe-by-construction and does NOT
depend on NODE_ENV. Calling next(error) here would fall through to Express's
finalhandler, which embeds err.stack in the HTTP response body whenever
app.get('env') !== 'production' (the default when NODE_ENV is unset or
development), disclosing absolute server paths, module layout, dependency
versions, and query/data fragments. Returning the opaque 500 unconditionally
removes that leak for every flagship app regardless of how it is deployed.
function errorMiddleware(
error: any,
req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>,
res: Response<any, Record<string, any>, number>,
_next: NextFunction,
): voiderror— The thrown value caught by Express.req— The request (used only as capture context for error tracking).res— The response to write the sanitized error to._next— The next function (intentionally never called for untagged errors).
registerServerCreatedHook(hook)
Register a hook to run with the HTTP(S) server right before it listens.
Typically called from a bond's setup (e.g. setupRealtimeSocketio) during
setupBonds(), which runs earlier in create() than server construction.
function registerServerCreatedHook(
hook: (server: http.Server | https.Server) => void | Promise<void>,
): voidhook— Receives the realhttp.Server/https.Server.
securityHeadersMiddleware(_req, res, next)
Global browser-security headers applied to EVERY response (mounted before the
routers in createServerFactory, mirroring the molecule.dev platform server).
Defaults are conservative and framework-agnostic — no app-specific CSP source lists, just the clickjacking / MIME-sniffing / referrer baseline a JSON API should always ship:
X-Content-Type-Options: nosniff— stop MIME-type sniffing.X-Frame-Options: DENY+Content-Security-Policy: frame-ancestors 'none'— anti-clickjacking. A generated app that intends to be embedded (iframe) can override these in its own middleware.X-XSS-Protection: 0— disable the legacy, buggy XSS auditor (modern correct value; CSP is the real defense).Referrer-Policy: strict-origin-when-cross-origin— don't leak full URLs cross-origin.Strict-Transport-Security— production only (mirrors the platform server'sNODE_ENVcheck) so local plain-HTTP dev isn't force-upgraded to HTTPS.
function securityHeadersMiddleware(
_req: Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>,
res: Response<any, Record<string, any>, number>,
next: NextFunction,
): void_req— The request (unused).res— The response to set headers on.next— Express next.
Injection Notes
Requirements
Peer dependencies:
@molecule/api-error-tracking^1.0.1@molecule/api-logger^1.0.1@molecule/api-middleware-body-parser^1.0.1@molecule/api-middleware-cookie-parser^1.0.1@molecule/api-middleware-cors^1.0.1@molecule/api-secrets^1.0.1express^4.0.0 || ^5.0.0
Runtime Dependencies
@molecule/api-error-tracking@molecule/api-logger@molecule/api-middleware-body-parser@molecule/api-middleware-cookie-parser@molecule/api-middleware-cors@molecule/api-secretsexpress
