Getting Started

Quick Start

Scaffold a runnable NextRush project in one command — npm create nextrush, cd, npm run dev. Pick a style, a middleware preset, and a runtime, and skip the manual setup from Installation.

Quick Start · Scaffold

Go from zero to a running API in under 2 minutes.

One command. No manual setup — tsconfig.json, scripts, a sample route, and a real unit test are all generated.

Generated

Running

Tested

~2 minNode 22+ to run CLIInteractive or flags

Wiring a project by hand teaches how the pieces fit — that's what Installation and the Task API tutorial are for. Once you've done that once, use this page to skip straight to running: it picks a style, a middleware preset, and a runtime for you.

Outcome

What you'll build

A new folder that runs with pnpm dev and ships a real unit test — everything wired for you:

📁 ProjectReady-to-runpackage.json, tsconfig, src/, .gitignore
⚡ ServerLive dev serverWelcome route + /health (or /api/health)
✅ TestsPassingvitest run — at least one real unit test

Setup · 3 steps

Create the project

The scaffolder is the npm package create-nextrush. Your package manager can invoke it several ways:

$ pnpm create nextrush@latest my-api

my-api is the target directory — pass . to scaffold into the current folder, or omit it to be prompted. The flow is built on @clack/prompts.

Install dependencies

The scaffolder installs for you by default — this step is usually a no-op. Only if you skipped install, or want to re-run it:

cd my-api
pnpm install

Run it

cd my-api
pnpm dev     # hot-reload dev server

🎉 You're ready — verify it works

Your app should be live on http://localhost:8080. Confirm it:

curl http://localhost:8080/
# → {"message":"Welcome to NextRush!"}

curl http://localhost:8080/health
# → {"status":"ok","timestamp":"…","uptime":…}

Expected: a Welcome to NextRush! JSON body on / and an ok status on /health. Class-based projects answer on /api/health instead of /health.

Your NextRush app is running. Everything is working correctly.

Recommended next

Build your first API

Further Reading

Your app is running. Want to understand what's happening under the hood? These explain the scaffold, project structure, CLI flags, and common pitfalls.

Six prompts, every one with a default — press Enter to accept.

1

Project name

Where to create the project. Defaults to my-nextrush-app if you didn't pass a directory. The scaffolder derives package.json's name from this path — invalid characters become hyphens, so My API! becomes my-api.

2

Style

Three choices. Functional is the default — the smallest surface.

Functional (default)

Routes only, no decorators. Smallest surface — best for small APIs and microservices.

Class-based

Controllers, DI, and decorators. Routes register under /api. Best when you want structure from day one.

Full

Both styles side by side. A reference layout showing functional routes and class-based controllers together.

Full comparison with generated trees and code: Project structure.

3

Runtime

Node.js (default)

No adapter needed — nextrush re-exports @nextrush/adapter-node.

Bun

Adds @nextrush/adapter-bun and swaps the generated import and scripts.

Deno

Adds @nextrush/adapter-deno and swaps the generated import and scripts.

Edge and serverless aren't scaffolder targets — Edge and Serverless are paths you follow manually once a project exists.

4

Middleware preset

Minimal

No middleware — core only.

API (default)

cors, body-parser, helmet. Registered in production-safe order.

Full

API preset + rate-limit, compression, request-id.

The scaffolder writes the matching import lines and app.use(...) calls into src/index.ts.

5

Install dependencies?

Defaults to yes. Say no to scaffold files only and run pnpm install yourself afterward.

6

Initialize git?

Defaults to yesgit init plus an initial commit. Say no to skip.

Every prompt has a matching flag. A fully-specified command never pauses:

pnpm create nextrush@latest my-api --style functional --runtime node --middleware api
pnpm create nextrush@latest my-bun-api --style functional --runtime bun --middleware full
pnpm create nextrush@latest my-deno-api --style class-based --runtime deno --middleware api

-y / --yes accepts every default without specifying each option:

pnpm create nextrush@latest my-api --yes

Scaffold without install or git:

