Skip to Content

CLI

Create a project, generate features, routes and services, and run, build and start your app.

@sukujs/cli gives you the suku command. It creates projects, generates code that follows the conventions in these docs, and wraps the commands to run, build and start an app.

bunx @sukujs/cli new my-api

Inside a project the CLI is a dev dependency, so suku is available to your scripts and through bunx suku.

suku dev, suku build and the generated tests use Bun, because Bun keeps the decorator metadata that constructor injection needs. The built app runs on Node or Bun.

Creating a project

suku new my-api

In a terminal, suku new asks for anything you did not pass as a flag:

QuestionChoicesDefaultFlag
Project nameanymy-apithe first argument
Production runtimeNode.js, BunNode.js--runtime node
Package managerbun, npm, pnpm, yarnbun--package-manager bun
Deployment targetsDocker, PM2, systemd, Cloudflare Workers, Vercel (any number)none--deploy docker,pm2
Initialise a git repository?yes, noyes--skip-git
Install dependencies?yes, noyes--skip-install

Move with the arrow keys, toggle a deployment target with space, confirm with enter. Pass --yes to ask nothing and take the defaults, which is what happens in CI, where there is no terminal.

suku new my-api --yes
suku new my-api --runtime bun --deploy docker,cloudflare --skip-git

What you get

my-api/
  src/
    main.ts              # creates the app, listens, shuts down on SIGTERM
    config.ts            # the environment, validated at startup
    app.feature.ts       # the root feature and a /health route
    app.test.ts          # tests that call the app in process
    hello/
      hello.feature.ts
      hello.routes.ts
      hello.service.ts
  package.json
  tsconfig.json

Deployment targets

Each target you pick adds its files and a section in the project's README. They follow the deployment guide.

TargetFilesScripts
dockerDockerfile, .dockerignore, docker-compose.yml
pm2ecosystem.config.cjs
systemddeploy/<name>.service
cloudflaresrc/worker.ts, wrangler.jsoncdev:worker, build:worker, deploy:cloudflare
vercelapi/index.ts, vercel.jsondev:vercel, deploy:vercel

Generating code

suku generate <schematic> <name>
suku g <schematic> <name>
SchematicAliasCreates
resourceresA CRUD feature: routes, service, schemas and a test
featuref<name>/<name>.feature.ts
routesr<name>/<name>.routes.ts, a @Routes class
services<name>/<name>.service.ts, a @Service class
middlewaremwmiddlewares/<name>.middleware.ts

Generated code is registered for you:

  • a feature or resource is added to imports in src/app.feature.ts, or to features in your entry file when there is no root feature
  • routes and a service are added to the feature file in the same folder
suku g resource products
CREATE src/products/products.schemas.ts (459 bytes)
CREATE src/products/products.service.ts (1020 bytes)
CREATE src/products/products.routes.ts (946 bytes)
CREATE src/products/products.feature.ts (290 bytes)
CREATE src/products/products.test.ts (1107 bytes)
UPDATE src/app.feature.ts (467 bytes)

That is a working GET, POST, PATCH and DELETE on /products with validated input, an in-memory service to replace with a repository, and a passing test.

Options

OptionEffect
--classlessroutes and service: generate route.group and service() instead of classes
--flatDo not create a folder named after the thing
--skip-importDo not edit other files to register it
--skip-testsresource: do not generate the test
-d, --dry-runPrint what would be written and write nothing
--forceOverwrite files that already exist

A name can be written any way (userProfiles, user_profiles, user-profiles); files are kebab-case and classes are PascalCase. A name with a folder, such as admin/reports, generates into src/admin/reports/.

The CLI never overwrites a file unless you pass --force. If it cannot find where to register something, it says so and leaves the file alone.

Running, building, starting

suku dev      # run src/main.ts with Bun, restart on change
suku build    # bundle the app and its dependencies into dist/
suku start    # run dist/main.js with the production runtime
OptionCommandsEffect
--entry <file>allEntry file. Default src/main.ts
--target <node|bun>buildWhat the bundle runs on. Default: the project's runtime
--outdir <dir>build, startOutput folder. Default dist
--minify, --sourcemapbuild
--external <package>buildLeave a package out of the bundle. Repeatable; needed for native modules
--runtime <node|bun>startOverride the project's runtime

Arguments after -- go to your app: suku dev -- --port 4000.

Project settings

The CLI reads an optional suku field in package.json.

package.json
{
  "suku": {
    "runtime": "node",
    "sourceRoot": "src",
    "entry": "src/main.ts"
  }
}

Reporting a problem

suku info prints the versions of the CLI, Node, Bun and every @sukujs/* package in the project. Include it in bug reports.

On this page