Skip to content
Rhea.js

Alpha 0.1.0-alpha. Not production-ready yet.

The secure, convention-driven backend framework for Node.js.

Build production-ready Express APIs with TypeScript, security-first defaults, powerful CLI tooling, and a predictable architecture.

npx create-rhea my-api

Published to npm as an alpha: the API can change between releases.

Pick a request. The responses below come from a real Rhea.js server running in production mode.

  1. Request ID
  2. Request logger
  3. Security headers
  4. CORS
  5. Rate limit
  6. Timeout
  7. Body size limit
  8. Pollution guard (request stopped here)
  9. Validation
  10. Your handler
  11. Error handler

Request

POST /users  {"a":{"__proto__":{"admin":true}}}

Response from the real server, 400

{
  "success": false,
  "error": {
    "code": "INVALID_BODY",
    "message": "Invalid request body",
    "requestId": "b6c66981-1c39-4378-86f3-36a2314afd5b"
  }
}

Bodies with __proto__, constructor or prototype keys are rejected before your code sees them.

Why Rhea.js

Express gives you the HTTP layer and leaves everything else to you. Most teams end up wiring the same things in every project: security headers, CORS rules, rate limiting, body limits, request IDs, logging that does not leak secrets, validation, an error handler that hides internals, and a folder layout nobody agreed on.

Rhea.js makes those decisions once and ships them as defaults. Express stays underneath, so what you know and what you have already written still works.

import { createApp, Router, sendSuccess } from "@rheajs/core";

const app = createApp({ cors: { origin: ["https://app.example.com"] } });

app.mount("/hello", Router().get("/", (_req, res) => sendSuccess(res, { hi: "rhea" })));

await app.start(5000);

That one call sets up everything in the pipeline above. Express can do all of it by hand. Rhea.js is about not having to.

What is included

Express 5 underneath
Your existing Express middleware and knowledge still apply. app.express is the real Express app.
One error model
Throw NotFoundError, ConflictError and friends from anywhere. Clients always get the same JSON shape and never an internal stack in production.
Validation with Zod
validate() checks body, query and params and reports every problem at once.
Environment that fails loudly
A missing or invalid variable stops startup with a list of what to fix.
Structured logs
JSON in production, readable in development, secrets redacted, one line per request with a request ID.
Graceful shutdown
SIGTERM and SIGINT stop accepting requests, finish in-flight ones, then run your shutdown hooks.
A CLI that writes the boring parts
Scaffold a project, generate a module with tests, build, and check your setup.
Narrow plugin API
Plugins get a small, stable surface instead of the whole framework.

Architecture

A project is organised by feature. Each module holds its controller, service, repository, routes, schema and types together, so code that changes together stays together.

my-api/
├── src/
│   ├── config/env.ts        validated environment
│   ├── modules/
│   │   ├── index.ts         module registry
│   │   ├── health/
│   │   └── users/           generated by: rhea generate module users
│   │       ├── users.controller.ts
│   │       ├── users.service.ts
│   │       ├── users.repository.ts
│   │       ├── users.routes.ts
│   │       ├── users.schema.ts
│   │       └── users.types.ts
│   ├── middleware/  core/  utils/  types/
│   ├── app.ts               buildApp()
│   └── server.ts
├── tests/{unit,integration,e2e}/
└── .env  .env.example  tsconfig*.json  vitest.config.ts

The app moves through a fixed lifecycle, and you can hook into any step:

  1. beforeInit
  2. init
  3. afterInit
  4. beforeStart
  5. afterStart
  6. beforeShutdown
  7. afterShutdown

Command line

rhea creates, runs, builds, tests and checks your project. Commands: create, dev, build, start, generate, test, doctor, security, docker, info.

npx create-rhea my-api
✓ Created my-api (22 files)

Next steps:
  cd my-api
  npm install
  npm run dev