pnpm create nextrush@latest my-api --yes --no-install --no-git

create-nextrush flags

PropertyTypeDescription
--style, -s"functional" | "class-based" | "full"= "functional"Project style
--runtime, -r"node" | "bun" | "deno"= "node"Target runtime for the generated project
--middleware, -m"minimal" | "api" | "full"= "api"Middleware preset
--pm"npm" | "pnpm" | "yarn" | "bun"= auto-detected from how you invoked the scaffolder; "bun" if --runtime bunPackage manager for install and generated scripts
--install, -iboolean= trueInstall dependencies
--no-installbooleanSkip dependency installation
--gitboolean= trueInitialize a git repository
--no-gitbooleanSkip git initialization
-y, --yesboolean= falseAccept every default, skipping all interactive prompts
-v, --versionbooleanPrint the installed create-nextrush version
-h, --helpbooleanPrint flag usage

The project name comes from the directory you pass — invalid characters become hyphens, so My API! becomes my-api. Passing . names the project after the current folder.

Every style shares tsconfig.json, package.json, README.md, .gitignore, and src/env.d.ts for editor hints — then adds its own source layout. Each ships at least one real vitest unit test, not a placeholder.

Best for: Small to mid-size APIs, microservices, minimal surface area — without sacrificing a professional layered structure.

my-api
src
config
index.ts
lib
types.ts
middleware
logger.ts
repositories
__tests__
todos.repository.test.ts
todos.repository.ts
routes
health.routes.ts
todos.routes.ts
services
__tests__
health.service.test.ts
todos.service.test.ts
health.service.ts
todos.service.ts
index.ts
.gitignore
package.json
tsconfig.json

A production-grade layered API: routes → services → repositories, with centralized config, shared domain types, and custom middleware. No classes, decorators, or DI — pure factory functions throughout.

The entrypoint uses the framework's built-in errorHandler from nextrush (first in the middleware chain). Services throw NotFoundError/BadRequestError from nextrush; routes stay free of try/catch:

src/index.ts (generated, api middleware)
import { createApp, createRouter, errorHandler, listen } from 'nextrush';
import { cors } from '@nextrush/cors';
import { json } from '@nextrush/body-parser';
import { helmet } from '@nextrush/helmet';
import { config } from './config/index.js';
import { logger } from './middleware/logger.js';
import { healthRouter } from './routes/health.routes.js';

const router = createRouter();
const app = createApp({ router });

// Error handling (first middleware — catches all downstream errors)
app.use(errorHandler({ includeStack: config.nodeEnv !== 'production' }));

// Request logging
app.use(logger());

// Middleware
app.use(cors());
app.use(helmet());
app.use(json());

// Routes
router.get('/', (ctx) => {
  ctx.json({ message: 'Welcome to NextRush!' });
});

app.route('/health', healthRouter);

await listen(app, config.port);
src/services/todos.service.ts (generated)
import { BadRequestError, NotFoundError } from 'nextrush';
import type { TodoRepository } from '../repositories/todos.repository.js';

export function createTodoService(repository: TodoRepository) {
  return {
    get(id: string) {
      const todo = repository.findById(id);
      if (!todo) throw new NotFoundError('Todo not found');
      return todo;
    },
    // ...create, list, remove
  };
}

Health lives at GET /health. On Bun or Deno, listen comes from @nextrush/adapter-bun or @nextrush/adapter-deno instead. Deno reads Deno.env.get('PORT') in config/index.ts.

Best for: DI, decorators, structured architecture from day one.

my-api
src
modules
health
health.controller.ts
health.module.ts
health.service.ts
todos
__tests__
todos.controller.test.ts
todos.service.test.ts
todos.controller.ts
todos.module.ts
todos.repository.ts
todos.service.ts
app.module.ts
index.ts
.gitignore
package.json
tsconfig.json

The root AppModule composes feature modules via @Module({ imports }), and the entry file wires the whole graph in one call:

