Skip to main content
All Projects
West JS Case Study
Open SourceCLI Tool / Developer ToolingActive

Open Source CLI

West JS Case Study

An open-source CLI that scaffolds fully-configured Express 5 + TypeScript backends in seconds — Prisma, Drizzle, Docker, Zod, Pino, and Git hooks all wired from day one, so developers can skip the setup and start shipping.

Product

west.js

Role

Creator & Maintainer

Timeline

2024 – Present

Completion

100%

By the numbers

v3.0.0

Current stable version

4

ORM integrations

1

Command to scaffold

100%

Open source

The Problem

Every new Node.js project starts the same painful way

Starting a Node.js backend from scratch means repeating the same scaffolding ritual every single time — setting up TypeScript configs, picking an ORM, wiring Docker, configuring ESLint and Prettier, adding pre-commit hooks, and writing boilerplate middleware before writing a single line of real logic. It's slow, error-prone, and inconsistent across projects.

Boilerplate that takes hours, not minutes

A production-grade Express setup involves a dozen config files, path aliases, strict tsconfig, ESLint flat config, Prettier, and Husky hooks — none of which are unique to any one project, yet every project starts from scratch.

ORM wiring is always different

Prisma 7's new driver adapter pattern, Drizzle's per-dialect connection setup, TypeORM's decorator metadata — each ORM has its own footguns. Getting them right takes documentation-diving every time.

Docker config gets copy-pasted and grows stale

Multi-stage Dockerfiles and database-service docker-compose configs get copy-pasted between projects and drift over time. There's no canonical, up-to-date baseline.

Security defaults are an afterthought

Helmet, CORS, rate limiting, and Zod-validated environment variables rarely make it into an initial scaffold — they get added reactively, if at all.

The Solution

One command. A fully production-wired backend.

west-js-app is a CLI that runs an interactive wizard (or accepts flags for CI) and writes every file, config, and middleware you'd otherwise spend hours assembling — typed, linted, tested, and containerised before you've typed your first route.

Interactive Wizard or Headless CI Mode

Run npx west-js-app for a guided prompt flow — choose ORM, database provider, Docker support, and package manager. Or pass --yes with flags for a fully non-interactive, scriptable generation suitable for CI pipelines.

Four ORM Paths, All Fully Scaffolded

Prisma 7 (with driver adapters), Drizzle ORM, TypeORM (with decorator metadata wired), or no database — each path generates correct config files, connection modules, example schemas, and db:* scripts. Not just dependencies: actual working code.

Docker Out of the Box

Multi-stage Dockerfile with a lean production image, plus docker-compose.yml with the correct database service (postgres:16-alpine, mysql:8-debian) auto-selected for the chosen provider. Run docker compose up --build and it works.

Security-first Middleware Stack

Helmet, CORS, express-rate-limit, and Zod-parsed environment variable validation are pre-wired at startup. If a required env variable is missing, the process exits immediately with a readable error — not silently in production.

Structured Logging with Pino

pino + pino-http produce JSON request logs in production and pretty-printed coloured output in development. Plugs directly into Datadog, Loki, or CloudWatch without any additional configuration.

Testing from the First File

Jest + ts-jest + Supertest are scaffolded with integration tests for the health route and example CRUD route. Run npm test and tests pass before you've written anything.

Technology Stack

Built with the right tools

TypeScriptTypeScriptLanguage
Node.jsNode.jsRuntime
Express 5Express 5Generated
PrismaPrismaGenerated
Drizzle ORMDrizzle ORMGenerated
ZodZodGenerated
JestJestGenerated
PinoPinoGenerated
DockerDockerGenerated
PostgreSQLPostgreSQLGenerated
ESLintESLintTooling

Architecture

Monorepo (pnpm workspaces)

CLI PackageNode.js + Ts — packages/cli
Prompt Engine--yes headless mode
Template EngineConditional file generation
Generated BackendExpress 5 + TypeScript
Docs SiteNext.js — web/
west.js/
├── packages/
│   └── cli/
│       ├── src/
│       │   ├── index.ts        # CLI entrypoint & flag parsing
│       │   ├── prompts/        # Interactive wizard steps
│       │   ├── generators/     # ORM-specific file generators
│       │   └── templates/      # File templates per combination
│       └── tests/              # CLI test suite
└── web/                        # Documentation site (westjs.vishalvoid.com)

Development Journey

How it was built

Phase 01

The itch worth scratching

