node-scaffold-cli
v1.0.6
Published
CLI tool to generate OOP structure with DI for Node.js projects (Knex/Objection ORM or Mongoose ODM)
Maintainers
Readme
Node Scaffold CLI
Generate production-ready Node.js code with OOP architecture, Dependency Injection, and support for Knex/Objection (SQL) or Mongoose (MongoDB).
🚀 Quick Start
# 1. Install the CLI globally
npm install -g node-scaffold-cli
# 2. Generate a complete resource
scaffold g user
# 3. Install required dependencies (see Prerequisites below)
# For Knex/Objection (SQL):
npm install express objection knex pg tsyringe reflect-metadata
npm install -D @types/express @types/node typescript
# For Mongoose (MongoDB):
npm install express mongoose tsyringe reflect-metadata
# 4. Configure TypeScript (see Setup section)
# 5. Start coding!That's it! 🎉
Note: Make sure to install the required dependencies for your chosen ORM and configure TypeScript before using the generated code.
📦 Prerequisites
Before using the generated code, you'll need to install dependencies based on your setup:
Quick Dependency Reference
| Setup | Required Packages |
| ---------------------- | -------------------------------------------------------- |
| All Setups | express, @types/express, @types/node, typescript |
| Knex (SQL) | objection, knex, pg (or mysql2, sqlite3, etc.) |
| Mongoose (MongoDB) | mongoose |
| With DI | tsyringe, reflect-metadata |
Installation Commands
Complete Setup (Knex + DI):
npm install express objection knex pg tsyringe reflect-metadata
npm install -D @types/express @types/node typescriptComplete Setup (Mongoose + DI):
npm install express mongoose tsyringe reflect-metadata
npm install -D @types/express @types/node typescriptWithout DI (Knex):
npm install express objection knex pg
npm install -D @types/express @types/node typescriptWithout DI (Mongoose):
npm install express mongoose
npm install -D @types/express @types/node typescriptTypeScript Configuration
Your tsconfig.json must include these options for DI to work:
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}Note: The CLI automatically adds
reflect-metadataimport when DI is enabled, but you still need to install the package.
See the Setup After Generation section for complete TypeScript configuration examples.
📋 What Does It Generate?
src/
├── models/
│ ├── user.model.ts
│ ├── product.model.ts
│ └── index.ts → Barrel exports (auto-generated)
├── repositories/
│ ├── user.repository.ts
│ ├── product.repository.ts
│ └── index.ts → Barrel exports (auto-generated)
├── services/
│ ├── user.service.ts
│ ├── product.service.ts
│ └── index.ts → Barrel exports (auto-generated)
├── controllers/
│ ├── user.controller.ts
│ ├── product.controller.ts
│ └── index.ts → Barrel exports (auto-generated)
└── routers/
├── user.router.ts
├── product.router.ts
└── index.ts → Barrel exports (auto-generated)Barrel Exports: Each folder automatically gets an index.ts file that exports all components, enabling clean imports like import { UserModel, ProductModel } from './models'.
Generated Code Example
Model (Objection):
export class User extends Model {
static tableName = 'users';
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'integer' },
// Add your fields here
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' }
}
};
}
}Note: Models are generated empty by default. Add your fields manually after generation.
Repository:
export class UserRepository {
async findById(id: number): Promise<User | undefined> {}
async findAll(): Promise<User[]> {}
async create(data: Partial<User>): Promise<User> {}
// ... more methods
}Service:
export class UserService {
constructor(private userRepository: UserRepository) {}
async getById(id: number): Promise<User | null> {}
async create(data: Partial<User>): Promise<User> {}
// ... with validation
}Controller:
export class UserController {
getAll = async (req: Request, res: Response) => {};
getById = async (req: Request, res: Response) => {};
create = async (req: Request, res: Response) => {};
// ... all CRUD endpoints
}Router:
export class UserRouter {
public router: Router;
constructor(private userController: UserController) {
this.router = Router();
this.setupRoutes();
}
private setupRoutes(): void {
this.router.get('/', this.userController.getAll);
this.router.get('/:id', this.userController.getById);
this.router.post('/', this.userController.create);
this.router.put('/:id', this.userController.update);
this.router.delete('/:id', this.userController.delete);
}
}📖 Two Commands
1. generate - Create Full Resource
Generate all components at once (interactive prompts):
scaffold generate user
scaffold g product --orm mongooseWhen to use: Starting a new resource from scratch
2. add - Create Single Component
Generate one component at a time:
scaffold add model user
scaffold add service product
scaffold a controller orderWhen to use: Adding to existing resources or granular control
⚙️ Options
| Option | Values | Default | Description |
| ------------- | ------------------ | ---------- | --------------------------------------------------- |
| --orm -o | knex, mongoose | knex | ORM/ODM (knex=Objection ORM, mongoose=Mongoose ODM) |
| --path -p | any path | ./src | Output directory |
| --no-di | - | DI enabled | Disable dependency injection |
🛡️ Safety Features
Duplicate File Protection
The CLI automatically checks if files already exist before creating them:
- If file exists: You'll be prompted to confirm overwrite
- If you skip: The file is preserved and marked as "skipped" in the summary
- Summary report: Shows which files were created, overwritten, or skipped
Example output:
✅ Created: src/models/user.model.ts
📦 Created models/index.ts with barrel export
✏️ Overwritten: src/repositories/user.repository.ts
📦 Updated repositories/index.ts with new export
⏭️ Skipped: src/services/user.service.tsAutomatic DI Setup
When generating with DI enabled (--di is default):
- Auto-detects entry point files (
index.ts,app.ts,main.ts,server.ts,config/container.ts) - Auto-adds
import 'reflect-metadata';if missing - Skips if already imported
- Shows message when import is added
Note: You still need to install: npm install reflect-metadata
💡 Common Usage
Complete Workflow
# Step 1: Install CLI (if not already installed)
npm install -g node-scaffold-cli
# Step 2: Install project dependencies (see Prerequisites above)
npm install express objection knex pg tsyringe reflect-metadata
npm install -D @types/express @types/node typescript
# Step 3: Configure TypeScript (see Setup section)
# Add experimentalDecorators and emitDecoratorMetadata to tsconfig.json
# Step 4: Generate resource
scaffold g user --orm knex
# Step 5: You'll be prompted:
# - Select components: ✓ Model ✓ Repository ✓ Service ✓ Controller (Router optional)
# Step 6: If files already exist, you'll be asked to confirm overwrite
# Step 7: Done! Files created in src/models, src/repositories, etc.
# - Summary shows: created, overwritten, and skipped files
# - reflect-metadata import added automatically (if DI enabled)
# - index.ts barrel export files created/updated automatically
# Step 8: Set up database and routes (see Setup section)Adding Fields to Models
Models are generated empty by default. Add fields manually after generation:
// Example: Adding fields to a Knex/Objection model
export class User extends Model {
static tableName = 'users';
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'integer' },
name: { type: 'string' },
email: { type: 'string' },
age: { type: 'number' },
// Add your fields here
created_at: { type: 'string', format: 'date-time' },
updated_at: { type: 'string', format: 'date-time' }
}
};
}
}Incremental Building
# Build piece by piece
scaffold add model user
scaffold add repository user
scaffold add service user
scaffold add controller user
scaffold add router userMultiple Resources
# Each resource in its own files
scaffold g user
scaffold g product
scaffold g order
# Result:
# src/models/user.model.ts, product.model.ts, order.model.ts
# src/repositories/user.repository.ts, product.repository.ts, ...
# Each folder has index.ts with barrel exportsClean Imports with Barrel Exports
The CLI automatically creates index.ts files in each folder, enabling clean imports:
// Instead of individual imports:
import { UserModel } from './models/user.model';
import { ProductModel } from './models/product.model';
// Use barrel exports:
import { UserModel, ProductModel } from './models';
import { UserRepository, ProductRepository } from './repositories';
import { UserService, ProductService } from './services';
import { UserController, ProductController } from './controllers';
import { UserRouter, ProductRouter } from './routers';Note: Barrel exports are automatically created/updated when you generate files. No manual maintenance needed!
📚 Documentation
- Quick Reference ⭐ - Command cheatsheet and examples
- Add Command Guide - Detailed guide for
addcommand - Complete Examples - Full working examples with setup
- Architecture - Understanding the generated structure
- Development Guide - Extending and customizing
🎯 Key Features
✅ Clean Architecture
- Model - Data structure (Objection ORM for SQL, Mongoose ODM for MongoDB)
- Repository - Database queries
- Service - Business logic + validation
- Controller - HTTP handlers
- Router - Express route definitions
✅ TypeScript Ready
- Full type safety
- IntelliSense support
- Type-safe classes and methods
✅ Dependency Injection
- Optional DI with tsyringe
- Easy testing with mocks
- Clean dependencies
✅ Production Patterns
- RESTful endpoints
- Error handling
- Validation placeholders
- CRUD operations
✅ Smart File Management
- Duplicate detection - Prompts before overwriting existing files
- Automatic setup - Adds
reflect-metadataimport when DI is enabled - Barrel exports - Automatically creates/updates
index.tsfiles in each folder for clean imports - Safe operations - Shows summary of created, overwritten, and skipped files
🔧 Setup After Generation
Step 1: Install Dependencies
For Knex (SQL) Projects:
npm install express objection knex pg tsyringe reflect-metadata
npm install -D @types/express @types/node typescriptFor Mongoose (MongoDB) Projects:
npm install express mongoose tsyringe reflect-metadata
npm install -D @types/express @types/node typescriptWithout Dependency Injection:
# Knex
npm install express objection knex pg
npm install -D @types/express @types/node typescript
# MongoDB
npm install express mongoose
npm install -D @types/express @types/node typescriptStep 2: Configure TypeScript
Create or update your tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules"]
}Important:
experimentalDecoratorsandemitDecoratorMetadataare required for Dependency Injection to work.
Step 3: Database Setup
For Knex (SQL):
# 1. Create database config (see EXAMPLES.md)
# 2. Create migration
npx knex migrate:make create_users_table
# 3. Run migrations
npx knex migrate:latestFor Mongoose (MongoDB):
# 1. Connect to MongoDB using Mongoose (see EXAMPLES.md)
# 2. No migrations needed!Step 4: Setup DI Container (If Using DI)
✨ Automatic Setup: When you generate files with DI enabled, the CLI automatically:
- Detects your entry point file (
index.ts,app.ts,main.ts,server.ts, orconfig/container.ts) - Adds
import 'reflect-metadata';at the top if it's missing - Skips adding if it's already imported
Manual Setup (if automatic detection fails):
// src/config/container.ts
import 'reflect-metadata'; // ← Added automatically by CLI
import { container } from 'tsyringe';
import { createDatabase } from './database';
import { UserRepository } from '../repositories/user.repository';
import { UserService } from '../services/user.service';
import { UserController } from '../controllers/user.controller';
// Register database connection
const db = createDatabase();
container.register('Database', { useValue: db });
// Register components
container.register(UserRepository, { useClass: UserRepository });
container.register(UserService, { useClass: UserService });
container.register(UserController, { useClass: UserController });Note: You still need to install:
npm install tsyringe reflect-metadata
Step 5: Register Routes
With Router (Recommended):
// src/server.ts or src/app.ts
import express from 'express';
import { container } from 'tsyringe';
import { UserRouter } from './routers/user.router';
const app = express();
app.use(express.json());
// Resolve router from DI container
const userRouter = container.resolve(UserRouter);
// Register router
app.use('/api/users', userRouter.router);
app.listen(3000, () => {
console.log('Server running on port 3000');
});With Dependency Injection (Direct Controller):
// src/server.ts or src/app.ts
import express from 'express';
import { container } from 'tsyringe';
import { UserController } from './controllers/user.controller';
const app = express();
app.use(express.json());
// Resolve controller from DI container
const userController = container.resolve(UserController);
// Register routes
app.get('/api/users', userController.getAll);
app.get('/api/users/:id', userController.getById);
app.post('/api/users', userController.create);
app.put('/api/users/:id', userController.update);
app.delete('/api/users/:id', userController.delete);
app.listen(3000, () => {
console.log('Server running on port 3000');
});Without Dependency Injection:
// src/server.ts
import express from 'express';
import { UserRouter } from './routers/user.router';
import { UserController } from './controllers/user.controller';
import { UserService } from './services/user.service';
import { UserRepository } from './repositories/user.repository';
import { createDatabase } from './config/database';
const app = express();
app.use(express.json());
// Manual instantiation
const db = createDatabase();
const userRepository = new UserRepository(db);
const userService = new UserService(userRepository);
const userController = new UserController(userService);
const userRouter = new UserRouter(userController);
// Register router
app.use('/api/users', userRouter.router);
app.listen(3000, () => {
console.log('Server running on port 3000');
});🆚 Generate vs Add
| Feature | generate | add |
| --------------- | ------------------- | --------------------- |
| Use Case | New resource | Single component |
| Interactive | Select components | No prompts |
| Speed | Fast for full setup | Precise control |
| Output | Multiple files | One file |
Tip: Use generate to start, then add for additional components later!
🎨 Examples
Example 1: Basic User CRUD
scaffold g user
# Models are generated empty - add fields manually after generationExample 2: E-commerce Product
scaffold g product --orm mongoose
# Models are generated empty - add fields manually after generationExample 3: Adding Admin Service
# You already have user resource
scaffold add service admin-user
# Customize for admin-specific logicExample 4: Multi-tenant App
scaffold g tenant --orm knex
scaffold g user --orm knex
scaffold g project --orm knex
# Each gets full CRUD structure🔍 Troubleshooting
Common Issues
"Cannot find module 'express'" or similar errors:
- Make sure you've installed all required dependencies (see Prerequisites)
- Run
npm installin your project directory
"Decorator metadata is not enabled" or DI not working:
- Ensure
experimentalDecorators: trueandemitDecoratorMetadata: trueare in yourtsconfig.json - Make sure
reflect-metadatais installed:npm install reflect-metadata - The CLI adds the import automatically, but you need to install the package
"File already exists" prompt:
- This is normal! The CLI protects you from accidental overwrites
- Choose "Yes" to overwrite, or "No" to skip and keep your existing file
TypeScript compilation errors:
- Make sure you have
@types/expressand@types/nodeinstalled - Check that your
tsconfig.jsonincludes the required compiler options
Database connection issues:
- For Knex/Objection: Ensure your database driver is installed (
pg,mysql2, etc.) - For Mongoose: Ensure
mongooseis installed - See EXAMPLES.md for complete database setup examples
Getting Help
- Quick Reference: Check QUICKREF.md for command examples
- Full Examples: See EXAMPLES.md for complete setup guides
- Issues: Report problems on GitHub
- Questions: Check documentation files first
🤝 Support
- Issues: Report on GitHub
- Questions: Check QUICKREF.md first
- Examples: See EXAMPLES.md
📝 License
MIT
🌟 Pro Tips
- Start with
generatefor new resources - Use
addfor granular control - Check QUICKREF.md for quick commands
- Read EXAMPLES.md for full setup
- Customize generated code - it's starter code!
- Safe to re-run - CLI checks for duplicates and asks before overwriting
- DI setup is automatic -
reflect-metadataimport is handled for you
Need more details? → Check out QUICKREF.md for command reference and examples!