npx rhea generate module users
✓ created src/modules/users/users.types.ts
✓ created src/modules/users/users.schema.ts
✓ created src/modules/users/users.repository.ts
✓ created src/modules/users/users.service.ts
✓ created src/modules/users/users.controller.ts
✓ created src/modules/users/users.routes.ts
✓ created tests/integration/users.test.ts
✓ registered in src/modules/index.ts
npx rhea build
Type checking + compiling (tsc, single pass, no output on type errors)...
✓ Built to dist/ in 3.3s
npx rhea doctor
Rhea.js Doctor

✓ Node.js 26.10.0
✓ npm 12.1.0
✓ ES modules
✓ TypeScript installed
✓ @rheajs/core (security middleware, error handling, rate limiting)
✓ Build configuration
✓ .env present
✓ .env.example present
✓ Rate limiting
✓ App built with createApp
✓ CORS configuration

0 errors, 0 warnings
npx rhea security
Rhea.js Security Scan

✓ No issues found by the static checks

Dependency audit skipped. Run with --audit (needs network).

This is a basic static check, not a penetration test or a security audit. A clean result does not guarantee security.

Captured from real runs on Node 26.10.0 (linux), @rheajs/core 0.1.0-alpha.0.

Security

Defaults aim to prevent common mistakes. They are not a guarantee, and Rhea.js has not had an independent security review.

On by default

  • Helmet security headers
  • CORS off until you list origins; wildcard refused in production
  • Rate limit, body size limit, request timeout
  • Prototype-pollution payloads rejected
  • No stack traces or internal messages in production
  • Secrets redacted from logs
  • Startup aborts on invalid configuration

Not included

  • Authentication and authorization
  • A shared rate-limit store: counters live in each process
  • Protection for routes where you skip validate()
  • An independent audit

rhea security runs static checks on your project. It is not a penetration test. Read the security guide.

Code examples

Validate input and throw typed errors:

import { Router, sendSuccess, validate, z, NotFoundError } from "@rheajs/core";

const createUser = z.object({ email: z.email() });

export const users = Router();

users.post("/", validate(createUser), (req, res) => sendSuccess(res, req.body, "Created", 201));

users.get("/:id", () => {
  throw new NotFoundError("User not found", { code: "USER_NOT_FOUND" });
});

Validate the environment so a bad deployment fails at startup:

import { baseEnvShape, loadEnv, z } from "@rheajs/core";

export const env = loadEnv({ ...baseEnvShape, DATABASE_URL: z.string().url() });

Plugins

A plugin receives a small context: a logger, addMiddleware, mount and onHook. It never touches framework internals, so the API can grow without breaking plugins. The plugin API is alpha and may change.

import { Router, type Plugin } from "@rheajs/core";

export const stats: Plugin = {
  name: "stats",
  setup(ctx) {
    let hits = 0;
    ctx.addMiddleware((_req, _res, next) => { hits++; next(); });
    ctx.mount("/stats", Router().get("/", (_req, res) => void res.json({ hits })));
  },
};

Testing

Generated projects use Vitest and Supertest. rhea generate module adds an integration test. The framework's own test suite includes an end-to-end run that scaffolds a project, installs it, builds it, starts it, checks real HTTP responses and confirms graceful shutdown.

const app = await buildApp().ready();
const res = await request(app.express).get("/health");
expect(res.status).toBe(200);

Production deployment

rhea docker writes a multi-stage Dockerfile that installs production dependencies only and runs as a non-root user. It was built and run once on Linux: the image served requests and exited cleanly on docker stop. Other platforms are untested. The deployment guide has the full checklist, including proxy settings that affect rate limiting.

Documentation

24 pages covering installation, every part of the framework, security, Docker and troubleshooting. Code examples in the docs are type-checked against the real build by the test suite. Read the docs.

GitHub, npm and community

The project is public. Everything below is real and links to where it lives.

Roadmap

Done
Core framework, CLI, project template, test suite with an end-to-end journey, documentation, this website
Next
Package publishing checks, repository and community files, first public alpha
After the alpha
Authentication plugin, database adapters (PostgreSQL, SQLite first), OpenAPI generation, queue and cache plugins, observability, OIDC
Before 1.0
Stable public API, mature docs, migration guides, independent security review, stable generated projects

No dates are promised. Version 1.0 waits until the items above are true.