create-backlist 1.3.3 β†’ 2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2025] [W.A.H.ISHAN]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # πŸš€ Create Backlist
2
+
3
+ [![NPM Version](https://img.shields.io/npm/v/create-backlist.svg)](https://www.npmjs.com/package/create-backlist)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ Tired of manually creating backend boilerplate every time you build a frontend? **`create-backlist`** is an intelligent CLI tool that analyzes your frontend project and automatically generates a backend with all the necessary routes and controllers, saving you hours of repetitive work.
7
+
8
+ It's not just another scaffolder; it's a **context-aware, dynamic code generator** that builds a backend tailor-made for your frontend's specific API needs.
9
+
10
+ ![Demo GIF (Optional: Add a GIF of your tool in action here)](link-to-your-demo-gif.gif)
11
+
12
+ ## ✨ Key Features
13
+
14
+ - **πŸ€– Intelligent Code Analysis:** Scans your frontend codebase (React, Vue, etc.) using Abstract Syntax Trees (ASTs) to detect API calls (`fetch` requests).
15
+ - **🌐 Multi-Language Support:** Generate a backend in your preferred stack.
16
+ - βœ… **Currently Supports:**
17
+ - Node.js (with TypeScript & Express)
18
+ - C# (with ASP.NET Core Web API)
19
+ - ⏳ **Coming Soon:**
20
+ - Python (with FastAPI)
21
+ - Java (with Spring Boot)
22
+ - **⚑️ Fully Automated:** A single command handles everything from project scaffolding to dependency installation.
23
+ - **πŸ”§ Zero-Configuration:** No complex config files needed. Just run the command and answer a few simple questions.
24
+ - **🧼 Clean Code Generation:** Creates a well-structured backend, ready for you to implement your business logic.
25
+
26
+ ## πŸ“¦ Installation & Usage
27
+
28
+ No global installation needed! Just run this command inside your existing frontend project's root directory:
29
+
30
+ ```bash
31
+ npm create backlist@latest
32
+ ```
33
+
34
+ The tool will then guide you through an interactive setup process:
35
+
36
+ 1. **Enter a name for your backend directory:** (default: `backend`)
37
+ 2. **Select the backend stack:** (e.g., `Node.js (TypeScript, Express)`)
38
+ 3. **Enter the path to your frontend `src` directory:** (default: `src`)
39
+
40
+ That's it! The tool will analyze your code, generate the backend in a new directory, and install all the necessary dependencies.
41
+
42
+ ### Example
43
+
44
+ Let's say your frontend has this API call:
45
+
46
+ ```javascript
47
+ // in your React component
48
+ fetch('/api/products/123', { method: 'PUT' });
49
+ ```
50
+
51
+ `create-backlist` will automatically generate a backend with a `products` controller and a `PUT` route for `products/:id`.
52
+
53
+ ## πŸ’‘ How It's Different from Other Tools
54
+
55
+ | Tool | Approach | Use Case |
56
+ | --------------------- | -------------------------------------- | ----------------------------------------------------------- |
57
+ | **Express Generator** | Static Scaffolding | Quickly start a *new, empty* Express project. |
58
+ | **NestJS CLI** | Static Scaffolding & Code Generation | Start a *new, structured* NestJS project and add parts manually. |
59
+ | **`create-backlist`** | **Dynamic & Context-Aware Scaffolding** | Generate a backend that is **tailor-made** for an *existing* frontend. |
60
+
61
+ While traditional generators give you a blank canvas, `create-backlist` looks at your finished painting (the frontend) and builds the perfect frame (the backend) for it.
62
+
63
+ ## πŸ—ΊοΈ Roadmap
64
+
65
+ `create-backlist` is actively being developed. Here are some of the features planned for future releases:
66
+
67
+ - [ ] **Python Support:** Generate a backend using FastAPI.
68
+ - [ ] **Java Support:** Generate a backend using Spring Boot.
69
+ - [ ] **Database Model Generation:** Automatically create basic database models (e.g., Mongoose, Prisma) based on `POST`/`PUT` request bodies.
70
+ - [ ] **Authentication Boilerplate:** Add an option to generate basic JWT-based authentication routes (`/login`, `/register`).
71
+
72
+ Have an idea for a new feature? Feel free to [open an issue](https://github.com/WAH-ISHAN/create-backlist/issues) on GitHub!
73
+
74
+ ## πŸ› οΈ Contributing
75
+
76
+ Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
77
+
78
+ 1. Fork the Project
79
+ 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`)
80
+ 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`)
81
+ 4. Push to the Branch (`git push origin feature/AmazingFeature`)
82
+ 5. Open a Pull Request
83
+
84
+ ## πŸ“„ License
85
+
86
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
87
+
88
+ ## πŸ™ Acknowledgements
89
+
90
+ - **Babel** for the amazing AST parser.
91
+ - **Inquirer.js** for the interactive CLI prompts.
92
+ - **fs-extra** for making file system operations a breeze.
93
+ - **Google's Gemini** for assistance with brainstorming and debugging.
94
+
95
+ ---
96
+
97
+ _Built with ❀️ by [W.A.H. ISHAN](https://github.com/WAH-ISHAN)._
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-backlist",
3
- "version": "1.3.3",
3
+ "version": "2.0.1",
4
4
  "description": "An advanced, multi-language backend generator based on frontend analysis.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/analyzer.js CHANGED
@@ -3,12 +3,24 @@ const { glob } = require('glob');
3
3
  const parser = require('@babel/parser');
4
4
  const traverse = require('@babel/traverse').default;
5
5
 
6
+ /**
7
+ * Converts a string to TitleCase, which is suitable for model and controller names.
8
+ * e.g., 'user-orders' -> 'UserOrders'
9
+ * @param {string} str The input string.
10
+ * @returns {string} The TitleCased string.
11
+ */
6
12
  function toTitleCase(str) {
7
13
  if (!str) return 'Default';
8
- return str.replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase())
14
+ return str.replace(/-_(\w)/g, g => g[1].toUpperCase()) // handle snake_case and kebab-case
15
+ .replace(/\w\S*/g, txt => txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase())
9
16
  .replace(/[^a-zA-Z0-9]/g, '');
10
17
  }
11
18
 
19
+ /**
20
+ * Analyzes frontend source files to find API endpoints and their details.
21
+ * @param {string} srcPath The path to the frontend source directory.
22
+ * @returns {Promise<Array<object>>} A promise that resolves to an array of endpoint objects.
23
+ */
12
24
  async function analyzeFrontend(srcPath) {
13
25
  if (!fs.existsSync(srcPath)) {
14
26
  throw new Error(`The source directory '${srcPath}' does not exist.`);
@@ -21,39 +33,92 @@ async function analyzeFrontend(srcPath) {
21
33
  const code = await fs.readFile(file, 'utf-8');
22
34
  try {
23
35
  const ast = parser.parse(code, { sourceType: 'module', plugins: ['jsx', 'typescript'] });
36
+
24
37
  traverse(ast, {
25
38
  CallExpression(path) {
39
+ // We are only interested in 'fetch' calls
26
40
  if (path.node.callee.name !== 'fetch') return;
41
+
27
42
  const urlNode = path.node.arguments[0];
28
43
 
29
44
  let urlValue;
30
45
  if (urlNode.type === 'StringLiteral') {
31
46
  urlValue = urlNode.value;
32
47
  } else if (urlNode.type === 'TemplateLiteral' && urlNode.quasis.length > 0) {
33
- urlValue = urlNode.quasis.map(q => q.value.raw).join('{id}');
48
+ // Reconstruct path for dynamic URLs like `/api/users/${id}` -> `/api/users/{id}`
49
+ urlValue = urlNode.quasis.map((q, i) => {
50
+ return q.value.raw + (urlNode.expressions[i] ? `{${urlNode.expressions[i].name || 'id'}}` : '');
51
+ }).join('');
34
52
  }
35
53
 
54
+ // Only process API calls that start with '/api/'
36
55
  if (!urlValue || !urlValue.startsWith('/api/')) return;
37
56
 
38
57
  let method = 'GET';
58
+ let schemaFields = null;
39
59
 
40
60
  const optionsNode = path.node.arguments[1];
41
61
  if (optionsNode && optionsNode.type === 'ObjectExpression') {
62
+ // Find the HTTP method
42
63
  const methodProp = optionsNode.properties.find(p => p.key.name === 'method');
43
64
  if (methodProp && methodProp.value.type === 'StringLiteral') {
44
65
  method = methodProp.value.value.toUpperCase();
45
66
  }
67
+
68
+ // --- NEW LOGIC: Analyze the 'body' for POST/PUT requests ---
69
+ if (method === 'POST' || method === 'PUT') {
70
+ const bodyProp = optionsNode.properties.find(p => p.key.name === 'body');
71
+
72
+ // Check if body is wrapped in JSON.stringify
73
+ if (bodyProp && bodyProp.value.callee && bodyProp.value.callee.name === 'JSON.stringify') {
74
+ const dataObjectNode = bodyProp.value.arguments[0];
75
+
76
+ // This is a simplified analysis assuming the object is defined inline.
77
+ // A more robust solution would trace variables back to their definition.
78
+ if (dataObjectNode.type === 'ObjectExpression') {
79
+ schemaFields = {};
80
+ dataObjectNode.properties.forEach(prop => {
81
+ const key = prop.key.name;
82
+ const valueNode = prop.value;
83
+
84
+ // Infer Mongoose schema type based on the value's literal type
85
+ if (valueNode.type === 'StringLiteral') {
86
+ schemaFields[key] = 'String';
87
+ } else if (valueNode.type === 'NumericLiteral') {
88
+ schemaFields[key] = 'Number';
89
+ } else if (valueNode.type === 'BooleanLiteral') {
90
+ schemaFields[key] = 'Boolean';
91
+ } else {
92
+ // Default to String if the type is complex or a variable
93
+ schemaFields[key] = 'String';
94
+ }
95
+ });
96
+ }
97
+ }
98
+ }
46
99
  }
47
100
 
101
+ // Generate a clean controller name (e.g., /api/user-orders -> UserOrders)
48
102
  const controllerName = toTitleCase(urlValue.split('/')[2]);
49
103
  const key = `${method}:${urlValue}`;
104
+
105
+ // Avoid adding duplicate endpoints
50
106
  if (!endpoints.has(key)) {
51
- endpoints.set(key, { path: urlValue, method, controllerName });
107
+ endpoints.set(key, {
108
+ path: urlValue,
109
+ method,
110
+ controllerName,
111
+ schemaFields // This will be null for GET/DELETE, and an object for POST/PUT
112
+ });
52
113
  }
53
114
  },
54
115
  });
55
- } catch (e) { /* Ignore parsing errors */ }
116
+ } catch (e) {
117
+ // Ignore files that babel can't parse (e.g., CSS-in-JS files)
118
+ }
56
119
  }
120
+
121
+ // Return all found endpoints as an array
57
122
  return Array.from(endpoints.values());
58
123
  }
59
124
 
@@ -9,7 +9,7 @@ async function generateNodeProject(options) {
9
9
  const { projectDir, projectName, frontendSrcDir } = options;
10
10
 
11
11
  try {
12
- // --- Step 1: Analyze Frontend ---
12
+ // --- Step 1: Analyze Frontend to get Endpoints and Schema Info ---
13
13
  console.log(chalk.blue(' -> Analyzing frontend for API endpoints...'));
14
14
  const endpoints = await analyzeFrontend(frontendSrcDir);
15
15
  if (endpoints.length > 0) {
@@ -18,58 +18,101 @@ async function generateNodeProject(options) {
18
18
  console.log(chalk.yellow(' -> No API endpoints found. A basic project will be created.'));
19
19
  }
20
20
 
21
- // --- Step 2: Scaffold Base Project ---
21
+ // --- Step 2: Identify which Database Models to Generate ---
22
+ const modelsToGenerate = new Map();
23
+ endpoints.forEach(ep => {
24
+ // If an endpoint has schemaFields and a valid controllerName, add it to our map.
25
+ if (ep.schemaFields && ep.controllerName !== 'Default' && !modelsToGenerate.has(ep.controllerName)) {
26
+ modelsToGenerate.set(ep.controllerName, ep.schemaFields);
27
+ }
28
+ });
29
+
30
+ // --- Step 3: Scaffold Base Project Structure & Files ---
22
31
  console.log(chalk.blue(' -> Scaffolding Node.js (Express + TS) project...'));
23
32
 
24
- // Define paths clearly
25
- const baseDir = getTemplatePath('node-ts-express/base');
26
- const serverTemplatePath = path.join(baseDir, 'server.ts');
27
- const tsconfigTemplatePath = path.join(baseDir, 'tsconfig.json');
28
-
33
+ // Create the main source directory
29
34
  const destSrcDir = path.join(projectDir, 'src');
30
- const serverDestPath = path.join(destSrcDir, 'server.ts');
31
- const tsconfigDestPath = path.join(projectDir, 'tsconfig.json');
32
-
33
- // Ensure destination directory exists
34
35
  await fs.ensureDir(destSrcDir);
35
36
 
36
- // Copy base files individually for clarity
37
- await fs.copy(serverTemplatePath, serverDestPath);
38
- await fs.copy(tsconfigTemplatePath, tsconfigDestPath);
39
-
40
- console.log(chalk.gray(' -> Base server.ts copied.'));
37
+ // Copy static base files
38
+ await fs.copy(getTemplatePath('node-ts-express/base/server.ts'), path.join(destSrcDir, 'server.ts'));
39
+ await fs.copy(getTemplatePath('node-ts-express/base/tsconfig.json'), path.join(projectDir, 'tsconfig.json'));
41
40
 
42
- // --- Step 3: Generate Dynamic Files ---
43
- await renderAndWrite(
44
- getTemplatePath('node-ts-express/partials/package.json.ejs'),
45
- path.join(projectDir, 'package.json'),
46
- { projectName }
41
+ // --- Step 4: Generate Dynamic Files (package.json, Models, Controllers) ---
42
+
43
+ // Prepare package.json content (in memory)
44
+ const packageJsonContent = JSON.parse(
45
+ await ejs.renderFile(getTemplatePath('node-ts-express/partials/package.json.ejs'), { projectName })
47
46
  );
47
+
48
+ // Conditionally add Mongoose if we are generating models
49
+ if (modelsToGenerate.size > 0) {
50
+ console.log(chalk.gray(' -> Preparing to add Mongoose to dependencies...'));
51
+ packageJsonContent.dependencies['mongoose'] = '^7.5.0'; // Use a recent, stable version
52
+ }
53
+
54
+ // Write the final package.json to the disk
55
+ await fs.writeJson(path.join(projectDir, 'package.json'), packageJsonContent, { spaces: 2 });
56
+
57
+ // Generate Model and Controller files if any were found
58
+ if (modelsToGenerate.size > 0) {
59
+ console.log(chalk.blue(' -> Generating database models and controllers...'));
60
+ await fs.ensureDir(path.join(destSrcDir, 'models'));
61
+ await fs.ensureDir(path.join(destSrcDir, 'controllers'));
62
+
63
+ for (const [modelName, schema] of modelsToGenerate.entries()) {
64
+ // Generate Model File (e.g., models/User.model.ts)
65
+ await renderAndWrite(
66
+ getTemplatePath('node-ts-express/partials/Model.ts.ejs'),
67
+ path.join(destSrcDir, 'models', `${modelName}.model.ts`),
68
+ { modelName, schema }
69
+ );
70
+ // Generate Controller File (e.g., controllers/User.controller.ts)
71
+ await renderAndWrite(
72
+ getTemplatePath('node-ts-express/partials/Controller.ts.ejs'),
73
+ path.join(destSrcDir, 'controllers', `${modelName}.controller.ts`),
74
+ { modelName }
75
+ );
76
+ }
77
+ }
78
+
79
+ // --- Step 5: Generate the Smart Route File ---
80
+ console.log(chalk.gray(' -> Generating dynamic routes...'));
48
81
  await renderAndWrite(
49
82
  getTemplatePath('node-ts-express/partials/routes.ts.ejs'),
50
83
  path.join(destSrcDir, 'routes.ts'),
51
- { endpoints }
84
+ { endpoints } // Pass all endpoints to the template
52
85
  );
53
86
 
54
- console.log(chalk.gray(' -> package.json and routes.ts generated.'));
55
-
56
- // --- Step 4: Modify the copied server.ts ---
57
- // Check if the file exists before reading
58
- if (!await fs.pathExists(serverDestPath)) {
59
- throw new Error(`Critical error: server.ts was not found at ${serverDestPath} after copy.`);
60
- }
61
-
87
+ // --- Step 6: Inject Routes into the Main Server File ---
88
+ const serverDestPath = path.join(destSrcDir, 'server.ts');
62
89
  let serverFileContent = await fs.readFile(serverDestPath, 'utf-8');
63
- serverFileContent = serverFileContent.replace('// INJECT:ROUTES', `import apiRoutes from './routes';\napp.use(apiRoutes);`);
64
- await fs.writeFile(serverDestPath, serverFileContent);
90
+
91
+ let dbConnectionCode = '';
92
+ if (modelsToGenerate.size > 0) {
93
+ dbConnectionCode = `
94
+ // --- Database Connection ---
95
+ import mongoose from 'mongoose';
96
+ const MONGO_URI = process.env.MONGO_URI || 'mongodb://localhost:27017/${projectName}';
97
+ mongoose.connect(MONGO_URI)
98
+ .then(() => console.log('MongoDB Connected...'))
99
+ .catch(err => console.error('MongoDB Connection Error:', err));
100
+ // -------------------------
101
+ `;
102
+ }
65
103
 
66
- console.log(chalk.gray(' -> server.ts modified successfully.'));
104
+ // Inject DB connection code after dotenv.config() and route loader
105
+ serverFileContent = serverFileContent
106
+ .replace("dotenv.config();", `dotenv.config();\n${dbConnectionCode}`)
107
+ .replace('// INJECT:ROUTES', `import apiRoutes from './routes';\napp.use(apiRoutes);`);
108
+
109
+ await fs.writeFile(serverDestPath, serverFileContent);
67
110
 
68
- // --- Step 5: Install Dependencies ---
69
- console.log(chalk.magenta(' -> Installing dependencies (npm install)...'));
111
+ // --- Step 7: Install All Dependencies at Once ---
112
+ console.log(chalk.magenta(' -> Installing dependencies (npm install)... This might take a moment.'));
70
113
  await execa('npm', ['install'], { cwd: projectDir });
71
114
 
72
- // --- Step 6: Generate README ---
115
+ // --- Step 8: Generate README ---
73
116
  await renderAndWrite(
74
117
  getTemplatePath('node-ts-express/partials/README.md.ejs'),
75
118
  path.join(projectDir, 'README.md'),
@@ -77,7 +120,8 @@ async function generateNodeProject(options) {
77
120
  );
78
121
 
79
122
  } catch (error) {
80
- // Re-throw the error to be caught by the main CLI handler
123
+ // Re-throw the error so it can be caught by the main CLI handler in index.js
124
+ // This allows for centralized error message display and cleanup.
81
125
  throw error;
82
126
  }
83
127
  }
@@ -0,0 +1,57 @@
1
+ // Auto-generated by create-backlist on <%= new Date().toISOString() %>
2
+ import { Request, Response } from 'express';
3
+ import <%= modelName %>, { I<%= modelName %> } from '../models/<%= modelName %>.model';
4
+
5
+ // @desc Create a new <%= modelName %>
6
+ export const create<%= modelName %> = async (req: Request, res: Response) => {
7
+ try {
8
+ const newDoc = new <%= modelName %>(req.body);
9
+ await newDoc.save();
10
+ res.status(201).json(newDoc);
11
+ } catch (error) {
12
+ res.status(500).json({ message: 'Error creating document', error });
13
+ }
14
+ };
15
+
16
+ // @desc Get all <%= modelName %>s
17
+ export const getAll<%= modelName %>s = async (req: Request, res: Response) => {
18
+ try {
19
+ const docs = await <%= modelName %>.find();
20
+ res.status(200).json(docs);
21
+ } catch (error) {
22
+ res.status(500).json({ message: 'Error fetching documents', error });
23
+ }
24
+ };
25
+
26
+ // @desc Get a single <%= modelName %> by ID
27
+ export const get<%= modelName %>ById = async (req: Request, res: Response) => {
28
+ try {
29
+ const doc = await <%= modelName %>.findById(req.params.id);
30
+ if (!doc) return res.status(404).json({ message: 'Document not found' });
31
+ res.status(200).json(doc);
32
+ } catch (error) {
33
+ res.status(500).json({ message: 'Error fetching document', error });
34
+ }
35
+ };
36
+
37
+ // @desc Update a <%= modelName %> by ID
38
+ export const update<%= modelName %>ById = async (req: Request, res: Response) => {
39
+ try {
40
+ const doc = await <%= modelName %>.findByIdAndUpdate(req.params.id, req.body, { new: true, runValidators: true });
41
+ if (!doc) return res.status(404).json({ message: 'Document not found' });
42
+ res.status(200).json(doc);
43
+ } catch (error) {
44
+ res.status(500).json({ message: 'Error updating document', error });
45
+ }
46
+ };
47
+
48
+ // @desc Delete a <%= modelName %> by ID
49
+ export const delete<%= modelName %>ById = async (req: Request, res: Response) => {
50
+ try {
51
+ const doc = await <%= modelName %>.findByIdAndDelete(req.params.id);
52
+ if (!doc) return res.status(404).json({ message: 'Document not found' });
53
+ res.status(200).json({ message: 'Document deleted successfully' });
54
+ } catch (error) {
55
+ res.status(500).json({ message: 'Error deleting document', error });
56
+ }
57
+ };
@@ -0,0 +1,22 @@
1
+ // Auto-generated by create-backlist on <%= new Date().toISOString() %>
2
+ import mongoose, { Schema, Document } from 'mongoose';
3
+
4
+ // Define the interface for the Document
5
+ export interface I<%= modelName %> extends Document {
6
+ <% Object.keys(schema).forEach(key => { %>
7
+ <%= key %>: <%= schema[key].toLowerCase() %>;
8
+ <% }); %>
9
+ }
10
+
11
+ // Define the Mongoose Schema
12
+ const <%= modelName %>Schema: Schema = new Schema({
13
+ <% Object.keys(schema).forEach(key => { %>
14
+ <%= key %>: {
15
+ type: <%= schema[key] %>,
16
+ // TODO: Add 'required', 'unique', etc. here
17
+ },
18
+ <% }); %>
19
+ }, { timestamps: true });
20
+
21
+ // Create and export the Model
22
+ export default mongoose.model<I<%= modelName %>>('<%= modelName %>', <%= modelName %>Schema);
@@ -1,23 +1,60 @@
1
- // Auto-generated by Backlist on <%= new Date().toISOString() %>
1
+ // Auto-generated by create-backlist on <%= new Date().toISOString() %>
2
2
  import { Router, Request, Response } from 'express';
3
+ <%# Create a unique set of controller names from the endpoints array %>
4
+ <% const controllersToImport = new Set(endpoints.map(ep => ep.controllerName).filter(name => name !== 'Default')); %>
5
+
6
+ // Import all the generated controllers
7
+ <% for (const controller of controllersToImport) { %>
8
+ import * as <%= controller %>Controller from '../controllers/<%= controller %>.controller';
9
+ <% } %>
3
10
 
4
11
  const router = Router();
5
12
 
6
- <% if (endpoints.length === 0) { %>
7
- // No API endpoints were detected in the frontend code. Add your routes here.
8
- <% } else { %>
13
+ <%# Loop through each endpoint found by the analyzer %>
9
14
  <% endpoints.forEach(endpoint => { %>
10
- <%# Convert /api/users/{id} to /users/:id %>
11
- <% const expressPath = endpoint.path.replace('/api', '').replace(/{(\w+)}/g, ':$1'); %>
15
+ <%
16
+ // Convert URL path for Express router (e.g., /api/users/{id} -> /users/:id)
17
+ const expressPath = endpoint.path.replace('/api', '').replace(/{(\w+)}/g, ':$1');
18
+ const controllerName = endpoint.controllerName;
19
+ let handlerFunction;
20
+
21
+ // --- LOGIC TO MAP ENDPOINT TO A CRUD CONTROLLER FUNCTION ---
22
+ // This logic assumes a standard RESTful API structure.
23
+
24
+ if (controllerName !== 'Default') {
25
+ if (endpoint.method === 'POST' && !expressPath.includes(':')) {
26
+ // e.g., POST /users -> create a new user
27
+ handlerFunction = `${controllerName}Controller.create${controllerName}`;
28
+ } else if (endpoint.method === 'GET' && !expressPath.includes(':')) {
29
+ // e.g., GET /users -> get all users
30
+ handlerFunction = `${controllerName}Controller.getAll${controllerName}s`;
31
+ } else if (endpoint.method === 'GET' && expressPath.includes(':')) {
32
+ // e.g., GET /users/:id -> get a single user by ID
33
+ handlerFunction = `${controllerName}Controller.get${controllerName}ById`;
34
+ } else if (endpoint.method === 'PUT' && expressPath.includes(':')) {
35
+ // e.g., PUT /users/:id -> update a user by ID
36
+ handlerFunction = `${controllerName}Controller.update${controllerName}ById`;
37
+ } else if (endpoint.method === 'DELETE' && expressPath.includes(':')) {
38
+ // e.g., DELETE /users/:id -> delete a user by ID
39
+ handlerFunction = `${controllerName}Controller.delete${controllerName}ById`;
40
+ }
41
+ }
42
+
43
+ // If no specific CRUD function matches, or if it's a default/unhandled route,
44
+ // create a simple placeholder function.
45
+ if (!handlerFunction) {
46
+ handlerFunction = `(req: Request, res: Response) => {
47
+ // TODO: Implement logic for this custom endpoint
48
+ res.status(501).json({ message: 'Handler not implemented for <%= endpoint.method %> <%= expressPath %>' });
49
+ }`;
50
+ }
51
+ %>
12
52
  /**
13
- * Handler for <%= endpoint.method.toUpperCase() %> <%= endpoint.path %>
53
+ * Route for <%= endpoint.method.toUpperCase() %> <%= endpoint.path %>
54
+ * Mapped to: <%- handlerFunction.includes('=>') ? 'Inline Handler' : handlerFunction %>
14
55
  */
15
- router.<%= endpoint.method.toLowerCase() %>('<%- expressPath %>', (req: Request, res: Response) => {
16
- console.log(`Request received for: ${req.method} ${req.path}`);
17
- // TODO: Implement your logic here.
18
- res.status(200).json({ message: 'Auto-generated response for <%= endpoint.path %>' });
19
- });
56
+ router.<%= endpoint.method.toLowerCase() %>('<%- expressPath %>', <%- handlerFunction %>);
57
+
20
58
  <% }); %>
21
- <% } %>
22
59
 
23
60
  export default router;