src/index.ts (generated excerpt)
import { createApp, createRouter, listen } from 'nextrush';
import { registerModule } from 'nextrush/class';
import { AppModule } from './app.module.js';

const router = createRouter();
const app = createApp({ router });

// Wire the root module — registers the whole module graph in one call
await registerModule(app, AppModule, { prefix: '/api' });

await listen(app, PORT);
src/modules/health/health.controller.ts (generated)
import { Controller, Get } from 'nextrush/class';
import { HealthService } from './health.service.js';

@Controller('/health')
export class HealthController {
  constructor(private readonly health: HealthService) {}

  @Get()
  check() {
    return this.health.getHealth();
  }
}

Health lives at GET /api/health — the /api prefix comes from registerModule, not the controller decorator alone.

Choosing class-based enables experimentalDecorators and emitDecoratorMetadata in tsconfig.json, and adds reflect-metadata plus @nextrush/class as explicit dependencies — same requirement as Installation's TypeScript step.

Best for: Both routing styles in one service. A reference layout.

my-api
src
middleware
error-handler.ts
modules
hello
__tests__
hello.service.test.ts
hello.controller.ts
hello.module.ts
hello.service.ts
routes
health.ts
app.module.ts
index.ts
.gitignore
package.json
tsconfig.json

full combines a functional /health route with a class-based module graph (@Module/AppModule via registerModule, the same standard as the class-based style) and error-handler.ts registered first in the middleware chain. Controllers mount under the /api prefix through the root module.

Useful as a reference when you want both routing styles in one service — not the smallest starting point.

Generated scripts reference

package.json scripts differ by runtime. Every style also includes "test": "vitest run".

Runtimedevbuildstarttest
nodenextrush devnextrush buildnode dist/index.jsvitest run
bunbun nextrush devbun nextrush buildbun dist/index.jsvitest run
denodeno run … npm:nextrush devdeno run … npm:nextrush builddeno run --allow-net --allow-read --allow-env dist/index.jsvitest run

Deno's dev/build scripts route through nextrush dev and nextrush build (not a raw deno run on the entry file) so decorator metadata stays consistent with Node and Bun. The --allow-* flags are Deno's permission model — see Deno onboarding.

Wrong package name with dlx/npx
Symptom:
Package not found when running pnpm dlx create nextrush
Cause:
Direct invocations need the hyphenated npm name, not the create-space form

✅ Fix: Use pnpm dlx create-nextrush or npx create-nextrush@latest

Old scaffold despite publishing a new version
Symptom:
The generated project has a flat src/routes/ layout with no services/, repositories/, or config/ — even though the scaffolder was just updated
Cause:
pnpm 11.x (and Deno's npm: compatibility) can resolve @latest to an old cached version for packages with a version gap — a known resolution bug (pnpm#8659), not a problem with create-nextrush

✅ Fix: Use npm create nextrush or bun create nextrush, which resolve @latest correctly — or use pnpm@10

Looking for /health on a class-based project
Symptom:
404 on GET /health after scaffolding class-based or full
Cause:
Controllers register under the /api prefix

✅ Fix: Try GET /api/health — or scaffold functional if you want /health at the root

Git or install step failed
Symptom:
CLI prints failed - see the error above but files exist on disk
Cause:
Missing git on PATH, network blocked during install, or permission error

✅ Fix: Files are already written — retry git init or pnpm install manually

Scaffolding into a non-empty directory
Symptom:
CLI asks for confirmation before overwriting
Cause:
Target folder already has files

✅ Fix: Confirm only if you mean to merge, or pass a fresh directory name

Use npm create nextrush or bun create nextrushpnpm create nextrush (bare) and deno run -A npm:create-nextrush@latest can resolve @latest to an old cached version on pnpm 11.x and Deno (a known resolution bug, pnpm#8659). Direct invocations (npx, pnpm dlx, bunx) need the hyphenated package name: create-nextrush, not create nextrush.

What's next

Continue learning

You have a running app — now make it yours. Each of these picks up exactly where the scaffolder left off:

Was this helpful?

On this page