create-backlist 4.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,76 +6,94 @@ const { analyzeFrontend } = require('../analyzer');
6
6
  const { renderAndWrite, getTemplatePath } = require('./template');
7
7
 
8
8
  async function generateNodeProject(options) {
9
- // v4.0: Destructure the new 'addSeeder' option
10
- const { projectDir, projectName, frontendSrcDir, addAuth, addSeeder } = options;
9
+ // v5.0: Destructure all new options
10
+ const { projectDir, projectName, frontendSrcDir, dbType, addAuth, addSeeder, extraFeatures = [] } = options;
11
+ const port = 8000;
11
12
 
12
13
  try {
13
- // --- Step 1: Analyze Frontend to get Endpoints and Schema Info ---
14
+ // --- Step 1: Analyze Frontend ---
14
15
  console.log(chalk.blue(' -> Analyzing frontend for API endpoints...'));
15
16
  const endpoints = await analyzeFrontend(frontendSrcDir);
16
- if (endpoints.length > 0) {
17
- console.log(chalk.green(` -> Found ${endpoints.length} endpoints.`));
18
- } else {
19
- console.log(chalk.yellow(' -> No API endpoints found. A basic project will be created.'));
20
- }
17
+ if (endpoints.length > 0) console.log(chalk.green(` -> Found ${endpoints.length} endpoints.`));
18
+ else console.log(chalk.yellow(' -> No API endpoints found. A basic project will be created.'));
21
19
 
22
- // --- Step 2: Identify which Database Models to Generate ---
20
+ // --- Step 2: Identify Models to Generate ---
23
21
  const modelsToGenerate = new Map();
24
22
  endpoints.forEach(ep => {
25
23
  if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
26
- modelsToGenerate.set(ep.controllerName, ep.schemaFields);
24
+ modelsToGenerate.set(ep.controllerName, { name: ep.controllerName, fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type, isUnique: key === 'email' })) });
27
25
  }
28
26
  });
29
-
30
27
  if (addAuth && !modelsToGenerate.has('User')) {
31
28
  console.log(chalk.yellow(' -> Authentication requires a "User" model. Creating a default one.'));
32
- modelsToGenerate.set('User', { name: 'String', email: 'String', password: 'String' });
29
+ modelsToGenerate.set('User', { name: 'User', fields: [{ name: 'name', type: 'String' }, { name: 'email', type: 'String', isUnique: true }, { name: 'password', type: 'String' }] });
33
30
  }
34
31
 
35
- // --- Step 3: Scaffold Base Project Structure & Files ---
36
- console.log(chalk.blue(' -> Scaffolding Node.js (Express + TS) project...'));
32
+ // --- Step 3: Base Scaffolding ---
33
+ console.log(chalk.blue(' -> Scaffolding Node.js project...'));
37
34
  const destSrcDir = path.join(projectDir, 'src');
38
35
  await fs.ensureDir(destSrcDir);
39
36
  await fs.copy(getTemplatePath('node-ts-express/base/server.ts'), path.join(destSrcDir, 'server.ts'));
40
37
  await fs.copy(getTemplatePath('node-ts-express/base/tsconfig.json'), path.join(projectDir, 'tsconfig.json'));
41
38
 
42
- // --- Step 4: Prepare and Write package.json with all conditional dependencies ---
43
- const packageJsonContent = JSON.parse(
44
- await ejs.renderFile(getTemplatePath('node-ts-express/partials/package.json.ejs'), { projectName })
45
- );
46
-
47
- if (modelsToGenerate.size > 0 || addAuth) {
48
- packageJsonContent.dependencies['mongoose'] = '^7.5.0';
39
+ // --- Step 4: Prepare and Write package.json ---
40
+ const packageJsonContent = JSON.parse(await ejs.renderFile(getTemplatePath('node-ts-express/partials/package.json.ejs'), { projectName }));
41
+
42
+ if (dbType === 'mongoose') packageJsonContent.dependencies['mongoose'] = '^7.6.3';
43
+ if (dbType === 'prisma') {
44
+ packageJsonContent.dependencies['@prisma/client'] = '^5.6.0';
45
+ packageJsonContent.devDependencies['prisma'] = '^5.6.0';
46
+ packageJsonContent.prisma = { seed: `ts-node ${addSeeder ? 'scripts/seeder.ts' : 'prisma/seed.ts'}` };
49
47
  }
50
48
  if (addAuth) {
51
49
  packageJsonContent.dependencies['jsonwebtoken'] = '^9.0.2';
52
50
  packageJsonContent.dependencies['bcryptjs'] = '^2.4.3';
53
- packageJsonContent.devDependencies['@types/jsonwebtoken'] = '^9.0.2';
54
- packageJsonContent.devDependencies['@types/bcryptjs'] = '^2.4.2';
51
+ packageJsonContent.devDependencies['@types/jsonwebtoken'] = '^9.0.5';
52
+ packageJsonContent.devDependencies['@types/bcryptjs'] = '^2.4.6';
55
53
  }
56
- // v4.0: Add seeder dependencies and scripts
57
54
  if (addSeeder) {
58
- packageJsonContent.devDependencies['@faker-js/faker'] = '^8.2.0';
59
- // We also need chalk for the seeder script's console logs
60
- packageJsonContent.dependencies['chalk'] = '^4.1.2';
61
- packageJsonContent.scripts['seed'] = 'ts-node scripts/seeder.ts';
62
- packageJsonContent.scripts['destroy'] = 'ts-node scripts/seeder.ts -d';
55
+ packageJsonContent.devDependencies['@faker-js/faker'] = '^8.3.1';
56
+ if (!packageJsonContent.dependencies['chalk']) packageJsonContent.dependencies['chalk'] = '^4.1.2';
57
+ packageJsonContent.scripts['seed'] = `ts-node scripts/seeder.ts`;
58
+ packageJsonContent.scripts['destroy'] = `ts-node scripts/seeder.ts -d`;
59
+ }
60
+ if (extraFeatures.includes('testing')) {
61
+ packageJsonContent.devDependencies['jest'] = '^29.7.0';
62
+ packageJsonContent.devDependencies['supertest'] = '^6.3.3';
63
+ packageJsonContent.devDependencies['@types/jest'] = '^29.5.10';
64
+ packageJsonContent.devDependencies['@types/supertest'] = '^2.0.16';
65
+ packageJsonContent.devDependencies['ts-jest'] = '^29.1.1';
66
+ packageJsonContent.scripts['test'] = 'jest --detectOpenHandles --forceExit';
67
+ }
68
+ if (extraFeatures.includes('swagger')) {
69
+ packageJsonContent.dependencies['swagger-ui-express'] = '^5.0.0';
70
+ packageJsonContent.dependencies['swagger-jsdoc'] = '^6.2.8';
71
+ packageJsonContent.devDependencies['@types/swagger-ui-express'] = '^4.1.6';
63
72
  }
64
73
  await fs.writeJson(path.join(projectDir, 'package.json'), packageJsonContent, { spaces: 2 });
65
74
 
66
- // --- Step 5 & 6: Generate Models, Controllers, and Auth boilerplate ---
75
+ // --- Step 5: Generate DB-specific files & Controllers ---
67
76
  if (modelsToGenerate.size > 0) {
68
- console.log(chalk.blue(' -> Generating database models and controllers...'));
69
- await fs.ensureDir(path.join(destSrcDir, 'models'));
70
77
  await fs.ensureDir(path.join(destSrcDir, 'controllers'));
71
- for (let [modelName, schema] of modelsToGenerate.entries()) {
72
- if (addAuth && modelName === 'User') {
73
- schema = { name: 'String', email: 'String', password: 'String', ...schema };
78
+ if (dbType === 'mongoose') {
79
+ console.log(chalk.blue(' -> Generating Mongoose models and controllers...'));
80
+ await fs.ensureDir(path.join(destSrcDir, 'models'));
81
+ for (const [modelName, modelData] of modelsToGenerate.entries()) {
82
+ const schema = modelData.fields.reduce((acc, field) => { acc[field.name] = field.type; return acc; }, {});
83
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Model.ts.ejs'), path.join(destSrcDir, 'models', `${modelName}.model.ts`), { modelName, schema });
84
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Controller.ts.ejs'), path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`), { modelName });
85
+ }
86
+ } else if (dbType === 'prisma') {
87
+ console.log(chalk.blue(' -> Generating Prisma schema and controllers...'));
88
+ await fs.ensureDir(path.join(projectDir, 'prisma'));
89
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/PrismaSchema.prisma.ejs'), path.join(projectDir, 'prisma', 'schema.prisma'), { modelsToGenerate: Array.from(modelsToGenerate.values()) });
90
+ for (const [modelName] of modelsToGenerate.entries()) {
91
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/PrismaController.ts.ejs'), path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`), { modelName });
74
92
  }
75
- await renderAndWrite(getTemplatePath('node-ts-express/partials/Model.ts.ejs'), path.join(destSrcDir, 'models', `${modelName}.model.ts`), { modelName, schema });
76
- await renderAndWrite(getTemplatePath('node-ts-express/partials/Controller.ts.ejs'), path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`), { modelName });
77
93
  }
78
94
  }
95
+
96
+ // --- Step 6: Generate Authentication Boilerplate ---
79
97
  if (addAuth) {
80
98
  console.log(chalk.blue(' -> Generating authentication boilerplate...'));
81
99
  await fs.ensureDir(path.join(destSrcDir, 'routes'));
@@ -83,60 +101,94 @@ async function generateNodeProject(options) {
83
101
  await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.controller.ts.ejs'), path.join(destSrcDir, 'controllers', 'Auth.controller.ts'), {});
84
102
  await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.routes.ts.ejs'), path.join(destSrcDir, 'routes', 'Auth.routes.ts'), {});
85
103
  await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.middleware.ts.ejs'), path.join(destSrcDir, 'middleware', 'Auth.middleware.ts'), {});
86
-
87
- const userModelPath = path.join(destSrcDir, 'models', 'User.model.ts');
88
- if (await fs.pathExists(userModelPath)) {
89
- let userModelContent = await fs.readFile(userModelPath, 'utf-8');
90
- if (!userModelContent.includes('bcryptjs')) {
91
- userModelContent = userModelContent.replace(`import mongoose, { Schema, Document } from 'mongoose';`, `import mongoose, { Schema, Document } from 'mongoose';\nimport bcrypt from 'bcryptjs';`);
92
- const preSaveHook = `\n// Hash password before saving\nUserSchema.pre('save', async function(next) {\n if (!this.isModified('password')) {\n return next();\n }\n const salt = await bcrypt.genSalt(10);\n this.password = await bcrypt.hash(this.password, salt);\n next();\n});\n`;
93
- userModelContent = userModelContent.replace(`// Create and export the Model`, `${preSaveHook}\n// Create and export the Model`);
94
- await fs.writeFile(userModelPath, userModelContent);
104
+
105
+ if (dbType === 'mongoose') {
106
+ const userModelPath = path.join(destSrcDir, 'models', 'User.model.ts');
107
+ if (await fs.pathExists(userModelPath)) {
108
+ let userModelContent = await fs.readFile(userModelPath, 'utf-8');
109
+ if (!userModelContent.includes('bcryptjs')) {
110
+ userModelContent = userModelContent.replace(`import mongoose, { Schema, Document } from 'mongoose';`, `import mongoose, { Schema, Document } from 'mongoose';\nimport bcrypt from 'bcryptjs';`);
111
+ const preSaveHook = `\n// Hash password before saving\nUserSchema.pre('save', async function(next) {\n if (!this.isModified('password')) {\n return next();\n }\n const salt = await bcrypt.genSalt(10);\n this.password = await bcrypt.hash(this.password, salt);\n next();\n});\n`;
112
+ userModelContent = userModelContent.replace(`// Create and export the Model`, `${preSaveHook}\n// Create and export the Model`);
113
+ await fs.writeFile(userModelPath, userModelContent);
114
+ }
95
115
  }
96
116
  }
97
117
  }
98
118
 
99
- // --- Step 7 (v4.0): Generate Seeder Script ---
119
+ // --- Step 7: Generate Seeder Script ---
100
120
  if (addSeeder) {
101
121
  console.log(chalk.blue(' -> Generating database seeder script...'));
102
122
  await fs.ensureDir(path.join(projectDir, 'scripts'));
103
- await renderAndWrite(
104
- getTemplatePath('node-ts-express/partials/Seeder.ts.ejs'),
105
- path.join(projectDir, 'scripts', 'seeder.ts'),
106
- { projectName }
107
- );
123
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Seeder.ts.ejs'), path.join(projectDir, 'scripts', 'seeder.ts'), { projectName });
108
124
  }
109
125
 
110
- // --- Step 8: Generate the Main Route File ---
111
- console.log(chalk.gray(' -> Generating dynamic API routes...'));
112
- await renderAndWrite(getTemplatePath('node-ts-express/partials/routes.ts.ejs'), path.join(destSrcDir, 'routes.ts'), { endpoints, addAuth });
126
+ // --- Step 8: Generate Extra Features ---
127
+ if (extraFeatures.includes('docker')) {
128
+ console.log(chalk.blue(' -> Generating Docker files...'));
129
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Dockerfile.ejs'), path.join(projectDir, 'Dockerfile'), { dbType, port });
130
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/docker-compose.yml.ejs'), path.join(projectDir, 'docker-compose.yml'), { projectName, dbType, port });
131
+ }
132
+ if (extraFeatures.includes('swagger')) {
133
+ console.log(chalk.blue(' -> Generating API documentation setup...'));
134
+ await fs.ensureDir(path.join(destSrcDir, 'utils'));
135
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/ApiDocs.ts.ejs'), path.join(destSrcDir, 'utils', 'swagger.ts'), { projectName, port });
136
+ }
137
+ if (extraFeatures.includes('testing')) {
138
+ console.log(chalk.blue(' -> Generating testing boilerplate...'));
139
+ const jestConfig = `/** @type {import('ts-jest').JestConfigWithTsJest} */\nmodule.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n verbose: true,\n};`;
140
+ await fs.writeFile(path.join(projectDir, 'jest.config.js'), jestConfig);
141
+ await fs.ensureDir(path.join(projectDir, 'src', '__tests__'));
142
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/App.test.ts.ejs'), path.join(projectDir, 'src', '__tests__', 'api.test.ts'), { addAuth });
143
+ }
144
+
145
+ // --- Step 9: Generate Main Route File & Inject Logic into Server ---
146
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/routes.ts.ejs'), path.join(destSrcDir, 'routes.ts'), { endpoints, addAuth, dbType });
113
147
 
114
- // --- Step 9: Inject Logic into Main Server File ---
115
148
  let serverFileContent = await fs.readFile(path.join(destSrcDir, 'server.ts'), 'utf-8');
116
- let dbConnectionCode = '';
117
- if (modelsToGenerate.size > 0 || addAuth) {
118
- dbConnectionCode = `\n// --- Database Connection ---\nimport mongoose from 'mongoose';\nconst MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/${projectName}';\nmongoose.connect(MONGO_URI)\n .then(() => console.log('MongoDB Connected...'))\n .catch(err => console.error('MongoDB Connection Error:', err));\n// -------------------------\n`;
149
+ let dbConnectionCode = '', swaggerInjector = '', authRoutesInjector = '';
150
+
151
+ if (dbType === 'mongoose') {
152
+ dbConnectionCode = `\n// --- Database Connection ---\nimport mongoose from 'mongoose';\nconst MONGO_URI = process.env.MONGO_URI || 'mongodb://127.0.0.1:27017/${projectName}';\nmongoose.connect(MONGO_URI).then(() => console.log('MongoDB Connected...')).catch(err => console.error(err));\n// -------------------------\n`;
153
+ } else if (dbType === 'prisma') {
154
+ dbConnectionCode = `\nimport { PrismaClient } from '@prisma/client';\nexport const prisma = new PrismaClient();\n`;
155
+ }
156
+ if (extraFeatures.includes('swagger')) {
157
+ swaggerInjector = `\nimport { setupSwagger } from './utils/swagger';\nsetupSwagger(app);\n`;
119
158
  }
120
- let authRoutesInjector = '';
121
159
  if (addAuth) {
122
- authRoutesInjector = `import authRoutes from './routes/Auth.routes';\napp.use('/api/auth', authRoutes);\n\n`;
160
+ authRoutesInjector = `import authRoutes from './routes/Auth.routes';\napp.use('/api/auth', authRoutes);\n\n`;
123
161
  }
162
+
124
163
  serverFileContent = serverFileContent
125
164
  .replace("dotenv.config();", `dotenv.config();${dbConnectionCode}`)
126
165
  .replace('// INJECT:ROUTES', `${authRoutesInjector}import apiRoutes from './routes';\napp.use('/api', apiRoutes);`);
166
+
167
+ const listenRegex = /(app\.listen\()/;
168
+ serverFileContent = serverFileContent.replace(listenRegex, `${swaggerInjector}\n$1`);
127
169
  await fs.writeFile(path.join(destSrcDir, 'server.ts'), serverFileContent);
128
170
 
129
- // --- Step 10: Install All Dependencies ---
130
- console.log(chalk.magenta(' -> Installing all dependencies... This might take a moment.'));
171
+ // --- Step 10: Install Dependencies & Run Post-install Scripts ---
172
+ console.log(chalk.magenta(' -> Installing dependencies... This may take a moment.'));
131
173
  await execa('npm', ['install'], { cwd: projectDir });
132
-
133
- // --- Step 11: Generate Final Files (README, .env.example) ---
134
- await renderAndWrite(getTemplatePath('node-ts-express/partials/README.md.ejs'), path.join(projectDir, 'README.md'), { projectName });
135
- if (addAuth) {
136
- const envExampleContent = `PORT=8000\nMONGO_URI=mongodb://127.0.0.1:27017/${projectName}\nJWT_SECRET=your_super_secret_jwt_key_123`;
137
- await fs.writeFile(path.join(projectDir, '.env.example'), envExampleContent);
174
+ if (dbType === 'prisma') {
175
+ console.log(chalk.blue(' -> Running `prisma generate`...'));
176
+ await execa('npx', ['prisma', 'generate'], { cwd: projectDir });
138
177
  }
139
-
178
+
179
+ // --- Step 11: Generate Final Files (.env.example) ---
180
+ let envContent = `PORT=${port}\n`;
181
+ if (dbType === 'mongoose') {
182
+ envContent += `MONGO_URI=mongodb://root:example@db:27017/${projectName}?authSource=admin\n`;
183
+ } else if (dbType === 'prisma') {
184
+ envContent += `DATABASE_URL="postgresql://user:password@db:5432/${projectName}?schema=public"\n`;
185
+ }
186
+ if (addAuth) envContent += `JWT_SECRET=your_super_secret_jwt_key_12345\n`;
187
+ if (extraFeatures.includes('docker')) {
188
+ envContent += `\n# Docker-compose credentials (used in docker-compose.yml)\nDB_USER=user\nDB_PASSWORD=password\nDB_NAME=${projectName}`;
189
+ }
190
+ await fs.writeFile(path.join(projectDir, '.env.example'), envContent);
191
+
140
192
  } catch (error) {
141
193
  throw error;
142
194
  }
@@ -0,0 +1,75 @@
1
+ const chalk = require('chalk');
2
+ const { execa } = require('execa');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const { analyzeFrontend } = require('../analyzer');
6
+ const { renderAndWrite, getTemplatePath } = require('./template');
7
+
8
+ async function generatePythonProject(options) {
9
+ const { projectDir, projectName, frontendSrcDir } = options;
10
+
11
+ try {
12
+ // --- Step 1: Analysis & Model Identification ---
13
+ console.log(chalk.blue(' -> Analyzing frontend for Python (FastAPI) backend...'));
14
+ const endpoints = await analyzeFrontend(frontendSrcDir);
15
+ const modelsToGenerate = new Map();
16
+ endpoints.forEach(ep => {
17
+ if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
18
+ modelsToGenerate.set(ep.controllerName, { name: ep.controllerName, fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type })) });
19
+ }
20
+ });
21
+
22
+ // --- Step 2: Scaffold Base Python Project ---
23
+ console.log(chalk.blue(' -> Scaffolding Python (FastAPI) project...'));
24
+ const appDir = path.join(projectDir, 'app');
25
+ const routesDir = path.join(appDir, 'routes');
26
+ await fs.ensureDir(appDir);
27
+ await fs.ensureDir(routesDir);
28
+
29
+ // --- Step 3: Generate Files from Templates ---
30
+ const controllers = Array.from(modelsToGenerate.keys());
31
+
32
+ // Generate main.py
33
+ await renderAndWrite(getTemplatePath('python-fastapi/main.py.ejs'), path.join(projectDir, 'app', 'main.py'), { projectName, controllers });
34
+
35
+ // Generate requirements.txt
36
+ await renderAndWrite(getTemplatePath('python-fastapi/requirements.txt.ejs'), path.join(projectDir, 'requirements.txt'), {});
37
+
38
+ // Generate route file for each model
39
+ for (const [modelName, modelData] of modelsToGenerate.entries()) {
40
+ await renderAndWrite(
41
+ getTemplatePath('python-fastapi/routes.py.ejs'),
42
+ path.join(routesDir, `${modelName.toLowerCase()}_routes.py`),
43
+ { modelName, schema: modelData }
44
+ );
45
+ }
46
+
47
+ // --- Step 4: Setup Virtual Environment and Install Dependencies ---
48
+ console.log(chalk.magenta(' -> Setting up virtual environment and installing dependencies...'));
49
+ // Create a virtual environment
50
+ await execa('python', ['-m', 'venv', 'venv'], { cwd: projectDir });
51
+
52
+ // Determine the correct pip executable path based on OS
53
+ const pipPath = process.platform === 'win32'
54
+ ? path.join('venv', 'Scripts', 'pip')
55
+ : path.join('venv', 'bin', 'pip');
56
+
57
+ // Install dependencies using the virtual environment's pip
58
+ await execa(path.join(projectDir, pipPath), ['install', '-r', 'requirements.txt'], { cwd: projectDir });
59
+
60
+ console.log(chalk.green(' -> Python backend generation is complete!'));
61
+ console.log(chalk.yellow('\nTo run your new Python backend:'));
62
+ console.log(chalk.cyan(' 1. Activate the virtual environment: `source venv/bin/activate` (or `venv\\Scripts\\activate` on Windows)'));
63
+ console.log(chalk.cyan(' 2. Start the server: `uvicorn app.main:app --reload`'));
64
+
65
+
66
+ } catch (error) {
67
+ // Improve error message for command not found
68
+ if (error.code === 'ENOENT') {
69
+ throw new Error(`'${error.command}' command not found. Please ensure Python and venv are installed and in your system's PATH.`);
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ module.exports = { generatePythonProject };
@@ -0,0 +1,61 @@
1
+ // Auto-generated by create-backlist
2
+ package <%= group %>.<%= projectName %>.controller;
3
+
4
+ import <%= group %>.<%= projectName %>.model.<%= controllerName %>;
5
+ import <%= group %>.<%= projectName %>.repository.<%= controllerName %>Repository;
6
+ import org.springframework.beans.factory.annotation.Autowired;
7
+ import org.springframework.http.HttpStatus;
8
+ import org.springframework.http.ResponseEntity;
9
+ import org.springframework.web.bind.annotation.*;
10
+
11
+ import java.util.List;
12
+ import java.util.Optional;
13
+
14
+ @RestController
15
+ @CrossOrigin(origins = "*") // Allow all origins for development
16
+ @RequestMapping("/api/<%= controllerName.toLowerCase() %>s")
17
+ public class <%= controllerName %>Controller {
18
+
19
+ @Autowired
20
+ private <%= controllerName %>Repository repository;
21
+
22
+ @GetMapping
23
+ public List<<%= controllerName %>> getAll<%= controllerName %>s() {
24
+ return repository.findAll();
25
+ }
26
+
27
+ @GetMapping("/{id}")
28
+ public ResponseEntity<<%= controllerName %>> get<%= controllerName %>ById(@PathVariable Long id) {
29
+ Optional<<%= controllerName %>> item = repository.findById(id);
30
+ return item.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.notFound().build());
31
+ }
32
+
33
+ @PostMapping
34
+ public ResponseEntity<<%= controllerName %>> create<%= controllerName %>(@RequestBody <%= controllerName %> newItem) {
35
+ <%= controllerName %> savedItem = repository.save(newItem);
36
+ return new ResponseEntity<>(savedItem, HttpStatus.CREATED);
37
+ }
38
+
39
+ @PutMapping("/{id}")
40
+ public ResponseEntity<<%= controllerName %>> update<%= controllerName %>(@PathVariable Long id, @RequestBody <%= controllerName %> updatedItem) {
41
+ return repository.findById(id)
42
+ .map(item -> {
43
+ // Manually map fields to update. For simplicity, we assume all fields are updatable.
44
+ <% model.fields.forEach(field => { %>
45
+ item.set<%= field.name.charAt(0).toUpperCase() + field.name.slice(1) %>(updatedItem.get<%= field.name.charAt(0).toUpperCase() + field.name.slice(1) %>());
46
+ <% }); %>
47
+ <%= controllerName %> savedItem = repository.save(item);
48
+ return ResponseEntity.ok(savedItem);
49
+ })
50
+ .orElseGet(() -> ResponseEntity.notFound().build());
51
+ }
52
+
53
+ @DeleteMapping("/{id}")
54
+ public ResponseEntity<Void> delete<%= controllerName %>(@PathVariable Long id) {
55
+ if (!repository.existsById(id)) {
56
+ return ResponseEntity.notFound().build();
57
+ }
58
+ repository.deleteById(id);
59
+ return ResponseEntity.noContent().build();
60
+ }
61
+ }
@@ -0,0 +1,24 @@
1
+ // Auto-generated by create-backlist v6.0
2
+ package <%= group %>.<%= projectName %>.model;
3
+
4
+ import jakarta.persistence.Entity;
5
+ import jakarta.persistence.Id;
6
+ import jakarta.persistence.GeneratedValue;
7
+ import jakarta.persistence.GenerationType;
8
+ import lombok.Data;
9
+
10
+ @Data
11
+ @Entity
12
+ public class <%= modelName %> {
13
+
14
+ @Id
15
+ @GeneratedValue(strategy = GenerationType.AUTO)
16
+ private Long id;
17
+
18
+ <% model.fields.forEach(field => { %>
19
+ <% let javaType = 'String'; %>
20
+ <% if (field.type === 'Number') javaType = 'Integer'; %>
21
+ <% if (field.type === 'Boolean') javaType = 'boolean'; %>
22
+ private <%= javaType %> <%= field.name %>;
23
+ <% }); %>
24
+ }
@@ -0,0 +1,12 @@
1
+ // Auto-generated by create-backlist
2
+ package <%= group %>.<%= projectName %>.repository;
3
+
4
+ import <%= group %>.<%= projectName %>.model.<%= modelName %>;
5
+ import org.springframework.data.jpa.repository.JpaRepository;
6
+ import org.springframework.stereotype.Repository;
7
+
8
+ @Repository
9
+ public interface <%= modelName %>Repository extends JpaRepository<<%= modelName %>, Long> {
10
+ // Spring Data JPA automatically provides CRUD methods like findAll(), findById(), save(), deleteById()
11
+ // You can add custom query methods here if needed.
12
+ }
@@ -0,0 +1,33 @@
1
+ // Auto-generated by create-backlist v5.0
2
+ import swaggerUi from 'swagger-ui-express';
3
+ import swaggerJsdoc from 'swagger-jsdoc';
4
+ import { Express } from 'express';
5
+
6
+ const options: swaggerJsdoc.Options = {
7
+ definition: {
8
+ openapi: '3.0.0',
9
+ info: {
10
+ title: '<%= projectName %> API Documentation',
11
+ version: '1.0.0',
12
+ description: 'API documentation for the auto-generated backend.',
13
+ },
14
+ servers: [
15
+ {
16
+ url: 'http://localhost:<%= port %>',
17
+ description: 'Development server',
18
+ },
19
+ ],
20
+ // TODO: Add components (e.g., securitySchemes for JWT)
21
+ },
22
+ // Path to the API docs
23
+ apis: ['./src/routes/*.ts', './src/routes.ts'], // Looks for JSDoc comments in routes
24
+ };
25
+
26
+ const swaggerSpec = swaggerJsdoc(options);
27
+
28
+ export function setupSwagger(app: Express) {
29
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
30
+ console.log(
31
+ `📄 API documentation is available at http://localhost:<%= port %>/api-docs`
32
+ );
33
+ }
@@ -0,0 +1,38 @@
1
+ // Auto-generated by create-backlist v5.0
2
+ import request from 'supertest';
3
+ import express from 'express';
4
+ // Import your main app configuration. This might need path adjustment.
5
+ // For simplicity, we create a test server here.
6
+ import apiRoutes from '../routes'; // Assuming main routes
7
+ import authRoutes from '../routes/Auth.routes'; // Assuming auth routes
8
+
9
+ const app = express();
10
+ app.use(express.json());
11
+ app.use('/api', apiRoutes);
12
+ app.use('/api/auth', authRoutes);
13
+
14
+
15
+ describe('API Endpoints', () => {
16
+
17
+ it('should respond to the root GET endpoint', async () => {
18
+ // This test assumes a GET /api/ endpoint exists or a similar public one.
19
+ // You might need to adjust this to a real endpoint from your app.
20
+ // Example for GET /api/users
21
+ // const res = await request(app).get('/api/users');
22
+ // expect(res.statusCode).toEqual(200);
23
+
24
+ // For now, a placeholder test:
25
+ expect(1 + 1).toBe(2);
26
+ });
27
+
28
+ // TODO: Add more specific tests for your generated endpoints
29
+ // describe('POST /api/users', () => {
30
+ // it('should create a new user', async () => {
31
+ // const res = await request(app)
32
+ // .post('/api/users')
33
+ // .send({ name: 'Test User', email: 'test@example.com', password: 'password123' });
34
+ // expect(res.statusCode).toEqual(201);
35
+ // expect(res.body).toHaveProperty('name', 'Test User');
36
+ // });
37
+ // });
38
+ });