Skip to main content
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).
1

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;
2

Create framework template directory

Create a new directory for your framework:
mkdir -p apps/create-node-blueprint/src/template/frameworks/koa
3

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' };
  }
};
4

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' },
  ],
}
5

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);
}
6

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:
1

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;
2

Create ORM template directory

mkdir -p apps/create-node-blueprint/src/template/orms/typeorm
3

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;
}
4

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:
  1. Environment variables - Add database connection variables
  2. Docker Compose - Add database service configuration
  3. 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 %>';
<% }); %>

Comments

<%# 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

1

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
2

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
3

Test the generated app

cd test1
npm install
npm run dev
Verify the app runs without errors.
4

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.ejsserver.ts)
  • Use descriptive names that indicate purpose

Updating Documentation

When adding new templates:
  1. Update the main README.md with the new option
  2. Add examples showing the new template in action
  3. 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:
  • Added enum/constant for new template type
  • Created template directory with all necessary files
  • Updated prompts to include new option
  • Updated generation logic to handle new template
  • Added dependencies to package.json template
  • Tested with multiple combinations
  • Verified generated projects run without errors
  • Updated documentation
  • Tested that existing templates still work

Next Steps

Contributing Overview

Review contribution guidelines

Monorepo Structure

Understand the codebase organization

Build docs developers (and LLMs) love