miryala
Version:
A production-ready TypeScript Express.js backend generator with MongoDB or PostgreSQL support
192 lines (165 loc) • 8.99 kB
JavaScript
const fs = require('fs');
const path = require('path');
const createFolders = (folders, projectRoot = process.cwd()) => {
folders.forEach((folder) => {
const folderPath = path.join(projectRoot, folder);
if (!fs.existsSync(folderPath)) {
fs.mkdirSync(folderPath, { recursive: true });
console.log(`Created folder: ${folder}`);
} else {
console.log(`Folder already exists: ${folder}`);
}
});
};
const createFile = (fileName, content, projectRoot = process.cwd()) => {
const filePath = path.join(projectRoot, fileName);
if (!fs.existsSync(filePath)) {
fs.writeFileSync(filePath, content);
console.log(`Created file: ${fileName}`);
} else {
console.log(`File already exists: ${fileName}`);
}
};
const generateEnvFileContent = (dbChoice) => {
const mongoDb = dbChoice === 'mongodb'
? `MONGO_DATABASE_URL=mongodb://localhost:27017/myapp`
: `DATABASE_URL=postgresql://user:password@localhost:5432/mydb`;
return `PORT=5000
NODE_ENV=development
# NODE_ENV=production
# NODE_ENV=seed
# Database
${mongoDb}
# JWT
JWT_ACCESS_TOKEN_SECRET=40051dc52aa534598646248e692db2d70224cf751507a782daee7742f45e92486ca3de2ce447ff10a2ca76032f07bd57624e7f12e38c1d6966438944f04fc4352
JWT_REFRESH_TOKEN_SECRET=037258be634643ed41ace736e5b3a1b4628e6d69889ef3275f4a3097307f3b522db381dda5e477d58f8519fc0e8e32c984497bf3adaa4d228a2e0459743
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_TOKEN_EXPIRES_IN=30d
BCRYPT_SALT_ROUNDS=10
# Email
EMAIL_HOST_PROVIDER_NAME=smtp.gmail.com
EMAIL_HOST_PROVIDER_PORT=587
EMAIL_SENDER_EMAIL=example@gmail.com
EMAIL_SENDER_EMAIL_APP_PASS=your_app_password
EMAIL_SENDER_NAME=Your App Name
EMAIL_REPLY_TO=example@gmail.com
EMAIL_TEST_RECIPIENTS=example@gmail.com
# Client & Backend URLs
CLIENT_SIDE_URL=http://localhost:5173
# CLIENT_SIDE_URL=your.client.domain.com
BACKEND_SIDE_URL=http://localhost:5000
# BACKEND_SIDE_URL=your.api.domain.com
`;
};
const createFiles = (dbChoice) => {
const templatesDir = path.join(__dirname, '../templates');
const serverTemplate = fs.readFileSync(path.join(templatesDir, 'server.js.template'), 'utf-8');
const appTemplate = fs.readFileSync(path.join(templatesDir, 'app.js.template'), 'utf-8');
createFile('server.js', serverTemplate.replace('{{DB_FILE}}', `./db/${dbChoice}.js`));
createFile('app.js', appTemplate);
createFile('.env', generateEnvFileContent(dbChoice));
};
// ─── Advanced Structure ───────────────────────────────────────────────────────
const createAdvancedStructure = async (dbChoice, projectRoot = process.cwd()) => {
const templatesDir = path.join(__dirname, '../templates/advanced');
const folders = [
'src/app',
'src/builder',
'src/config',
'src/constants',
'src/errors',
'src/helpers',
'src/interfaces',
'src/lib',
'src/middlewares',
'src/modules/demo_module',
'src/routers',
'src/schemas',
'src/seed',
'src/shared',
'src/types',
'src/utils',
];
if (dbChoice === 'postgresql') folders.push('prisma', 'generated/prisma');
createFolders(folders, projectRoot);
const read = (name) => fs.readFileSync(path.join(templatesDir, name), 'utf-8');
const write = (fileName, content) => createFile(fileName, content, projectRoot);
// Root files
write('.env', generateEnvFileContent(dbChoice));
write('.env.example', generateEnvFileContent(dbChoice));
write('.gitignore', read('gitignore.template'));
write('.prettierrc.json', read('prettierrc.template'));
write('tsconfig.json', read('tsconfig.template'));
write('eslint.config.mjs', read('eslint.template'));
// src/
write('src/app.ts', read('app.ts.template'));
write('src/server.ts', read('server.ts.template').replace('{{DB_IMPORT}}', dbChoice === 'mongodb' ? read('db_import_mongo.template') : read('db_import_prisma.template')));
// src/config/
write('src/config/index.ts', read('config.ts.template'));
// src/constants/
write('src/constants/userRole_constant.ts', read('userRole_constant.ts.template'));
// src/errors/
write('src/errors/AppError.ts', read('AppError.ts.template'));
write('src/errors/globalErrorHandler.ts', read(dbChoice === 'mongodb' ? 'globalErrorHandler_mongo.ts.template' : 'globalErrorHandler_prisma.ts.template'));
if (dbChoice === 'mongodb') {
write('src/errors/handleMongooseCastError.ts', read('handleMongooseCastError.ts.template'));
write('src/errors/handleMongooseDuplicateError.ts', read('handleMongooseDuplicateError.ts.template'));
write('src/errors/handleMongooseValidationError.ts', read('handleMongooseValidationError.ts.template'));
} else {
write('src/errors/handlePrismaCastError.ts', read('handlePrismaCastError.ts.template'));
write('src/errors/handlePrismaDuplicateError.ts', read('handlePrismaDuplicateError.ts.template'));
write('src/errors/handlePrismaValidationError.ts', read('handlePrismaValidationError.ts.template'));
}
// src/interfaces/
write('src/interfaces/errors.ts', read('errors_interface.ts.template'));
write('src/interfaces/index.d.ts', read('index.d.ts.template'));
write('src/interfaces/emailFormat.ts', read('emailFormat.ts.template'));
write('src/interfaces/jwtToken_interface.ts', read('jwtToken_interface.ts.template'));
write('src/interfaces/userRole_type.ts', read('userRole_type.ts.template'));
// src/middlewares/
write('src/middlewares/auth.ts', read('auth.ts.template'));
write('src/middlewares/notFound.ts', read('notFound.ts.template'));
write('src/middlewares/validateRequest.ts', read('validateRequest.ts.template'));
write('src/middlewares/rateLimitingHandler.ts', read('rateLimitingHandler.ts.template'));
write('src/middlewares/handleFileUpload.ts', read('handleFileUpload.ts.template'));
write('src/middlewares/bigIntSerializer.ts', read('bigIntSerializer.ts.template'));
write('src/middlewares/formDataToSetJSONformatData.ts', read('formDataToSetJSONformatData.ts.template'));
write('src/middlewares/morganMiddleware.ts', read('morganMiddleware.ts.template'));
// src/utils/
write('src/utils/catchAsync.ts', read('catchAsync.ts.template'));
write('src/utils/sendResponse.ts', read('sendResponse.ts.template'));
write('src/utils/commonUtils.ts', read('commonUtils.ts.template'));
write('src/utils/sendEmail.ts', read('sendEmail.ts.template'));
write('src/utils/node_cache.ts', read('node_cache.ts.template'));
write('src/utils/logger.ts', read('logger.ts.template'));
write('src/utils/removeUploadedFiles.ts', read('removeUploadedFiles.ts.template'));
// src/builder/
if (dbChoice === 'mongodb') {
write('src/builder/QueryBuilder.ts', read('MongooseQueryBuilder.ts.template'));
} else {
write('src/builder/QueryBuilder.ts', read('PrismaQueryBuilder.ts.template'));
write('src/shared/prisma.ts', read('prisma_shared.template'));
write('prisma/schema.prisma', read('schema.prisma.template'));
}
// src/routers/
write('src/routers/index.ts', read('routers_index.ts.template'));
// src/modules/demo_module/
write('src/modules/demo_module/demo_module.route.ts', read('demo_route.ts.template'));
write('src/modules/demo_module/demo_module.controller.ts', read('demo_controller.ts.template'));
write('src/modules/demo_module/demo_module.service.ts', read('demo_service.ts.template'));
if (dbChoice === 'mongodb') {
write('src/modules/demo_module/demo_module.model.ts', read('demo_model_mongo.ts.template'));
}
write('src/modules/demo_module/demo_module.validation.ts', read('demo_validation.ts.template'));
write('src/modules/demo_module/demo_module.constant.ts', read('demo_module_constant.ts.template'));
write('src/modules/demo_module/demo_module.interface.ts', read('demo_module_interface.ts.template'));
write('src/modules/demo_module/demo_module.helpers.ts', read('demo_module_helpers.ts.template'));
write('src/modules/demo_module/demo_module.lib.ts', read('demo_module_lib.ts.template'));
write('src/modules/demo_module/demo_module.utils.ts', read('demo_module_utils.ts.template'));
// uploads/
createFolders(['uploads/images', 'uploads/videos'], projectRoot);
write('uploads/images/.gitkeep', '');
write('uploads/videos/.gitkeep', '');
console.log('✅ Advanced TypeScript project structure created successfully.');
};
module.exports = { createFolders, createFile, createFiles, createAdvancedStructure };