Skip to content

Latest commit

 

History

1,087 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@adaptivestone/framework

A TypeScript-first, ESM Node.js framework: convention-based controllers and Mongoose models, a tree-based router with per-controller generated route/handler types, and batteries-included auth, rate limiting, i18n, and caching.

📖 Full documentation → https://framework.adaptivestone.com/

🤖 LLM-ready docs (whole site as one file) → https://framework.adaptivestone.com/llm-context.md

Requirements

  • Node ≥ 24 (ESM-only, runs .ts sources natively) — or Bun ≥ 1.4.0, see Runtimes
  • MongoDB — required; boot fails fast without MONGO_DSN
  • AUTH_SALT — required; generate one with node src/cli.ts generateRandomBytes

Runtimes

Node is the primary runtime. Bun ≥ 1.4.0 is supported as an additional one: the same application code, the same Express adapter, running on Bun's Node compatibility layer — there is no Bun-specific build, no Bun.serve path, and no dependency pin or override needed. 1.4.0 is the floor because it is the first stable Bun that imports the Mongoose/BSON graph the framework resolves.

Certified on every release, on both the floor and the latest stable Bun: the framework test suite; and a consumer that installs the published tarball with bun install, imports the public entry points, boots a Server, serves a request, runs Mongoose create/read/update/delete against MongoDB and shuts down.

