xpressify
v2.2.2
Published
Modern Express CLI — scaffolding and code generation for TypeScript, ESM and Zod-powered Express applications
Downloads
774
Maintainers
Readme
__ __ _ __
\ \ / / (_)/ _|
\ V / _ __ _ __ ___ ___ ___ _| |_ _ _
> < | '_ \| '__/ _ \/ __/ __| | _| | | |
/ . \| |_) | | | __/\__ \__ \ | | | |_| |
/_/ \_\ .__/|_| \___||___/___/_|_| \__, |
| | __/ |
|_| |___/Modern Express CLI — TypeScript · ESM · Zod
What is Xpressify?
Xpressify is a CLI that removes the repetitive setup work of a new Express backend. Run one command and get a production-ready TypeScript + ESM project with structure, security middleware, and tooling already wired.
It also helps after project creation: use generators to add routes, middleware, and TypeScript constructs with consistent architecture and naming — just like Angular CLI does for frontend projects.
Quick Start
# Install globally
npm install -g xpressify
# Create a new project (interactive)
xpressify new my-api
# Or scaffold non-interactively (CI-friendly)
xpressify new my-api --yes --features eslint,prettier,zod
# Start coding
cd my-api
npm run devCommand Reference
| Command | Description |
|---|---|
| xpressify new [name] | Scaffold a new Express + TypeScript project |
| xpressify generate <type> <component-name> | Generate a component inside an existing project |
| xpressify g <type> <component-name> | Alias for generate |
| xpressify --help | Show help |
| xpressify --version | Show installed version |
The binary is also available as short aliases x and xpressify-cli —
x g route users is equivalent to xpressify generate route users.
new — Create a project
Interactive mode
xpressify new my-apiThe command launches an interactive prompt where you pick your package manager, quality tooling, and optional libraries. When done, you get:
my-api/
├── src/
│ ├── app.ts ← Express app with cors, helmet, rate-limit
│ ├── server.ts ← Entry point with dotenv
│ ├── routes/
│ ├── controllers/
│ ├── services/
│ ├── middlewares/
│ └── utils/
├── .env.example
├── .gitignore
├── tsconfig.json ← NodeNext module + moduleResolution
└── package.json ← "type": "module"Non-interactive mode
Pass --yes (or any configuration flag) to skip the prompts. This is what you
want in CI pipelines, Docker images, and one-shot scaffolding scripts:
# Defaults (npm, no optional features, install deps)
xpressify new my-api --yes
# Pick features and package manager
xpressify new my-api --yes \
--features eslint,prettier,zod \
--package-manager pnpm
# Logger feature + specific library
xpressify new my-api --yes \
--features logger,jwt \
--logger winston
# Testing framework (vitest or jest)
xpressify new my-api --yes \
--features testing \
--testing-library jest
# Docker-ready project
xpressify new my-api --yes \
--features docker,eslint,prettier
# Scaffold only, skip dependency install
xpressify new my-api --yes --no-installFlags for new:
| Flag | Description | Default |
|---|---|---|
| -y, --yes | Skip all prompts | — |
| --package-manager <pm> | npm, pnpm, or yarn | npm |
| --features <list> | Comma-separated feature list (see below) | `` (none) |
| --logger <library> | pino or winston (only with logger feature) | pino |
| --testing-library <library> | vitest or jest (only with testing feature) | vitest |
| --no-install | Skip dependency installation | install |
What you can select
Code quality tools
| Option | What it does |
|---|---|
| eslint | Static analysis with TypeScript rules |
| prettier | Opinionated code formatting |
| husky | Git hooks — auto-adds ESLint + Prettier |
| github-actions | CI pipeline for Node 22 / 24 |
Project features
| Option | What it does |
|---|---|
| zod | Runtime schema validation |
| logger | Structured logging — choose pino or winston |
| jwt | Adds jsonwebtoken + bcryptjs to deps |
| docker | Adds multi-stage Dockerfile, .dockerignore, and docker-compose.yml |
| testing | Adds a test framework — choose vitest or jest with sample test |
generate — Add components
Run from anywhere inside your project. Xpressify walks up the directory tree
to find package.json and treats that as the project root. Every generator
writes files relative to that root — the result does not depend on the
directory you ran the command from.
# Route — creates router + controller + service (3 files)
xpressify g route users
xpressify g route user-profile
# Middleware — creates typed Express middleware
xpressify g middleware auth
xpressify g middleware request-logger
# TypeScript constructs — defaults to src/classes, src/interfaces, src/enums
xpressify g class User
xpressify g interface Product
xpressify g enum Status
# Same, with an explicit path (path notation is supported by every generator)
xpressify g class src/models/User
xpressify g interface src/types/Product
xpressify g enum src/domain/Status
# DTO — Zod-aware if zod is in the target project, plain interface otherwise
xpressify g dto CreateUser
xpressify g dto src/dtos/CreateOrder
# Test — picks Vitest or Jest by inspecting the target's devDependencies
xpressify g test users
# Util — defaults to src/utils/ with a .util.ts suffix
xpressify g util format-date
xpressify g util src/modules/auth/token-helpersGenerator types
| Type | Output |
|---|---|
| route | src/routes/<name>.router.ts + src/controllers/<name>.controller.ts + src/services/<name>.service.ts |
| middleware | src/middlewares/<name>.middleware.ts |
| class | src/classes/<name>.class.ts (or <path>/<name>.class.ts) |
| interface | src/interfaces/<name>.interface.ts (or <path>/<name>.interface.ts) |
| enum | src/enums/<name>.enum.ts (or <path>/<name>.enum.ts) |
| dto | src/dtos/<name>.dto.ts (or <path>/<name>.dto.ts) — uses Zod schema + z.infer when zod is present, otherwise emits a plain interface |
| test | src/__tests__/<name>.test.ts (or <path>/<name>.test.ts) — Vitest or Jest template chosen by detecting the framework in package.json; fails with a hint if neither is installed |
| util | src/utils/<name>.util.ts (or <path>/<name>.util.ts) |
Generated files use ESM-compatible .js extensions in relative imports,
matching the NodeNext module resolution used in the scaffolded tsconfig.json.
Programmatic API
Xpressify also works as a library. Types, Zod schemas, naming utilities, and error classes are all exported from the main entry point:
import type {
NewProjectOptions,
Feature,
LoggerLibrary,
TestingLibrary,
GenerateOptions,
} from 'xpressify';
import {
NewProjectOptionsSchema,
TestingLibrarySchema,
resolveNames,
XpressifyError,
} from 'xpressify';
// Validate options programmatically
const options = NewProjectOptionsSchema.parse({
name: 'my-api',
targetDir: '/projects/my-api',
packageManager: 'pnpm',
features: ['eslint', 'prettier', 'zod', 'testing'],
loggerLibrary: null,
testingLibrary: 'vitest',
installDependencies: true,
});
// Use the naming utilities
const names = resolveNames('user-profile');
// { kebab: 'user-profile', pascal: 'UserProfile', camel: 'userProfile', ... }
// Parse a testing library value directly
const lib = TestingLibrarySchema.parse('jest'); // 'jest'What You Get by Default
Every generated project ships with practical, production-oriented defaults:
Express with cors, helmet, and express-rate-limit pre-wired; a /health
endpoint out of the box; ESM output via "type": "module" in package.json
and NodeNext in tsconfig.json; TypeScript strict mode targeting ES2022;
tsx + nodemon for a fast local dev loop with no build step; and dotenv
for environment variables. A README.md and .nvmrc are scaffolded so
the project is ready to open, run, and share without further setup.
Relative imports in the scaffold use .js extensions (import app from './app.js'),
which is what Node requires at runtime for ESM and what tsc preserves under
NodeNext module resolution.
Requirements
Node.js >= 22.0.0 (Node 20 reached end-of-life on 2026-04-30).
node --versionTroubleshooting
Invalid CLI input (bad project name, unknown --package-manager, illegal
characters in a component name) produces a single, readable error message
and exits with code 1. If you need the full stack trace to debug an
unexpected failure, set XPRESSIFY_DEBUG=1:
XPRESSIFY_DEBUG=1 xpressify new my-api --yesIf generate fails with "Not inside a Node.js project", make sure you're
running the command from somewhere inside a directory tree that contains a
package.json — Xpressify walks up to find the project root.
If generate refuses with "Refusing to create file outside of project
root", the path you provided resolved outside your project (for example
../../foo). Drop the escaping segments and use a path inside the project.
Upgrading
To 2.2.2
- BREAKING (generate):
g class,g interface, andg enumwithout an explicit path used to land next to the current working directory. They now write tosrc/classes/,src/interfaces/, andsrc/enums/under the detected project root, matching the behaviour of every other generator (route,middleware,util,dto,test). Explicit path arguments (g class src/models/User) are unaffected.
From 1.x to 2.x
Version 2.0.0 is a breaking release — generated projects now use ESM
instead of CommonJS, and the generate command argument was renamed from
the broken <n> placeholder to <component-name>. Existing projects
scaffolded by 1.x are not affected; only newly generated projects get
the new defaults. See CHANGELOG.md for the full list
of changes.
Development
git clone https://github.com/davids199005-oss/xpressify.git
cd xpressify
npm install
npm run dev # tsup in watch modeUseful scripts:
npm run build # production build with tsup
npm run typecheck # tsc --noEmit
npm run lint # ESLint
npm run test # Vitest
npm run test:coverage # Vitest + coverage reportThe CLI binary is bundled with all node_modules inlined (except figlet,
which reads its .flf font files at runtime). This is deliberate — several
direct dependencies are ESM-only and cannot be loaded via require() from
a CJS bundle. See tsup.config.ts for the exact configuration.
License
MIT © David Veryutin