The motivation was personal. Setting up a new Express backend for a client project meant spending the first hour on TypeScript config, ESLint, Prettier, Husky, and Zod middleware before writing a single endpoint. I'd done it too many times. The idea was simple: build the tool once, so the setup is never the bottleneck again.

Phase 02

Designing the generator architecture

The CLI needed to support a matrix of ORM × database × tooling combinations — each producing working code, not just updated package.json files. The approach was template-based generation with conditional logic: every combination is explicitly handled, meaning Prisma 7's driver adapter pattern is correctly scaffolded regardless of whether the user picks PostgreSQL, MySQL, or SQLite.

Phase 03

Tackling Prisma 7's breaking changes

Prisma 7 introduced a required driver adapter pattern that broke the classic environment-variable-only approach. The generator had to produce the correct PrismaClient constructor with the right adapter package (PrismaPg, PrismaMysql, PrismaBetterSqlite3) per provider, plus the new prisma.config.ts with defineConfig(). Getting this right required deep reading of upstream docs and iteration.

Phase 04

The --yes flag and CI workflows

After the interactive mode worked reliably, I added a fully non-interactive --yes flag with per-option flags (--name, --database, --db-provider, --docker, --package-manager). This made the CLI useful in CI/CD pipelines, project templates, and scripted environments — not just interactive developer terminals. It also became the primary way to run the generator's own test suite.

Phase 05

Versioning and the documentation site

As the tool evolved through v1 → v2 → v3.0.0, a proper documentation site became necessary. The westjs.vishalvoid.com site was built to provide a searchable reference for all CLI flags, generated project structure, ORM-specific setup flows, and Docker config — making the tool self-contained without relying on a README.

Engineering Challenges

Hard problems, real solutions

Supporting every ORM × database combination correctly

Problem

Prisma, Drizzle, TypeORM, and Mongoose each have fundamentally different initialization patterns, config file formats, and required dependencies. A naive template approach would produce broken projects for less-common combinations.

Solution

Built a conditional generation engine where each ORM path is explicitly handled. Every combination is tested by running the CLI with the --yes flag and verifying the generated project compiles, lints, and passes tests.

Prisma 7's driver adapter requirement

Problem

Prisma 7 deprecated the classic PrismaClient without a driver adapter. The generator had to produce correct, version-specific scaffolding — including the new prisma.config.ts and the right adapter package per database provider — without falling back to outdated patterns.

Solution

Implemented provider-specific adapter mappings (pg → PrismaPg, mysql2 → PrismaMysql, better-sqlite3 → PrismaBetterSqlite3) and generated the complete Prisma 7 config surface in every relevant file.

TypeORM's reflect-metadata footgun

Problem

TypeORM requires import 'reflect-metadata' to appear before any TypeORM imports — a constraint that is easy to miss and produces cryptic runtime errors. The documentation warns about it, but developers still hit it.

Solution

The generator injects the import at the top of src/app.ts automatically and enables experimentalDecorators and emitDecoratorMetadata in tsconfig.json, eliminating the most common TypeORM setup failure entirely.

Quality & Testing

Tests included. Zero config required.

Every generated project ships with a working test suite. Developers don't set up Jest — they inherit a configured environment with integration tests that already pass. The test infrastructure itself is verified by running the CLI and executing npm test on the output.

MethodEndpointTestsPassing
GET/health22
GET/api/examples22
POST/api/examples22
GET/api/examples/:id22
DELETE/api/examples/:id22
CLI Flag Parsing100%
Generated Health Route100%
Generated Example Route (no-DB)100%
ORM Scaffold Correctness100%
Docker Config Generation100%

Project Roadmap

What's done, what's next

v3.0.0 · Stable

Interactive CLI wizard
--yes flag for CI/CD pipelines
Prisma 7 with driver adapter scaffolding
Drizzle ORM (PostgreSQL / MySQL / SQLite)
TypeORM with decorator metadata
Mongoose / MongoDB support
Multi-stage Dockerfile + docker-compose
Zod env validation at startup
Pino structured logging (dev + production)
Jest + Supertest test scaffold
ESLint flat config + Prettier + Husky
Vercel deployment config
Full documentation site

Upcoming · In Progress

Fastify as an alternative to Express
Vitest option alongside Jest
Redis + BullMQ job queue scaffold
OpenAPI / Swagger generation flag

Future · Planned

Authentication scaffold (JWT / OAuth)
WebSocket support (socket.io / ws)
GitHub Actions CI workflow generation
Hono.js support
Plugin system for community templates