Node-only, as of Bun 1.4:

  • cluster.js / runCluster — the multi-process production entry point. It imports fine under Bun, but only the Node path is tested; run single-process under Bun (or put the process manager outside the app).
  • Node's test-runner CLI. Bun implements the node:test API but not node --test, so a Bun project runs its suite with bun test and the framework preloads rather than the flags the Node docs show. Four framework test files need mock.module() (Node's --experimental-test-module-mocks), which Bun does not implement (oven-sh/bun#5090), and are the only thing excluded from the Bun run: cluster.test.ts, models/UserOld.test.ts, helpers/redis/redisConnection.failure.test.ts, services/i18n/I18n.missing.test.ts. Everything else passes on both runtimes.
  • assert.deepStrictEqual against a Mongoose array. Bun 1.4 reports any Proxy — which is what a Mongoose array is — as unequal. Spread it first ([...doc.tags]). This is a test-assertion quirk; the values themselves are correct.

Quickstart

The fastest way to start is to clone the example project and use it as a template — it ships a working Server, controllers, config, tests, and a Docker dev stack (MongoDB + Redis included):

git clone https://github.com/adaptivestone/framework-example-project.git my-app
cd my-app
docker compose up

Your app starts at http://localhost:3300. Provide an AUTH_SALT before first boot (see Requirements and .env.example). Edit a controller under src/controllers/ and the dev server reloads automatically. Full walkthrough in the Getting Started guide.

To add the framework to an existing project instead:

npm install @adaptivestone/framework

Upgrade notes (5.5.0)

See the 5.5.0 release notes for everything new. Check these when you upgrade:

  • Logs are redacted by default: fields named authorization, cookie, password, secret or token are written as [REDACTED]. Your own redact list in config/log.ts replaces the default, so keep these names in it; redact: [] turns redaction off.
  • A few error responses changed. Field-error 400s always carry message and per-field arrays (safety-net errors.<field> used to be a string), every 500 has one text, the default 404 text is Not found, and the Role and RateLimiter responses carry error codes. Tests that compare whole bodies may need updating.
  • Errors thrown in middleware go through the error-handler registry: an HttpError from a middleware answers with its own status instead of a 500. The built-in Auth, Role and RateLimiter now throw instead of writing the response, so tests that call them directly must expect a thrown error.
  • Built-in health endpoints answer at /health/live and /health/ready. Your own controllers/Health.ts replaces them; GET /health itself stays free.
  • Counters of request-keyed rate limits (consumeKeyComponents.request) reset on deploy: their keys are now normalized and hashed.
  • New: npm run cli createEnv creates .env with a fresh AUTH_SALT on a fresh clone.
  • Deprecated, removed in v6: usedAuthParameters (now authSchemes), the positional body argument of HttpError, RateLimiter.gerenateConsumeKey, and notVerified in the unverified-login response.

Upgrade notes (5.4.2)

See the 5.4.2 release notes for all fixes. Most need no action:

  • Projects that run npm run gen must update the optional peer: npm i -D oxc-parser@^0.152.0.
  • user.generateToken() now rejects with a Mongoose VersionError when the stored password changed after the document was loaded; the built-in login answers this like a wrong password. Custom login flows should do the same.
  • The HTTP server opens its port only after routes, bootHttp and error handlers are mounted; startServer() resolves once it is listening.
  • createuser --token prints a usable session token; without the flag the command no longer creates a session.

Authentication and cache upgrade notes (5.4.1)

See the 5.4.1 release notes for all fixes, including model/validation typing corrections and the optional-i18next declaration change.

The built-in registration and password-reset routes accept passwords of 15–128 Unicode code points, including spaces. Existing passwords remain usable at login. To customize the length limits, extend the default in your application's src/config/auth.ts:

import auth from '@adaptivestone/framework/config/auth.js';

export default {
  ...auth,
  passwordPolicy: { minLength: 15, maxLength: 128 },
};

The policy applies to the built-in HTTP routes; direct model writes and custom password-change endpoints must enforce their own policy. Translate auth.passwordTooShort and auth.passwordTooLong to customize the validation messages (min and max are available as interpolation parameters).

With consumeKeyComponents.route enabled, rate limits use the registered route and method. Case variants, encoded spellings, trailing slashes and different parameter values share the same budget; implicit HEAD shares GET's budget. A limiter mounted before routing shares one bucket per method, without splitting by the requested URL. Upgrading resets existing rate-limit counters once; roll out to all workers promptly so older workers cannot keep separate alias buckets.

Cache keys now include a cache-v2 marker so strings and signed BigInts round-trip without collisions. The upgrade starts with a cold cache; older keys expire under their original TTLs. During mixed-version deployment, each version invalidates only its own key space, so complete the rollout before relying on cross-worker cache invalidation. Cache hit/miss logs no longer include keys or values.

Generated types

The framework generates genTypes.d.ts (typed getConfig/getModel) and per-controller *.routes.gen.ts files (typed handler signatures). Regenerate them with:

node src/cli.ts generatetypes

Code generation needs the optional peer dependency oxc-parser — install it as a devDependency in your project:

npm i -D oxc-parser

It parses your controller sources and is never loaded at runtime, so it stays out of production installs (npm ci --omit=dev).

They are gitignored — regenerate after pulling (a fresh checkout is red in the editor until the first run). In CI, guard against stale types with node src/cli.ts generatetypes --check, which writes nothing and exits non-zero if anything is out of date. The template wires this into its check:types script.

Starting with v5.2.0, a fully parenthesized controller-folder segment is an organizational route group: src/controllers/(group)/Reports.ts keeps the default /reports URL, while its generated file remains beside the controller. Ordinary folders still contribute their lowercased URL segments.

OpenAPI

Generate an OpenAPI 3.1 document from the same controller routes and request schemas used at runtime:

node src/cli.ts openapi --output openapi.json

Zod request schemas describe their input shape. An unsupported individual schema produces a contextual warning and safe fallback without removing healthy routes from the document. See the full OpenAPI guide.

Public API & stability

Only the subpaths listed under exports in package.json are importable as @adaptivestone/framework/<path>; internal modules are intentionally not exported. Exported paths follow semver in two tiers:

  • Tier 1 — stable: server.js, Cli.js, cluster.js, types.js, folderConfig.js, modules/*, models/*, controllers/*, tests/*, migrations/*.
  • Tier 2 — extension surface: config/*, helpers/*, and services/* — may change in a minor (with a deprecation cycle); pin to a minor if you import them directly. config/* is what you import to extend the framework's default config (e.g. import http from '@adaptivestone/framework/config/http.js', then re-export an edited copy from your own src/config/http.ts).

License

MIT

About

Modern node js web framework

Topics

Resources

Stars

4 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages