CLI to create production-ready Node.js Express APIs with TypeScript/JavaScript, JWT auth, auto-generated Swagger docs, and repository pattern
npm install create-charcole$ref to Zod schemas
npm install @charcoles/swagger
typescript
// Zod schema for validation
const registerSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
// Manual OpenAPI schema (duplicate!)
/**
* @swagger
* /api/auth/register:
* post:
* requestBody:
* schema:
* type: object
* properties:
* email:
* type: string
* format: email
* password:
* type: string
* minLength: 8
* ... 60 more lines
*/
`
After with @charcoles/swagger (20 lines, zero duplication):
`typescript
// 1. Register schema once in swagger.config.ts
setupSwagger(app, {
schemas: { registerSchema }, // Auto-converted!
});
// 2. Reference in JSDoc
/**
* @swagger
* /api/auth/register:
* post:
* summary: Register user
* requestBody:
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/registerSchema'
* responses:
* 201:
* $ref: '#/components/responses/Success'
* 400:
* $ref: '#/components/responses/ValidationError'
*/
`
Result: Change your Zod schema ā Swagger updates automatically! š
$3
#### Revolutionary Repository Pattern
- Database abstraction layer - Switch databases without changing business logic
- In-memory repository included - Test APIs instantly without database setup
- Clean separation - Business logic stays independent of database implementation
- Future-proof - Easy migration between MongoDB, PostgreSQL, MySQL, etc.
#### Optional JWT Authentication Module
- Complete auth system - Register, login, logout, protected routes
- JWT-based authentication - Stateless, scalable token management
- Password hashing - Secure bcrypt password handling
- Ready-to-use - Production-ready auth APIs out of the box
- Modular design - Include/exclude during project creation
Quick Start
`bash
Create your charcole app now (with or without project name)
npx create-charcole@latest my-awesome-api
OR (interactive mode)
npx create-charcole@latest
Follow prompts to select:
1. Language: TypeScript or JavaScript
2. JWT Authentication: Yes/No (includes complete auth system)
3. Swagger Documentation: Yes/No (auto-generated from Zod schemas)
Configure environment
cp .env.example .env
Start development server (with auto-reload)
npm run dev
Visit Swagger UI (if swagger enabled)
http://localhost:3000/api-docs
OR start production server
npm start
`
Server runs on http://localhost:3000 by default.
Swagger Documentation (New in v2.2)
$3
Traditional API documentation requires writing the same schema twice:
1. Once in Zod for validation
2. Again in OpenAPI/Swagger YAML
This leads to:
- ā Massive duplication (76+ lines per endpoint)
- ā Out-of-sync documentation
- ā High maintenance burden
$3
@charcoles/swagger automatically converts your Zod schemas to OpenAPI:
`typescript
// Before: Register schema in swagger.config.ts
import { registerSchema, loginSchema } from "./schemas";
setupSwagger(app, {
schemas: {
registerSchema, // Auto-converted to OpenAPI!
loginSchema,
},
});
// After: Use $ref everywhere
/**
* @swagger
* schema:
* $ref: '#/components/schemas/registerSchema'
*/
`
$3
| Aspect | Before | After | Improvement |
| ---------------------- | --------------- | -------------- | ------------------ |
| Lines per endpoint | 45-76 lines | 10-20 lines | 60-75% less |
| Schema duplication | 100% | 0% | Eliminated |
| Maintenance | Update 2 places | Update 1 place | 50% less work |
| Sync issues | Common | Impossible | Always in sync |
See complete guide: src/lib/swagger/SWAGGER_GUIDE.md (when swagger is enabled)
Repository Pattern: A Game Changer
$3
Traditional apps mix database logic with business logic. Switching databases means rewriting everything.
$3
Charcole introduces a Repository Pattern that abstracts database operations:
`javascript
// Traditional approach (tightly coupled)
// app.ts
import mongoose from 'mongoose';
async function getUser(id: string) {
return await UserModel.findById(id); // ā Direct MongoDB dependency
}
// Charcole v2.2 approach (abstracted)
// repositories/user.repo.ts
const users: User[] = [];
type CreateUserData = {
email: string;
name: string;
passwordHash: string;
};
export const userRepo = {
async findByEmail(email: string): Promise {
return users.find((u) => u.email === email);
},
async create(data: CreateUserData): Promise {
const user: User = {
id: randomUUID(),
email: data.email,
name: data.name,
passwordHash: data.passwordHash,
role: "user",
provider: "credentials",
isEmailVerified: false,
createdAt: new Date(),
updatedAt: new Date(),
};
users.push(user);
return user;
},
};
// controller.js
async login(req, res) {
try {
const result = await AuthService.login(req.body, req.app.locals.userRepo);
res.json(result);
} catch (err) {
res.status(401).json({ message: err.message });
}
},
`
$3
- ā
Test instantly - In-memory repository works without database setup
- ā
Switch databases easily - Change MongoDB to PostgreSQL by updating one file
- ā
Clean architecture - Business logic stays pure
- ā
Better testing - Mock repositories for unit tests
- ā
Future-proof - Adapt to any database technology
JWT Authentication Module (Optional)
$3
When you select "Yes" for authentication during project creation:
`
src/modules/auth/
ā āāā auth.controller.ts # Register, login, logout, me endpoints
ā āāā auth.middleware.ts # JWT verification, protected routes
ā āāā auth.service.ts # Business logic for authentication
ā āāā auth.routes.ts # Auth API routes
ā āāā auth.schemas.ts # Auth API Schemas (auto-documented if Swagger enabled!)
ā āāā auth.constants.ts # Auth API constants
`
$3
`
POST /api/auth/register # Create new account
POST /api/auth/login # Get JWT token
GET /api/protected/me # Get current user (protected)
`
Golden Rules (Updated for v2.2)
1. Wrap async handlers with asyncHandler
`typescript
router.get("/users/:id", asyncHandler(async (req, res) => { ... }))
`
2. Throw AppError (never use res.status().json())
`typescript
throw new NotFoundError("User", { id });
`
3. Validate requests with validateRequest
`typescript
router.post("/users", validateRequest(schema), handler);
`
4. Use repositories for database operations
`typescript
// ā Direct database calls
const user = await UserModel.findById(id);
// ā
Repository pattern
const user = await AuthService.login(req.body, req.app.locals.userRepo);
`
5. Define schemas once in Zod (if Swagger enabled)
`typescript
// ā
Single source of truth
const userSchema = z.object({ ... });
// Register in swagger.config.ts
setupSwagger(app, { schemas: { userSchema } });
// ā Never duplicate in JSDoc
``