create-backlist 5.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.
package/bin/index.js CHANGED
@@ -98,6 +98,18 @@ async function main() {
98
98
  ],
99
99
  when: (answers) => answers.stack === 'node-ts-express'
100
100
  },
101
+ {
102
+ type: 'list',
103
+ name: 'stack',
104
+ message: 'Select the backend stack:',
105
+ choices: [
106
+ { name: 'Node.js (TypeScript, Express)', value: 'node-ts-express' },
107
+ { name: 'C# (ASP.NET Core Web API)', value: 'dotnet-webapi' },
108
+ new inquirer.Separator(),
109
+ { name: 'Python (FastAPI) - Coming Soon', disabled: true, value: 'python-fastapi' },
110
+ { name: 'Java (Spring Boot)', value: 'java-spring' }, // <-- ENABLED!
111
+ ],
112
+ },
101
113
  ]);
102
114
 
103
115
  const options = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-backlist",
3
- "version": "5.0.0",
3
+ "version": "5.0.1",
4
4
  "description": "An advanced, multi-language backend generator based on frontend analysis.",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -18,11 +18,13 @@
18
18
  "dependencies": {
19
19
  "@babel/parser": "^7.22.7",
20
20
  "@babel/traverse": "^7.22.8",
21
+ "axios": "^1.13.1",
21
22
  "chalk": "^4.1.2",
22
23
  "ejs": "^3.1.9",
23
24
  "execa": "^6.1.0",
24
25
  "fs-extra": "^11.1.1",
25
26
  "glob": "^10.3.3",
26
- "inquirer": "^8.2.4"
27
+ "inquirer": "^8.2.4",
28
+ "unzipper": "^0.12.3"
27
29
  }
28
- }
30
+ }
@@ -0,0 +1,100 @@
1
+ const chalk = require('chalk');
2
+ const { execa } = require('execa');
3
+ const fs = require('fs-extra');
4
+ const path = require('path');
5
+ const axios = require('axios'); // For making HTTP requests
6
+ const unzipper = require('unzipper'); // For extracting .zip files
7
+ const { analyzeFrontend } = require('../analyzer');
8
+ const { renderAndWrite, getTemplatePath } = require('./template');
9
+
10
+ async function generateJavaProject(options) {
11
+ const { projectDir, projectName, frontendSrcDir } = options;
12
+ const group = 'com.backlist.generated'; // A default Java group ID
13
+
14
+ try {
15
+ // --- Step 1: Download Base Project from Spring Initializr ---
16
+ console.log(chalk.blue(' -> Contacting Spring Initializr to download a base Spring Boot project...'));
17
+
18
+ // Define standard dependencies for a web API
19
+ const dependencies = 'web,data-jpa,lombok,postgresql'; // Using PostgreSQL as an example DB driver
20
+ const springInitializrUrl = `https://start.spring.io/starter.zip?type=maven-project&language=java&bootVersion=3.2.0&groupId=${group}&artifactId=${projectName}&name=${projectName}&dependencies=${dependencies}`;
21
+
22
+ const response = await axios({
23
+ url: springInitializrUrl,
24
+ method: 'GET',
25
+ responseType: 'stream'
26
+ });
27
+
28
+ // --- Step 2: Unzip the Downloaded Project ---
29
+ console.log(chalk.blue(' -> Unzipping the Spring Boot project...'));
30
+ await new Promise((resolve, reject) => {
31
+ const stream = response.data.pipe(unzipper.Extract({ path: projectDir }));
32
+ stream.on('finish', () => {
33
+ console.log(chalk.gray(' -> Project unzipped successfully.'));
34
+ resolve();
35
+ });
36
+ stream.on('error', reject);
37
+ });
38
+
39
+ // --- Step 3: Analyze Frontend ---
40
+ const endpoints = await analyzeFrontend(frontendSrcDir);
41
+ const modelsToGenerate = new Map();
42
+ endpoints.forEach(ep => {
43
+ if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
44
+ modelsToGenerate.set(ep.controllerName, { name: ep.controllerName, fields: Object.entries(ep.schemaFields).map(([key, type]) => ({ name: key, type })) });
45
+ }
46
+ });
47
+
48
+ // --- Step 4: Generate Java Entities and Controllers ---
49
+ if (modelsToGenerate.size > 0) {
50
+ console.log(chalk.blue(' -> Generating Java entities and controllers...'));
51
+
52
+ const javaSrcPath = path.join(projectDir, 'src', 'main', 'java', ...group.split('.'), projectName);
53
+ const entityDir = path.join(javaSrcPath, 'model');
54
+ const controllerDir = path.join(javaSrcPath, 'controller');
55
+ await fs.ensureDir(entityDir);
56
+ await fs.ensureDir(controllerDir);
57
+
58
+ for (const [modelName, modelData] of modelsToGenerate.entries()) {
59
+ await renderAndWrite(
60
+ getTemplatePath('java-spring/partials/Entity.java.ejs'),
61
+ path.join(entityDir, `${modelName}.java`),
62
+ { group, projectName, modelName, model: modelData }
63
+ );
64
+ await renderAndWrite(
65
+ getTemplatePath('java-spring/partials/Controller.java.ejs'),
66
+ path.join(controllerDir, `${modelName}Controller.java`),
67
+ { group, projectName, controllerName: modelName }
68
+ );
69
+ }
70
+ }
71
+
72
+ // --- Step 5: Configure application.properties ---
73
+ console.log(chalk.blue(' -> Configuring database in application.properties...'));
74
+ const propsPath = path.join(projectDir, 'src', 'main', 'resources', 'application.properties');
75
+ const dbProps = [
76
+ `\n\n# --- Auto-generated by create-backlist ---`,
77
+ `# --- Database Configuration (PostgreSQL) ---`,
78
+ `spring.datasource.url=jdbc:postgresql://localhost:5432/${projectName}`,
79
+ `spring.datasource.username=postgres`,
80
+ `spring.datasource.password=password`,
81
+ `spring.jpa.hibernate.ddl-auto=update`,
82
+ `spring.jpa.show-sql=true`,
83
+ ];
84
+ await fs.appendFile(propsPath, dbProps.join('\n'));
85
+
86
+ console.log(chalk.green(' -> Java (Spring Boot) backend generation is complete!'));
87
+ console.log(chalk.yellow('\nTo run your new Java backend:'));
88
+ console.log(chalk.cyan(' 1. Open the project in a Java IDE (like IntelliJ IDEA or VS Code).'));
89
+ console.log(chalk.cyan(' 2. Run the main application file.'));
90
+
91
+
92
+ } catch (error) {
93
+ if (error.response) {
94
+ throw new Error(`Failed to download from Spring Initializr. Status: ${error.response.status}`);
95
+ }
96
+ throw error;
97
+ }
98
+ }
99
+
100
+ module.exports = { generateJavaProject };
@@ -36,39 +36,39 @@ async function generateNodeProject(options) {
36
36
  await fs.copy(getTemplatePath('node-ts-express/base/server.ts'), path.join(destSrcDir, 'server.ts'));
37
37
  await fs.copy(getTemplatePath('node-ts-express/base/tsconfig.json'), path.join(projectDir, 'tsconfig.json'));
38
38
 
39
- // --- Step 4: Prepare and Write package.json with All Conditional Dependencies ---
39
+ // --- Step 4: Prepare and Write package.json ---
40
40
  const packageJsonContent = JSON.parse(await ejs.renderFile(getTemplatePath('node-ts-express/partials/package.json.ejs'), { projectName }));
41
41
 
42
- if (dbType === 'mongoose') packageJsonContent.dependencies['mongoose'] = '^7.5.0';
42
+ if (dbType === 'mongoose') packageJsonContent.dependencies['mongoose'] = '^7.6.3';
43
43
  if (dbType === 'prisma') {
44
- packageJsonContent.dependencies['@prisma/client'] = '^5.5.2';
45
- packageJsonContent.devDependencies['prisma'] = '^5.5.2';
44
+ packageJsonContent.dependencies['@prisma/client'] = '^5.6.0';
45
+ packageJsonContent.devDependencies['prisma'] = '^5.6.0';
46
46
  packageJsonContent.prisma = { seed: `ts-node ${addSeeder ? 'scripts/seeder.ts' : 'prisma/seed.ts'}` };
47
47
  }
48
48
  if (addAuth) {
49
49
  packageJsonContent.dependencies['jsonwebtoken'] = '^9.0.2';
50
50
  packageJsonContent.dependencies['bcryptjs'] = '^2.4.3';
51
- packageJsonContent.devDependencies['@types/jsonwebtoken'] = '^9.0.2';
52
- packageJsonContent.devDependencies['@types/bcryptjs'] = '^2.4.2';
51
+ packageJsonContent.devDependencies['@types/jsonwebtoken'] = '^9.0.5';
52
+ packageJsonContent.devDependencies['@types/bcryptjs'] = '^2.4.6';
53
53
  }
54
54
  if (addSeeder) {
55
- packageJsonContent.devDependencies['@faker-js/faker'] = '^8.2.0';
56
- packageJsonContent.dependencies['chalk'] = '^4.1.2';
55
+ packageJsonContent.devDependencies['@faker-js/faker'] = '^8.3.1';
56
+ if (!packageJsonContent.dependencies['chalk']) packageJsonContent.dependencies['chalk'] = '^4.1.2';
57
57
  packageJsonContent.scripts['seed'] = `ts-node scripts/seeder.ts`;
58
58
  packageJsonContent.scripts['destroy'] = `ts-node scripts/seeder.ts -d`;
59
59
  }
60
60
  if (extraFeatures.includes('testing')) {
61
61
  packageJsonContent.devDependencies['jest'] = '^29.7.0';
62
62
  packageJsonContent.devDependencies['supertest'] = '^6.3.3';
63
- packageJsonContent.devDependencies['@types/jest'] = '^29.5.5';
64
- packageJsonContent.devDependencies['@types/supertest'] = '^2.0.14';
63
+ packageJsonContent.devDependencies['@types/jest'] = '^29.5.10';
64
+ packageJsonContent.devDependencies['@types/supertest'] = '^2.0.16';
65
65
  packageJsonContent.devDependencies['ts-jest'] = '^29.1.1';
66
- packageJsonContent.scripts['test'] = 'jest --detectOpenHandles';
66
+ packageJsonContent.scripts['test'] = 'jest --detectOpenHandles --forceExit';
67
67
  }
68
68
  if (extraFeatures.includes('swagger')) {
69
69
  packageJsonContent.dependencies['swagger-ui-express'] = '^5.0.0';
70
70
  packageJsonContent.dependencies['swagger-jsdoc'] = '^6.2.8';
71
- packageJsonContent.devDependencies['@types/swagger-ui-express'] = '^4.1.4';
71
+ packageJsonContent.devDependencies['@types/swagger-ui-express'] = '^4.1.6';
72
72
  }
73
73
  await fs.writeJson(path.join(projectDir, 'package.json'), packageJsonContent, { spaces: 2 });
74
74
 
@@ -93,9 +93,37 @@ async function generateNodeProject(options) {
93
93
  }
94
94
  }
