Build with clarity.
A lightweight modular backend engine and CLI toolkit for Express + TypeScript.
ExetJS gives your backend a clear starting point: a small runtime, explicit modules, typed configuration, and a CLI that handles the repetitive work. Express remains visible, so the application stays yours.
No decorators, reflection metadata, heavy dependency injection container, or automatic module discovery. Modules are registered explicitly.
Installation #
You need Node.js 24 LTS or newer and a package manager. Install the CLI globally using pnpm or npm.
pnpm add -g @exetjs/clinpm install -g @exetjs/cliVerify that the executable is available:
exet --versionFor manual integration in an existing ESM project, install @exetjs/core directly. For a new application, the CLI is the recommended starting point.
Quick start #
Create a project, enter its directory, and start development. The setup wizard asks for your package manager, server host, port, API prefix, and CORS origin.
exet init my-api
cd my-api
exet startThe CLI prints the resolved server address and health URL. Open the health URL or request it from your terminal. With port 5000 and the default API prefix:
curl http://localhost:5000/api/v1/healthPort 5000 is an example. Use the host and port you selected during setup, or the health URL printed by the CLI.
CLI commands #
A focused command set for the everyday development loop. Run project commands from the directory containing exet.config.ts.
exet init [projectName]
Create an Express + TypeScript project with an interactive setup.
exet init my-api --no-installexet start
Validate configuration and run the project using its local tsx installation.
exet start --no-watchexet install <moduleName>
Add a module template, update the registry and install its dependencies.
exet install auth --dry-runexet list
List the module templates available in the installed CLI version.
exet listexet doctor
Check project paths, runtime configuration, registry markers and health registration.
exet doctorexet install also supports --no-install to skip dependency installation and --force to allow overwriting template files. Review a dry run before replacing existing work.
Project structure #
The generated project separates runtime setup from feature modules. Routes and handlers live with their module; shared utilities have a dedicated home.
my-api/
├── src/
│ ├── app.ts # Application bootstrap
│ ├── main.ts # Runtime entry point
│ ├── config/ # Environment and app config
│ ├── middlewares/ # Logging and error handling
│ ├── modules/
│ │ ├── index.ts # Explicit module registry
│ │ └── health/ # Built-in health module
│ └── shared/ # Reusable utilities
├── .env
├── .env.example
├── exet.config.ts
├── package.json
└── tsconfig.jsonsrc/app.ts creates the application, registers the modules, and starts the server. src/modules/index.ts is the explicit module registry. Keep the registry marker comments so the CLI can safely add new modules.
Runtime configuration #
Project defaults live in exet.config.ts. An environment file and process environment can override runtime values. The order is explicit:
HOST=0.0.0.0
PORT=5000
API_PREFIX=/api/v1
APP_NAME=my-api
NODE_ENV=development
CORS_ENABLED=true
CORS_ORIGIN=http://localhost:5173
LOG_LEVEL=info| Variable | Purpose |
|---|---|
HOST | Network interface to bind to. Use 0.0.0.0 for a container or remote host. |
PORT | HTTP listening port for the application. |
API_PREFIX | Base path for registered API routes, usually /api/v1. |
APP_NAME | Application name used by the runtime. |
NODE_ENV | Runtime environment, such as development or production. |
CORS_ENABLED | Whether CORS handling is enabled. |
CORS_ORIGIN | Allowed origin for cross-origin requests. |
LOG_LEVEL | Logging verbosity, for example info or debug. |
The environment file path comes from paths.envFile. Set EXET_ENV_FILE to override that path for a specific process. New projects use HOST and PORT; the older APP_HOST and APP_PORT aliases are deprecated.
Modules #
See the module catalog for available modules and the Core module contract for the complete API.
A module declares its name, base path, and registration function. It returns an Express router, keeping ordinary Express routes and middleware available.
import { Router } from 'express';
import type { ExetModule } from '@exetjs/core';
export const helloModule: ExetModule = {
name: 'hello',
basePath: '/hello',
register: () => {
const router = Router();
router.get('/', (_req, res) => {
res.json({ message: 'Hello, ExetJS.' });
});
return { router };
},
};Add the module to the exported registry alongside the built-in health module:
import type { ExetModule } from '@exetjs/core';
import { healthModule } from './health/health.module.js';
import { helloModule } from './hello/hello.module.js';
// exet:imports:start
// exet:imports:end
export const modules: ExetModule[] = [
healthModule,
helloModule,
// exet:modules:start
// exet:modules:end
];With the default prefix, the route is available at /api/v1/hello. To explore the templates included with your installed CLI, run exet list.
Version 0.1.0 includes an authentication skeleton with in-memory persistence. Replace its repository and configure a strong JWT_SECRET before production use.
Health check #
Generated projects include a health module at the default endpoint:
/api/v1/healthapplication/jsoncurl http://localhost:5000/api/v1/healthThe handler returns HTTP 200 with an API response containing status: "ok", engine: "ExetJS", and a timestamp. Its path follows your configured API prefix.
Use it to check that the application is reachable. Add checks for your database and other dependencies if your deployment needs readiness checks beyond the built-in endpoint.
Deployment #
Deploy the generated backend to an environment that supports Node.js 24 or newer. Type-check and build the project, then run the compiled entry point using the production script.
pnpm check
pnpm build
pnpm start:prodSet NODE_ENV=production and provide the runtime environment values through your host. Bind to HOST=0.0.0.0 where required, configure the assigned port, and set the intended CORS origin.
The production script runs node dist/main.js. Keep required runtime dependencies available and use your host's process supervision. The development command exet start is intended for the local workflow.
Frequently asked questions #
Does ExetJS replace Express?
No. ExetJS is built around Express. Modules return Express routers, and your application can use familiar routes and middleware.
Is a database included?
The core does not select a database for you. Add the persistence layer your application needs. The current auth template uses an in-memory repository that you should replace for production.
Can I use npm instead of pnpm?
Yes. The project wizard supports pnpm, npm, and Yarn. Use the package manager selected during setup for subsequent project commands.
How does ExetJS relate to NurJS?
ExetJS continues the project previously published as nurjs-core and nurjs-cli. New projects should use @exetjs/core and @exetjs/cli. The executable is now exet.
Where can I find the package source of truth?
The published core package and CLI package contain their READMEs and version-specific implementation. This guide follows version 0.1.0.