Skip to main content
Version: 4.0.0-preview

Running the App

Four CLI commands cover the full local lifecycle: write code with dev, compile with build, run the compiled output with prod, and (optionally) do the whole thing inside Docker with container-dev.

CommandWhat it does
devHot-reload dev server via tsx --watch
buildtsc -p tsconfig.build.json; rewrites alias imports in opinionated projects
prodnode against the compiled JavaScript
container-devDocker-compose-based development workflow (start, stop, attach, shell, status, logs)

All four read expressots.config.ts from the project root and the compile path from tsconfig.build.json.

Prerequisites

FilePurpose
expressots.config.tsProvides entryPoint and opinionated flags.
tsconfig.build.jsonMust define compilerOptions.outDir. Used by build (output) and prod (input).

dev

Hot-reload local dev server.

expressots dev

Under the hood it shells out to:

npx tsx --watch [-r tsconfig-paths/register] ./src/<entryPoint>.ts
  • tsx --watch is tsx's built-in watch mode with no nodemon, no SIGTERM quirks on Windows.
  • tsconfig-paths/register is added for opinionated projects so @useCases/..., @providers/... etc. resolve at dev time.
  • The process inherits stdio; press Ctrl+C to stop. Saved files trigger a fast restart.

Options

FlagAliasDefaultDescription
--container-cfalseRun dev inside Docker. Generates Dockerfiles + compose if missing.
--build-bfalse(With --container) Rebuild the dev container before starting.
--detach-dfalse(With --container) Run the dev container in the background.
expressots dev --container --build --detach

When --container is set, the CLI invokes Compose v2 (docker compose) when available and falls back to Compose v1 (docker-compose). Both Dockerfile.development and docker-compose.development.yml are generated on demand.

build

Compile to JavaScript.

expressots build

Under the hood:

npx tsc -p tsconfig.build.json

Then, for opinionated projects only, the CLI post-processes every emitted .js file:

  • Replaces require("@alias/<path>") imports with relative paths so the compiled bundle runs without tsconfig-paths/register.
  • Copies package.json into outDir so production tooling (npm prune --production, npm start, etc.) can resolve dependencies.
Why the rewrite

v3 required register-path.js at runtime. v4 removed that. Alias resolution is baked into build time, so production startup has zero path-mapping cost.

prod

Run the compiled output with plain Node.

expressots prod

The exact invocation depends on whether the project is opinionated:

opinionatedCommand
truenode ./${outDir}/src/${entryPoint}.js
falsenode ./${outDir}/${entryPoint}.js

In either case the alias rewrite has already happened during build, so production startup is just node <file>.

container-dev

Compose-driven development workflow. Use this when your dev story requires sidecar services (Postgres, Redis, RabbitMQ) and you want them all running on docker compose up.

ActionPurpose
start(default) Boot the dev compose file in the foreground.
stopStop the dev compose stack.
attachAttach to the app container's stdout.
shellOpen an interactive shell inside the app container.
statusShow running container state for the compose project.
logsTail the app container's logs.
expressots container-dev start --build
expressots container-dev logs --follow --tail 200
expressots container-dev shell
expressots container-dev stop

Options

FlagAliasDefaultDescription
--container-cfalseForce the container path. Without this, start only prints guidance.
--service-sappService name from the compose file.
--compose-file-fdocker-compose.development.ymlCompose file to use.
--build-bfalseRebuild images on start.
--detach-dfalseRun start in the background.
--port-p(compose-driven)Override the host port for start.
--debug-port9229Expose the Node inspector on this port.
--watch-wtrueMount source for live edits.
--followfalselogs follow mode.
--tail100logs tail line count.
dev or container-dev?
  • expressots dev is faster. tsx --watch reload is sub-second and there is no Docker layer.
  • expressots dev --container is a one-shot containerized dev.
  • expressots container-dev is the structured Compose workflow when you need multiple services and a long-lived dev environment.

Common scripts

expressots new emits these scripts in package.json:

{
"scripts": {
"dev": "expressots dev",
"build": "expressots build",
"prod": "expressots prod"
}
}

So you can call them through your package manager too:

npm run dev
npm run build
npm run prod

Troubleshooting

ProblemFix
tsconfig.build.json: outDir missingAdd "outDir": "build" (or any folder name) under compilerOptions.
Cannot find module '@useCases/...' in productionYou ran node directly. Use expressots prod so the alias rewrite is applied, or rebuild first.
Port already in use (EADDRINUSE)Stop the previous process or change the port via env / bootstrap({ port }).
--container first run is very slowExpected. It generates Dockerfile.development, builds the image, and pulls base layers. Subsequent runs are cached.