Getting Started
Install, scaffold, generate & mount the plugin
Nucleus is one config object turned into a running backend. The loop is: declare config.nucleus.json, run the generator to produce the Drizzle schema, and mount NucleusElysiaPlugin in an Elysia app. The CLI can also scaffold an entire project — API, frontend, Kubernetes manifests and CI pipelines — so you start from a working system, not a blank file.
This page is the orientation: the fastest path from nothing to a booting backend, the two top-level primitives every config carries (appId, mode), and how the plugin is wired. The full command set lives on the CLI Reference page; every feature block referenced here has its own deep page in the sidebar.
The CLI#
nucleus-core-ts ships one dependency-free CLI (exposed as nucleus, nucleus-core-ts and nucleus-generate). The fastest start is init → dev: init scaffolds a runnable backend from a preset, dev up brings up Postgres + Redis, and dev runs it on port 4000. add extends the project later; generate and scaffold are still here. The CLI Reference page documents every command and flag — this is just the orientation.
1# scaffold a runnable backend from a preset, then run it2bunx nucleus-core-ts init --preset saas --app-id shop -o ./shop3cd ./shop && nucleus dev up && nucleus dev # -> http://localhost:4000/docs4 5# regenerate the Drizzle schema after editing config.json (add entity does this for you)6nucleus generate src/config.json src/drizzleinitpreset → runnable backendOptionalBuilds a config (preset, existing file, or interactive), validates it, writes a complete backend and — unless --no-install — installs deps and generates the schema. Presets: minimal, api, saas, marketplace. The lean local-dev path.
devup | down | runOptionalThe local run loop. dev up ensures Postgres + Redis (starting throwaway Docker containers only for whatever isn't already running); dev runs the app; dev down tears those containers back down.
addentity | feature | oauthOptionalExtend an existing project's config in place (schema-validated), regenerating the Drizzle schema for entity changes. Any new secrets are written as env-var names to fill in .env.
generateconfig → DrizzleOptionalReads your config.json and emits the Drizzle schema + relations the plugin loads, merging your entities with the built-in system tables (users, user_sessions, roles, claims, audit_logs, …). (Alias: gen; a path argument is auto-detected as generate.)
scaffoldfull projectOptionalInteractive full-project scaffolding — Elysia API, frontend, Kubernetes manifests and CI/CD pipelines — so a new service is deployable from day one. Distinct from init (which produces just a lean backend). (Alias: new.)
validate / doctorchecksOptionalvalidate checks a config against the JSON-Schema + cross-field rules (CI-friendly, exit 1 on error); doctor checks the local machine (bun, Postgres, Redis, config validity). Both are read-only.
The two primitives — appId & mode#
Every config has two scalar fields at its root, before any feature block. They get no page of their own because a sentence covers each.
appIdstringRequiredA unique, stable identifier for this service. Nucleus uses it to brand transactional emails, tag logs / monitoring / audit data, and namespace the monitoring + config-override Redis keys. Note appId itself does NOT prefix the core session (`session:<id>`) or rate-limit (`rl:`) Redis keys. For full cross-service isolation on a single Redis DB, set config.redis.keyPrefix (a literal or an env-var NAME): it transparently namespaces EVERY Redis key — session: and rl: included — via ioredis's native keyPrefix in direct mode and an equivalent manual prefix in Dapr mode, so you don't need separate Redis databases (see Database → Redis). Choose appId once; don't change it in production.
mode'development' | 'production'OptionalWhen unset the framework treats the service as production. development flips the logger defaults — level debug with pretty-printed, colorized, caller-tagged output; production (or unset) uses info-level compact logs. mode does not affect cookie security: auth cookies are marked Secure only when a non-localhost authentication.cookieDomain is configured; with no cookieDomain set (the default, typical for local dev) the Secure flag is omitted. Set cookieDomain in production to get Secure cookies.
"production"Mounting the plugin#
The generated schema and relations are handed to NucleusElysiaPlugin alongside the path to your config. That single plugin mounts every enabled feature's routes.
1import { Elysia } from "elysia";2import { NucleusElysiaPlugin } from "nucleus-core-ts";3 4const app = new Elysia()5 .use(6 await NucleusElysiaPlugin({7 options: "./src/config.json", // path to config.nucleus.json8 schema: "./src/drizzle/schema.ts", // path to the generated Drizzle schema9 relations: "./src/drizzle/relations.ts", // path to the generated relations10 swagger: { path: "/docs" }, // OpenAPI docs — on by default; { enabled: false } turns them off11 }),12 )13 .listen(9000);optionsNucleusConfigOptions | string (path)OptionalEither the config object inline or a path to your config.nucleus.json — a string is read and JSON-parsed at boot, anything else is used directly as the config object. The path form is the shippable-across-environments happy path: secrets inside the file are env-var NAMES, resolved from process.env at boot, so the same file ships across environments.
schema / relationsstring (paths)OptionalFilesystem paths to the Drizzle schema and relations files produced by generate (resolved from process.cwd() and required at boot). The plugin pushes the schema to Postgres on boot and uses relations for the Query API's with expansion.
swaggerNucleusSwaggerConfigOptionalOpenAPI docs config — enabled by default (pass { enabled: false } to disable). Set path (default /swagger), provider ('scalar' | 'swagger-ui'), documentation, exclude, and more. Builds the page from the TypeBox schemas every entity and route generates, so your live API is self-documenting.
staticAssetsstring | falseOptionalDirectory of static files, served under the /nucleus-core URL prefix (not the site root) — so a file resolves at /nucleus-core/<file>, not /<file>. By default Nucleus serves its own bundled package assets (resolved from the nucleus-core-ts / nucleus-core install), falling back to ${process.cwd()}/public only when that package folder isn't found. Pass a string to serve your own directory instead (still mounted under /nucleus-core), or false to disable static serving entirely.
systemTablesNucleusTable[]OptionalExtra system/built-in tables to register at mount, beyond the bundled set. They are merged in alongside your generated entities (into the same allEntities set) so their routes, public-route rules and schema availability are wired the same way. Optional; omit it for the standard bundled tables.
Related sections