Skip to content

Create

@modularityjs/create scaffolds a new application that consumes the framework. It writes the project files, wires a working boot script for the contracts you pick, runs pnpm install, generates an AGENTS.md, and initialises git — so the next command after scaffolding is pnpm dev.

Use this when you're starting a new app. To add a package inside the framework monorepo itself, see the /new-package skill.

Run

bash
pnpm create @modularityjs my-app

This downloads the scaffolder, runs it interactively, writes the project into ./my-app, installs dependencies, generates AGENTS.md, and initialises a git repository.

Non-interactive (defaults — Fastify + Zod + console logger, no cache / database / session):

bash
pnpm create @modularityjs my-app --yes

Presets

A preset pre-fills a coherent set of options; individual flags still win where they overlap.

PresetStack
apiFastify + Zod + console + middleware: cors, compression, security-headers + features: health
htmxFastify + Zod + console + middleware: compression, security-headers, formbody + features: health
workerNo HTTP + Zod + console + features: events, scheduler, health
bash
pnpm create @modularityjs my-app --preset=api --yes
pnpm create @modularityjs my-app --preset=worker --cache=redis --yes

Flags

pnpm create @modularityjs <name> [options]

Positional:
  <name>                  App name (lowercase, digits, hyphens).

Options:
  --yes, -y               Skip all prompts and use defaults.
  --force, -f             Overwrite a non-empty target directory.
  --no-install            Skip `pnpm install` after scaffolding.
  --no-git                Skip `git init`.
  --preset=<value>        api | htmx | worker — pre-fills a coherent stack.
  --http=<value>          fastify | none                                            (default fastify)
  --validation=<value>    zod | none                                                (default zod)
  --logger=<value>        console | none                                            (default console)
  --cache=<value>         memory | redis | none                                     (default none)
  --database=<value>      typeorm-sqlite | typeorm-postgres | prisma | none         (default none)
  --session=<value>       memory | redis | none                                     (default none)
  --auth=<value>          jwt | none                                                (default none)
  --authz=<value>         rbac | policy | none                                      (default none, requires --auth)
  --mfa=<list>            totp, sms, email, backup-codes, webauthn                  (requires --auth)
  --secrets=<value>       memory | vault | aws | gcp | none                         (default none)
  --encryption=<value>    aes | none                                                (default none)
  --mail=<value>          memory | nodemailer | none                                (default none)
  --sms=<value>           memory | twilio | none                                    (default none)
  --notification=<list>   mail, sms                                                 (each channel requires the matching contract)
  --queue=<value>         memory | redis | none                                     (default none)
  --webhook=<value>       memory | direct | none                                    (default none)
  --outbox=<value>        memory | typeorm | prisma | none                          (typeorm/prisma require --database)
  --storage=<value>       memory | local | s3 | none                                (default none)
  --template=<value>      handlebars | ejs | none                                   (default none)
  --assets=<value>        local | none                                              (default none)
  --media=<list>          image, video, audio
  --ws=<value>            fastify | none                                            (requires --http=fastify)
  --i18n=<value>          json | none                                               (default none)
  --feature-flags=<value> static | growthbook | none                                (default none)
  --rate-limit=<value>    memory | redis | none                                     (default none)
  --middleware=<list>     Comma-separated. cors, compression, security-headers, formbody, upload
  --features=<list>       Comma-separated. cli, events, scheduler, telemetry, health
                          (cli and telemetry auto-pull every applicable extension — see below)
  --dir=<path>            Target directory                                          (default ./<name>)
  --help, -h              Show help.
  --version, -v           Show version.

Any flag you pass pre-fills the matching prompt; anything you omit falls through to the interactive prompt. Combine with --yes to skip prompts entirely while still overriding individual defaults:

bash
pnpm create @modularityjs my-app --yes --database=prisma --cache=redis --features=events,health

Cross-cutting auto-wiring

Two features pull in every applicable extension for the rest of the plan so the scaffolded app is functional end-to-end, not just at the contract level.

Telemetry extensions

Adding telemetry to --features wires the OpenTelemetry tracer and every applicable *-telemetry extension so spans show up for every contract without further configuration:

Plan elementExtension added
--http=fastifyhttp-telemetry
--logger=consolelogger-telemetry
--cache=...cache-telemetry
--session=...session-telemetry
--features=eventsevents-telemetry
--features=schedulerscheduler-telemetry + lock-telemetry (scheduler pulls in lock)
--queue=...queue-telemetry
--rate-limit=...rate-limit-telemetry
--storage=...storage-telemetry
--mail=...mail-telemetry
--sms=...sms-telemetry
--notification=...notification-telemetry
--webhook=...webhook-telemetry
--outbox=...outbox-telemetry

The shared @modularityjs/plugins package is added once as the AOP host.

CLI extensions

Adding cli to --features wires @modularityjs/cli + @modularityjs/cli-commander and every applicable *-cli extension. The scaffolder emits a cli npm script alongside the HTTP entrypoint (or wires the CLI as the primary dev script when --http=none), so pnpm cli --help (or pnpm dev -- --help for CLI-only apps) lists the admin commands for every contract in your plan:

Plan elementCLI extension added
(always — base commands like modules, services)modularity-cli
--http=fastifyhttp-cli
--cache=...cache-cli
--database=...database-cli
--features=eventsevents-cli
--features=schedulerscheduler-cli + lock-cli
--features=healthhealth-cli
--auth=jwtauth-cli
--authz=...authz-cli
--queue=...queue-cli
--rate-limit=...rate-limit-cli
--storage=...storage-cli
--webhook=...webhook-cli
--assets=...assets-cli
--outbox=...outbox-cli
--ws=fastifyws-cli
--features=telemetry (only when telemetry extensions fire)plugins-cli