95
95
 
96
- // --- Step 6: Generate Auth, Seeder, and Extra Features ---
97
- if (addAuth) { /* ... Logic from v4.0 ... */ }
98
- if (addSeeder) { /* ... Logic from v4.0 ... */ }
96
+ // --- Step 6: Generate Authentication Boilerplate ---
97
+ if (addAuth) {
98
+ console.log(chalk.blue(' -> Generating authentication boilerplate...'));
99
+ await fs.ensureDir(path.join(destSrcDir, 'routes'));
100
+ await fs.ensureDir(path.join(destSrcDir, 'middleware'));
101
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.controller.ts.ejs'), path.join(destSrcDir, 'controllers', 'Auth.controller.ts'), {});
102
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.routes.ts.ejs'), path.join(destSrcDir, 'routes', 'Auth.routes.ts'), {});
103
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Auth.middleware.ts.ejs'), path.join(destSrcDir, 'middleware', 'Auth.middleware.ts'), {});
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
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ // --- Step 7: Generate Seeder Script ---
120
+ if (addSeeder) {
121
+ console.log(chalk.blue(' -> Generating database seeder script...'));
122
+ await fs.ensureDir(path.join(projectDir, 'scripts'));
123
+ await renderAndWrite(getTemplatePath('node-ts-express/partials/Seeder.ts.ejs'), path.join(projectDir, 'scripts', 'seeder.ts'), { projectName });
124
+ }
125
+
126
+ // --- Step 8: Generate Extra Features ---
99
127
  if (extraFeatures.includes('docker')) {
100
128
  console.log(chalk.blue(' -> Generating Docker files...'));
101
129
  await renderAndWrite(getTemplatePath('node-ts-express/partials/Dockerfile.ejs'), path.join(projectDir, 'Dockerfile'), { dbType, port });
@@ -108,13 +136,13 @@ async function generateNodeProject(options) {
108
136
  }
109
137
  if (extraFeatures.includes('testing')) {
110
138
  console.log(chalk.blue(' -> Generating testing boilerplate...'));
111
- const jestConfig = `module.exports = { preset: 'ts-jest', testEnvironment: 'node' };`;
139
+ const jestConfig = `/** @type {import('ts-jest').JestConfigWithTsJest} */\nmodule.exports = {\n preset: 'ts-jest',\n testEnvironment: 'node',\n verbose: true,\n};`;
112
140
  await fs.writeFile(path.join(projectDir, 'jest.config.js'), jestConfig);
113
141
  await fs.ensureDir(path.join(projectDir, 'src', '__tests__'));
114
142
  await renderAndWrite(getTemplatePath('node-ts-express/partials/App.test.ts.ejs'), path.join(projectDir, 'src', '__tests__', 'api.test.ts'), { addAuth });
115
143
  }
116
144
 
117
- // --- Step 7: Generate Main Route File & Inject Logic into Server ---
145
+ // --- Step 9: Generate Main Route File & Inject Logic into Server ---
118
146
  await renderAndWrite(getTemplatePath('node-ts-express/partials/routes.ts.ejs'), path.join(destSrcDir, 'routes.ts'), { endpoints, addAuth, dbType });
119
147
 
120
148
  let serverFileContent = await fs.readFile(path.join(destSrcDir, 'server.ts'), 'utf-8');
@@ -134,23 +162,31 @@ async function generateNodeProject(options) {
134
162
 
135
163
  serverFileContent = serverFileContent
136
164
  .replace("dotenv.config();", `dotenv.config();${dbConnectionCode}`)
137
- .replace('// INJECT:ROUTES', `${authRoutesInjector}import apiRoutes from './routes';\napp.use('/api', apiRoutes);\n${swaggerInjector}`);
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`);
138
169
  await fs.writeFile(path.join(destSrcDir, 'server.ts'), serverFileContent);
139
170
 
140
- // --- Step 8: Install Dependencies & Run Post-install Scripts ---
171
+ // --- Step 10: Install Dependencies & Run Post-install Scripts ---
141
172
  console.log(chalk.magenta(' -> Installing dependencies... This may take a moment.'));
142
173
  await execa('npm', ['install'], { cwd: projectDir });
143
174
  if (dbType === 'prisma') {
144
175
  console.log(chalk.blue(' -> Running `prisma generate`...'));
145
176
  await execa('npx', ['prisma', 'generate'], { cwd: projectDir });
146
177
  }
147
-
148
- // --- Step 9: Generate Final Files (.env.example) ---
178
+
179
+ // --- Step 11: Generate Final Files (.env.example) ---
149
180
  let envContent = `PORT=${port}\n`;
150
- if (dbType === 'mongoose') envContent += `DATABASE_URL=mongodb://root:example@localhost:27017/${projectName}?authSource=admin\n`;
151
- if (dbType === 'prisma') envContent += `DATABASE_URL="postgresql://user:password@localhost:5432/${projectName}?schema=public"\n`;
152
- if (addAuth) envContent += `JWT_SECRET=your_super_secret_key\n`;
153
- if (extraFeatures.includes('docker')) envContent += `\n# Docker-compose credentials\nDB_USER=user\nDB_PASSWORD=password\nDB_NAME=${projectName}`;
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
+ }
154
190
  await fs.writeFile(path.join(projectDir, '.env.example'), envContent);
155
191
 
156
192
  } catch (error) {
@@ -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,38 @@
1
+ # Auto-generated by create-backlist v6.0
2
+ from fastapi import FastAPI
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ import uvicorn
5
+ import os
6
+
7
+ # Import generated routers
8
+ <% for (const controller of controllers) { %>
9
+ from app.routes import <%= controller.toLowerCase() %>_routes
10
+ <% } %>
11
+
12
+ # Create FastAPI app instance
13
+ app = FastAPI(title="<%= projectName %>")
14
+
15
+ # CORS (Cross-Origin Resource Sharing) Middleware
16
+ # This allows your frontend to communicate with this backend
17
+ app.add_middleware(
18
+ CORSMiddleware,
19
+ allow_origins=["*"], # In production, specify your frontend's origin
20
+ allow_credentials=True,
21
+ allow_methods=["*"],
22
+ allow_headers=["*"],
23
+ )
24
+
25
+ # Root endpoint
26
+ @app.get("/")
27
+ def read_root():
28
+ return {"message": "Welcome to the FastAPI backend generated by create-backlist!"}
29
+
30
+ # Include generated routers
31
+ <% for (const controller of controllers) { %>
32
+ app.include_router(<%= controller.toLowerCase() %>_routes.router, prefix="/api")
33
+ <% } %>
34
+
35
+
36
+ if __name__ == "__main__":
37
+ port = int(os.getenv("PORT", 8000))
38
+ uvicorn.run(app, host="0.0.0.0", port=port)
@@ -0,0 +1,4 @@
1
+ fastapi
2
+ uvicorn[standard]
3
+ pydantic
4
+ # Add other dependencies like 'sqlalchemy', 'psycopg2-binary' if using a database
@@ -0,0 +1,42 @@
1
+ # Auto-generated by create-backlist v6.0
2
+ from fastapi import APIRouter, status
3
+ from pydantic import BaseModel
4
+ from typing import List
5
+
6
+ # --- Pydantic Models for Request/Response Validation ---
7
+ # These are automatically generated based on your frontend code.
8
+
9
+ <% if (schema) { %>
10
+ class <%= modelName %>Base(BaseModel):
11
+ <% for (const field of schema.fields) { %>
12
+ <%= field.name %>: <%= field.type === 'Number' ? 'int' : 'str' %>
13
+ <% } %>
14
+
15
+ class <%= modelName %>(<%= modelName %>Base):
16
+ id: int # Or str, depending on your DB
17
+ # Add other fields like createdAt if needed
18
+ class Config:
19
+ orm_mode = True
20
+ <% } %>
21
+
22
+ # --- Router Definition ---
23
+ router = APIRouter(
24
+ prefix="/<%= modelName.toLowerCase() %>s",
25
+ tags=["<%= modelName %>s"]
26
+ )
27
+
28
+ # --- API Endpoints ---
29
+
30
+ @router.get("/", response_model=List[<%= modelName %>])
31
+ async def get_all_items():
32
+ # TODO: Implement database logic to fetch all items
33
+ # Example: return await db.query(<%= modelName %>).all()
34
+ return [{"id": 1, "name": "Example Item", "email": "example@test.com"}]
35
+
36
+ @router.post("/", status_code=status.HTTP_201_CREATED, response_model=<%= modelName %>)
37
+ async def create_item(item: <%= modelName %>Base):
38
+ # TODO: Implement database logic to create a new item
39
+ # Example: new_item = await db.create(<%= modelName %>, **item.dict())
40
+ return {**item.dict(), "id": 2}
41
+
42
+ # TODO: Add routes for GET (by id), PUT, and DELETE based on detected endpoints.