This guide explains how to add new templates to Node Blueprint CLI, whether it’s a new framework, database, ORM, or other template type.
Template System Overview
Node Blueprint uses EJS (Embedded JavaScript) templates to generate project files dynamically based on user selections. Templates are organized in the apps/create-node-blueprint/src/template/ directory.
Template Directory Structure
src/template/
├── base/ # Base files for every project
├── common/ # Shared code across configurations
├── frameworks/ # Framework-specific templates
│ ├── express/
│ └── fastify/
├── orms/ # ORM-specific templates
│ ├── drizzle/
│ ├── prisma/
│ └── mongoose/
└── util/ # Utility templates
Adding a New Framework
Let’s walk through adding a new framework (e.g., Koa, Hapi, or NestJS).
Add framework to enums
First, add your framework to the framework enum/constants: Look for the framework definitions in src/enums/ or src/constants/ and add your new framework: export const FRAMEWORKS = {
EXPRESS: 'express' ,
FASTIFY: 'fastify' ,
KOA: 'koa' , // New framework
} as const ;
Create framework template directory
Create a new directory for your framework: mkdir -p apps/create-node-blueprint/src/template/frameworks/koa
Add framework-specific files
Add the framework-specific template files. For example: frameworks/koa/server.ts.ejs :import Koa from 'koa' ;
import Router from '@koa/router' ;
const app = new Koa ();
const router = new Router ();
// Routes
router . get ( '/' , ( ctx ) => {
ctx . body = { message: 'Hello from <%= projectName %>' };
});
app . use ( router . routes ());
app . use ( router . allowedMethods ());
const PORT = process . env . PORT || 3000 ;
app . listen ( PORT , () => {
console . log ( `Server running on port ${ PORT } ` );
});
frameworks/koa/middleware.ts.ejs :import type { Context , Next } from 'koa' ;
export const errorHandler = async ( ctx : Context , next : Next ) => {
try {
await next ();
} catch ( err ) {
console . error ( err );
ctx . status = 500 ;
ctx . body = { error: 'Internal Server Error' };
}
};
Update prompts
Add your framework to the interactive prompts in src/prompts/: {
type : 'select' ,
name : 'framework' ,
message : 'Select a framework' ,
options : [
{ value: 'express' , label: 'Express' },
{ value: 'fastify' , label: 'Fastify' },
{ value: 'koa' , label: 'Koa' , hint: 'Lightweight & expressive' },
],
}
Update template generation logic
Modify the template generation service in src/services/ to handle your new framework: if ( config . framework === 'koa' ) {
// Copy Koa-specific templates
await copyTemplate ( 'frameworks/koa/server.ts.ejs' , 'src/server.ts' , config );
await copyTemplate ( 'frameworks/koa/middleware.ts.ejs' , 'src/middleware.ts' , config );
}
Add framework dependencies
Update the base package.json template to include framework-specific dependencies: {
"dependencies" : {
<% if (framework === 'koa') { %>
"koa" : "^2.15.0" ,
"@koa/router" : "^12.0.1"
<% } %>
},
"devDependencies" : {
<% if (framework === 'koa') { %>
"@types/koa" : "^2.14.0" ,
"@types/koa__router" : "^12.0.0"
<% } %>
}
}
Adding a New ORM
The process for adding a new ORM is similar:
Add ORM to enums
Add your ORM to the constants: export const ORMS = {
DRIZZLE: 'drizzle' ,
PRISMA: 'prisma' ,
MONGOOSE: 'mongoose' ,
TYPEORM: 'typeorm' , // New ORM
} as const ;
Create ORM template directory
mkdir -p apps/create-node-blueprint/src/template/orms/typeorm
Add ORM-specific files
Create configuration, schema, and connection files: orms/typeorm/config.ts.ejs :import { DataSource } from 'typeorm' ;
import { User } from './entities/User' ;
export const AppDataSource = new DataSource ({
type: '<%= database %>' ,
host: process . env . DB_HOST ,
port: parseInt ( process . env . DB_PORT || '5432' ),
username: process . env . DB_USER ,
password: process . env . DB_PASSWORD ,
database: process . env . DB_NAME ,
entities: [ User ],
synchronize: true ,
});
orms/typeorm/entities/User.ts.ejs :import { Entity , PrimaryGeneratedColumn , Column } from 'typeorm' ;
@ Entity ()
export class User {
@ PrimaryGeneratedColumn ()
id : number ;
@ Column ()
name : string ;
@ Column ({ unique: true })
email : string ;
}
Update prompts and generation logic
Add the ORM to prompts and update the template generation service to handle TypeORM files.
Adding a New Database
For databases, you mainly need to update:
Environment variables - Add database connection variables
Docker Compose - Add database service configuration
ORM configurations - Update ORM configs to support the new database
EJS Template Syntax
Node Blueprint uses EJS for dynamic templates. Here are common patterns:
Conditional Content
<% if (framework === 'express') { %>
import express from 'express';
<% } else if (framework === 'fastify') { %>
import fastify from 'fastify';
<% } %>
Variables
const projectName = '<%= projectName %>';
const port = <%= port || 3000 %>;
Loops
<% features.forEach(feature => { %>
import { <%= feature %> } from './<%= feature %>';
<% }); %>
<%# This is a comment and won't appear in output %>
Testing Your Templates
Always test template generation with multiple combinations before submitting a PR!
Test Checklist
Test in isolation
Test your new template with different combinations: # If adding a new framework
create-node-blueprint --name test1 --framework koa --database postgres --orm drizzle
create-node-blueprint --name test2 --framework koa --database mongodb --orm mongoose
Verify generated files
Check that:
All expected files are created
No syntax errors in generated code
Dependencies are correctly added to package.json
TypeScript compiles without errors
Test the generated app
cd test1
npm install
npm run dev
Verify the app runs without errors.
Test with existing templates
Make sure your changes don’t break existing template combinations: create-node-blueprint --name test3 --framework express --database postgres --orm prisma
create-node-blueprint --name test4 --framework fastify --database mongodb --orm mongoose
Best Practices
Template Organization
Keep framework-specific code in frameworks/
Keep ORM-specific code in orms/
Put shared code in common/
Base files that every project needs go in base/
Code Quality
Use TypeScript with proper types
Follow the existing code style
Add comments for complex logic
Use descriptive variable names in EJS templates
Dependencies
Pin major versions, allow minor/patch updates (e.g., ^2.15.0)
Include both runtime dependencies and dev dependencies (@types)
Test with the specified versions
File Naming
Use .ejs extension for template files
Match the output filename (e.g., server.ts.ejs → server.ts)
Use descriptive names that indicate purpose
Updating Documentation
When adding new templates:
Update the main README.md with the new option
Add examples showing the new template in action
Update the docs site if needed
Common Patterns
Handling Multiple Database Types
<% if (database === 'postgres' || database === 'mysql') { %>
import { Pool } from 'pg';
<% } else if (database === 'mongodb') { %>
import { MongoClient } from 'mongodb';
<% } %>
Environment Variables
# Database
<% if (database === 'postgres') { %>
DB_HOST=localhost
DB_PORT=5432
<% } else if (database === 'mongodb') { %>
DB_URI=mongodb://localhost:27017/<%= projectName %>
<% } %>
Docker Compose Services
services:
<% if (database === 'postgres') { %>
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: ${DB_USER}
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_DB: ${DB_NAME}
ports:
- "5432:5432"
<% } %>
Getting Help
Stuck while adding templates? Here’s how to get help:
Check existing templates - Look at how Express, Fastify, or other templates are implemented
Open an issue - Describe what you’re trying to add and any problems you’re facing
Ask in discussions - Get feedback from maintainers and community
Submission Checklist
Before submitting your PR:
Next Steps
Contributing Overview Review contribution guidelines
Monorepo Structure Understand the codebase organization