To opt out of any auto-added extension, delete the corresponding entry from src/bootstrap.ts after scaffolding (or src/main.ts if neither HTTP nor CLI is selected — that's the only plan that uses main.ts for the modules array). add is additive and won't re-introduce removed modules.

Cross-contract validation

Some flag combinations imply dependencies. The scaffolder fails early with a clear error rather than producing a broken project:

  • --authz=... requires --auth=....
  • --mfa=... requires --auth=....
  • --ws=fastify requires --http=fastify.
  • --outbox=typeorm requires --database=typeorm-sqlite or --database=typeorm-postgres.
  • --outbox=prisma requires --database=prisma.
  • --notification=mail requires --mail=...; --notification=sms requires --sms=....

When --features=cli is on, auth-cli, authz-cli, queue-cli, storage-cli and every other applicable *-cli extension auto-attaches. Same for --features=telemetry and the corresponding *-telemetry extensions.

Auth (JWT)

--auth=jwt wires AuthModule + AuthJwtModule.forRoot({ secret }), and — when --http=fastify is also on — the HTTP extensions HttpAuthModule + HttpAuthJwtModule so handlers can read request.identity and use @Authenticated().

The HS256 signing secret (32 random bytes, base64url) is generated at scaffold time and stored in three places:

  • .env.example as JWT_SECRET=... — the canonical override.
  • .modularityjs/scaffold.json — so add re-runs against the same secret deterministically.
  • A fallback literal inside the AuthJwtModule.forRoot({...}) call rendered into src/bootstrap.ts, so the app boots without .env set up.

Production deployments must override JWT_SECRET in the environment. Treat the generated secret as a dev placeholder, not a real secret.

Running add --auth=jwt on a project that already has auth is a no-op for the secret (the existing value is kept). Disabling and re-enabling generates a fresh secret.

What you get

The scaffolder writes a small, opinionated project structure. Some files are always emitted; others depend on the plan.

Always emitted:

  • package.json with @modularityjs/modularity, @modularityjs/di, @modularityjs/di-inversify, plus the contract + driver pairs you selected, and dev/build/start/test scripts.
  • tsconfig.json, tsconfig.build.json, and eslint.config.mjs matching the framework's coding standard.
  • src/bootstrap.ts — exports an async function bootstrap() that returns createApp({ di: inversify, modules: [...] }) with the modules for your selected contracts already wired. The entrypoint files below import it and pick the right app.start(...) call.
  • README.md, .gitignore, .npmrc (sets engine-strict=true plus the private-registry mapping; the pnpm version itself is pinned via packageManager: 'pnpm@…' in package.json), .prettierrc, .prettierignore, .editorconfig, commitlint.config.mjs, .husky/pre-commit, .husky/commit-msg.
  • .github/workflows/ci.yml — runs pnpm install --frozen-lockfile, pnpm format:check, pnpm build, pnpm typecheck, pnpm lint, pnpm test on push and PR.
  • AGENTS.md, generated automatically after pnpm install so coding agents pick up the framework's conventions immediately.
  • .modularityjs/scaffold.json — the persisted plan that powers the add subcommand.

Entrypoint (exactly one is chosen based on the plan):

  • src/server.ts — when --http=fastify; calls app.start({ http: ... }).
  • src/cli.ts — when --features=cli; dispatches to the CLI runner. Emitted alongside server.ts if both are on.
  • src/main.ts — only when neither HTTP nor CLI is selected (e.g. a pure worker plan).

When --http=fastify:

  • src/app.module.ts, src/hello.controller.ts, src/hello.service.ts — a seeded /hello/:name route.
  • src/__tests__/hello.service.spec.ts — a passing createTestHarness example.

When any driver reads runtime env vars (Redis, Postgres, JWT secret, …):

  • .env.example plus --env-file-if-exists=.env in the dev / start scripts.

The output compiles and runs out of the box — pnpm dev starts the app immediately.

add subcommand

Run from inside a scaffolded project to bolt on another contract or middleware without re-running the full scaffolder:

bash
cd my-app
pnpm create @modularityjs add --cache=redis

By default this writes a preview to .modularityjs/preview/ so you can diff against your current code and pick what to apply:

bash
diff -ru . .modularityjs/preview/ | less

Pass --apply --force to overwrite the regenerable plan files — package.json, src/bootstrap.ts, src/main.ts, src/server.ts, src/cli.ts, .env.example, and .modularityjs/scaffold.json — in place. Orphan entrypoints (e.g. an src/cli.ts left behind after dropping --features=cli) are removed too. Handcrafted files like app.module.ts and controllers are never touched:

bash
pnpm create @modularityjs add --features=events,scheduler --apply --force

Single-driver flags replace the existing selection — add accepts every contract in the registry (--cache, --session, --authz, --encryption, --queue, --rate-limit, --storage, --mail, --sms, --webhook, --secrets, --template, --i18n, --feature-flags, --assets, --ws) plus the special-shape flags --auth, --database, --outbox. --http, --validation, --logger are scaffold-only flags — add doesn't take them. Multi-select lists (--middleware, --features, --media, --mfa, --notification) are unioned with what's already in the plan. Run pnpm create @modularityjs add --help for the full list. After applying, re-run pnpm install and optionally regenerate AGENTS.md.

After scaffolding

The scaffolder prints these next steps on completion:

  1. cd my-app
  2. pnpm dev — runs the boot script in watch mode (loads .env automatically when present).
  3. curl http://localhost:3000/hello/world to hit the seeded route.
  4. Read Getting Started for the mental model behind createApp, modules, and the contract/driver split.