express-genix 4.6.0 → 4.6.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/README.md CHANGED
@@ -112,6 +112,7 @@ npm run dev
112
112
 
113
113
  ```bash
114
114
  cd my-express-app
115
+ express-genix add infra # Adds Production-Ready Infra (Docker, CI/CD, Nginx, Makefile)
115
116
  express-genix add docker # Adds Dockerfile, docker-compose.yml, .dockerignore
116
117
  express-genix add cicd # Adds GitHub Actions CI workflow
117
118
  express-genix add auth # Adds JWT auth, controllers, routes, middleware
@@ -119,6 +120,23 @@ express-genix add websocket # Adds Socket.io setup
119
120
  express-genix add prisma # Adds Prisma schema, client config, migrations
120
121
  ```
121
122
 
123
+ ## Interactive Resource Scaffolding
124
+
125
+ Generate a complete resource (Controller, Service, and Routes) inside an existing Express Genix project.
126
+
127
+ ```bash
128
+ cd my-express-app
129
+ express-genix generate product
130
+ # or use the alias
131
+ express-genix g product
132
+ ```
133
+
134
+ This command automatically:
135
+ 1. Creates `src/controllers/productController.js` (with CRUD operations)
136
+ 2. Creates `src/services/productService.js` (with business logic structure)
137
+ 3. Creates `src/routes/productRoutes.js` (with Swagger annotations and auth middleware if enabled)
138
+ 4. **Auto-injects** the new route into `src/routes/index.js` (`router.use('/products', productRoutes);`)
139
+
122
140
  ### Generate from natural language (AI)
123
141
 
124
142
  Requires `OPENAI_API_KEY` or `ANTHROPIC_API_KEY` in your environment.
package/index.js CHANGED
@@ -267,7 +267,7 @@ Configuration:
267
267
  .command('add <feature>')
268
268
  .description('Add a feature to an existing express-genix project')
269
269
  .action(async (feature) => {
270
- const supportedFeatures = ['auth', 'websocket', 'docker', 'cicd', 'prisma'];
270
+ const supportedFeatures = ['auth', 'websocket', 'docker', 'cicd', 'prisma', 'infra'];
271
271
  if (!supportedFeatures.includes(feature)) {
272
272
  console.error(`āŒ Unknown feature: "${feature}"`);
273
273
  console.log(`Supported features: ${supportedFeatures.join(', ')}`);
@@ -296,6 +296,21 @@ Configuration:
296
296
 
297
297
 
298
298
 
299
+ program
300
+ .command('generate <resource>')
301
+ .alias('g')
302
+ .description('Generate a new resource (controller, service, routes)')
303
+ .action(async (resource) => {
304
+ try {
305
+ const { generateResource } = require('./lib/scaffold');
306
+ await generateResource(resource, process.cwd());
307
+ console.log(`\nšŸŽ‰ Resource "${resource}" generated successfully!`);
308
+ } catch (error) {
309
+ console.error(`\nāŒ Failed to generate resource: ${error.message}`);
310
+ process.exit(1);
311
+ }
312
+ });
313
+
299
314
  await program.parseAsync(process.argv);
300
315
  }
301
316
 
package/lib/features.js CHANGED
@@ -11,6 +11,7 @@ const addFeature = async (feature, projectDir) => {
11
11
  const handlers = {
12
12
  docker: () => addDocker(projectDir, templatesDir, pkg),
13
13
  cicd: () => addCicd(projectDir, templatesDir, pkg),
14
+ infra: () => addInfra(projectDir, templatesDir, pkg),
14
15
  auth: () => addAuth(projectDir, templatesDir, pkg),
15
16
  websocket: () => addWebsocket(projectDir, templatesDir, pkg),
16
17
  prisma: () => addPrisma(projectDir, templatesDir, pkg),
@@ -62,6 +63,47 @@ const addCicd = (projectDir, templatesDir, pkg) => {
62
63
  console.log(' Created .github/workflows/ci.yml');
63
64
  };
64
65
 
66
+ const addInfra = (projectDir, templatesDir, pkg) => {
67
+ const config = inferConfig(projectDir, pkg);
68
+ config.hasNginx = true; // Enable Nginx in docker-compose
69
+
70
+ // 1. Add Docker files
71
+ const dockerFiles = [
72
+ { template: 'core/Dockerfile.ejs', output: 'Dockerfile' },
73
+ { template: 'core/docker-compose.yml.ejs', output: 'docker-compose.yml' },
74
+ { template: 'core/dockerignore.ejs', output: '.dockerignore' },
75
+ ];
76
+
77
+ for (const file of dockerFiles) {
78
+ const content = renderTemplate(templatesDir, file.template, config);
79
+ fs.writeFileSync(path.join(projectDir, file.output), content);
80
+ }
81
+
82
+ // 2. Add CI/CD files
83
+ const workflowDir = path.join(projectDir, '.github', 'workflows');
84
+ fs.mkdirSync(workflowDir, { recursive: true });
85
+ const ciContent = renderTemplate(templatesDir, 'cicd/github-actions.yml.ejs', config);
86
+ fs.writeFileSync(path.join(workflowDir, 'ci.yml'), ciContent);
87
+
88
+ // 3. Add Infra specific files (Makefile, Nginx)
89
+ const infraFiles = [
90
+ { template: 'infra/Makefile.ejs', output: 'Makefile' },
91
+ { template: 'infra/nginx.conf.ejs', output: 'nginx/nginx.conf' },
92
+ ];
93
+
94
+ for (const file of infraFiles) {
95
+ const outputPath = path.join(projectDir, file.output);
96
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
97
+ const content = renderTemplate(templatesDir, file.template, config);
98
+ fs.writeFileSync(outputPath, content);
99
+ }
100
+
101
+ console.log(' āœ… Production-Ready Infrastructure added!');
102
+ console.log(' Created Dockerfile, docker-compose.yml, .dockerignore');
103
+ console.log(' Created .github/workflows/ci.yml');
104
+ console.log(' Created Makefile and nginx/nginx.conf');
105
+ };
106
+
65
107
  const addAuth = (projectDir, templatesDir, pkg) => {
66
108
  const config = inferConfig(projectDir, pkg);
67
109
 
@@ -0,0 +1,165 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const ejs = require('ejs');
4
+
5
+ /**
6
+ * Capitalize first letter
7
+ */
8
+ const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
9
+
10
+ /**
11
+ * Convert to camelCase
12
+ */
13
+ const toCamelCase = (str) => {
14
+ return str.replace(/[-_](.)/g, (_, c) => c.toUpperCase());
15
+ };
16
+
17
+ /**
18
+ * Convert to PascalCase
19
+ */
20
+ const toPascalCase = (str) => {
21
+ const camel = toCamelCase(str);
22
+ return capitalize(camel);
23
+ };
24
+
25
+ /**
26
+ * Simple pluralize (adds 's' if not ending in 's')
27
+ */
28
+ const pluralize = (str) => {
29
+ if (str.endsWith('s')) return str;
30
+ if (str.endsWith('y')) return str.slice(0, -1) + 'ies';
31
+ return str + 's';
32
+ };
33
+
34
+ /**
35
+ * Generate a resource (controller, service, route)
36
+ */
37
+ const generateResource = async (resourceName, projectDir) => {
38
+ const packageJsonPath = path.join(projectDir, 'package.json');
39
+
40
+ if (!fs.existsSync(packageJsonPath)) {
41
+ throw new Error('No package.json found. Run this command from your express-genix project root.');
42
+ }
43
+
44
+ const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
45
+
46
+ // Check if it's an express-genix project (or at least has the structure)
47
+ const srcDir = path.join(projectDir, 'src');
48
+ if (!fs.existsSync(srcDir)) {
49
+ throw new Error('No "src" directory found. Are you in an express-genix project?');
50
+ }
51
+
52
+ // Determine features from package.json dependencies
53
+ const hasAuth = !!pkg.dependencies['jsonwebtoken'];
54
+
55
+ const nameCamel = toCamelCase(resourceName);
56
+ const namePascal = toPascalCase(resourceName);
57
+ const namePlural = pluralize(nameCamel);
58
+
59
+ const templateData = {
60
+ resourceName: resourceName.toLowerCase(),
61
+ resourceNameCamel: nameCamel,
62
+ resourceNamePascal: namePascal,
63
+ resourceNamePlural: namePlural,
64
+ hasAuth,
65
+ };
66
+
67
+ const templatesDir = path.join(__dirname, '..', 'templates', 'scaffold');
68
+
69
+ const filesToGenerate = [
70
+ {
71
+ template: 'controller.js.ejs',
72
+ dest: path.join(srcDir, 'controllers', `${nameCamel}Controller.js`),
73
+ },
74
+ {
75
+ template: 'service.js.ejs',
76
+ dest: path.join(srcDir, 'services', `${nameCamel}Service.js`),
77
+ },
78
+ {
79
+ template: 'route.js.ejs',
80
+ dest: path.join(srcDir, 'routes', `${nameCamel}Routes.js`),
81
+ },
82
+ ];
83
+
84
+ console.log(`\nGenerating resource: ${namePascal}...`);
85
+
86
+ for (const file of filesToGenerate) {
87
+ if (fs.existsSync(file.dest)) {
88
+ console.log(`āš ļø Skipping ${path.basename(file.dest)} (already exists)`);
89
+ continue;
90
+ }
91
+
92
+ const templatePath = path.join(templatesDir, file.template);
93
+ const templateContent = fs.readFileSync(templatePath, 'utf8');
94
+ const rendered = ejs.render(templateContent, templateData);
95
+
96
+ // Ensure directory exists
97
+ fs.mkdirSync(path.dirname(file.dest), { recursive: true });
98
+
99
+ fs.writeFileSync(file.dest, rendered);
100
+ console.log(`āœ… Created ${path.relative(projectDir, file.dest)}`);
101
+ }
102
+
103
+ // Inject route into src/routes/index.js
104
+ injectRoute(srcDir, nameCamel, namePlural);
105
+ };
106
+
107
+ /**
108
+ * Inject the new route into src/routes/index.js
109
+ */
110
+ const injectRoute = (srcDir, nameCamel, namePlural) => {
111
+ const routesIndexPath = path.join(srcDir, 'routes', 'index.js');
112
+
113
+ if (!fs.existsSync(routesIndexPath)) {
114
+ console.log(`āš ļø Could not find src/routes/index.js to inject the route.`);
115
+ return;
116
+ }
117
+
118
+ let content = fs.readFileSync(routesIndexPath, 'utf8');
119
+
120
+ const requireStatement = `const ${nameCamel}Routes = require('./${nameCamel}Routes');`;
121
+ const useStatement = `router.use('/${namePlural}', ${nameCamel}Routes);`;
122
+
123
+ if (content.includes(requireStatement) || content.includes(useStatement)) {
124
+ console.log(`āš ļø Route already injected in src/routes/index.js`);
125
+ return;
126
+ }
127
+
128
+ // Find the last require statement
129
+ const requireRegex = /const .* = require\('.*'\);/g;
130
+ let lastRequireMatch;
131
+ let match;
132
+ while ((match = requireRegex.exec(content)) !== null) {
133
+ lastRequireMatch = match;
134
+ }
135
+
136
+ if (lastRequireMatch) {
137
+ const insertPos = lastRequireMatch.index + lastRequireMatch[0].length;
138
+ content = content.slice(0, insertPos) + '\n' + requireStatement + content.slice(insertPos);
139
+ } else {
140
+ // Fallback: put it after express require
141
+ content = content.replace(/const express = require\('express'\);/, `const express = require('express');\n${requireStatement}`);
142
+ }
143
+
144
+ // Find the last router.use statement
145
+ const useRegex = /router\.use\('.*', .*\);/g;
146
+ let lastUseMatch;
147
+ while ((match = useRegex.exec(content)) !== null) {
148
+ lastUseMatch = match;
149
+ }
150
+
151
+ if (lastUseMatch) {
152
+ const insertPos = lastUseMatch.index + lastUseMatch[0].length;
153
+ content = content.slice(0, insertPos) + '\n' + useStatement + content.slice(insertPos);
154
+ } else {
155
+ // Fallback: put it before router.get('/', ...)
156
+ content = content.replace(/router\.get\('\/'/, `${useStatement}\n\nrouter.get('/'`);
157
+ }
158
+
159
+ fs.writeFileSync(routesIndexPath, content);
160
+ console.log(`āœ… Injected /${namePlural} route into src/routes/index.js`);
161
+ };
162
+
163
+ module.exports = {
164
+ generateResource,
165
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "express-genix",
3
- "version": "4.6.0",
3
+ "version": "4.6.1",
4
4
  "description": "Production-grade CLI to generate Express apps with JWT, RBAC, GraphQL, TypeScript, Prisma, MongoDB, PostgreSQL, file uploads, email, background jobs, and more",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -1,11 +1,24 @@
1
1
  # templates/core/docker-compose.yml.ejs
2
2
  version: '3.8'
3
3
 
4
- services:
4
+ services:<% if (typeof hasNginx !== 'undefined' && hasNginx) { %>
5
+ nginx:
6
+ image: nginx:alpine
7
+ ports:
8
+ - "80:80"
9
+ volumes:
10
+ - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
11
+ depends_on:
12
+ - app
13
+ restart: unless-stopped
14
+ networks:
15
+ - app-network
16
+ <% } %>
5
17
  app:
6
18
  build: .
7
- ports:
8
- - "3000:3000"
19
+ ports:<% if (typeof hasNginx !== 'undefined' && hasNginx) { %>
20
+ - "3000" # Exposed to Nginx, not host directly<% } else { %>
21
+ - "3000:3000"<% } %>
9
22
  environment:
10
23
  - NODE_ENV=production
11
24
  env_file:
@@ -0,0 +1,30 @@
1
+ .PHONY: build up down logs test lint clean
2
+
3
+ # Build the Docker image
4
+ build:
5
+ docker-compose build
6
+
7
+ # Start the application in detached mode
8
+ up:
9
+ docker-compose up -d
10
+
11
+ # Stop the application
12
+ down:
13
+ docker-compose down
14
+
15
+ # View logs
16
+ logs:
17
+ docker-compose logs -f app
18
+
19
+ # Run tests
20
+ test:
21
+ npm test
22
+
23
+ # Run linter
24
+ lint:
25
+ npm run lint
26
+
27
+ # Clean up Docker resources
28
+ clean:
29
+ docker-compose down -v --remove-orphans
30
+ docker system prune -f
@@ -0,0 +1,25 @@
1
+ events {
2
+ worker_connections 1024;
3
+ }
4
+
5
+ http {
6
+ upstream app_servers {
7
+ server app:3000;
8
+ }
9
+
10
+ server {
11
+ listen 80;
12
+ server_name localhost;
13
+
14
+ location / {
15
+ proxy_pass http://app_servers;
16
+ proxy_http_version 1.1;
17
+ proxy_set_header Upgrade $http_upgrade;
18
+ proxy_set_header Connection 'upgrade';
19
+ proxy_set_header Host $host;
20
+ proxy_cache_bypass $http_upgrade;
21
+ proxy_set_header X-Real-IP $remote_addr;
22
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
23
+ }
24
+ }
25
+ }
@@ -0,0 +1,94 @@
1
+ const <%= resourceNameCamel %>Service = require('../services/<%= resourceNameCamel %>Service');
2
+ const { AppError } = require('../utils/errors');
3
+ const { success, created, paginated } = require('../utils/response');
4
+ const { createLogger } = require('../utils/logger');
5
+
6
+ const logger = createLogger('<%= resourceNamePascal %>Controller');
7
+
8
+ const getAll<%= resourceNamePascal %>s = async (req, res, next) => {
9
+ try {
10
+ const page = Math.max(1, parseInt(req.query.page, 10) || 1);
11
+ const limit = Math.min(100, Math.max(1, parseInt(req.query.limit, 10) || 10));
12
+
13
+ logger.info('Fetching <%= resourceNamePlural %>', { page, limit });
14
+ const result = await <%= resourceNameCamel %>Service.getAll<%= resourceNamePascal %>s(page, limit);
15
+
16
+ return paginated(res, result.<%= resourceNamePlural %>, {
17
+ page,
18
+ limit,
19
+ total: result.total,
20
+ totalPages: result.totalPages,
21
+ });
22
+ } catch (error) {
23
+ logger.error('Error fetching <%= resourceNamePlural %>', { error: error.message });
24
+ next(error);
25
+ }
26
+ };
27
+
28
+ const get<%= resourceNamePascal %>ById = async (req, res, next) => {
29
+ try {
30
+ const { id } = req.params;
31
+ const <%= resourceNameCamel %> = await <%= resourceNameCamel %>Service.get<%= resourceNamePascal %>ById(id);
32
+
33
+ if (!<%= resourceNameCamel %>) {
34
+ throw new AppError('<%= resourceNamePascal %> not found', 404);
35
+ }
36
+
37
+ return success(res, <%= resourceNameCamel %>);
38
+ } catch (error) {
39
+ next(error);
40
+ }
41
+ };
42
+
43
+ const create<%= resourceNamePascal %> = async (req, res, next) => {
44
+ try {
45
+ const data = req.body;
46
+
47
+ logger.info('Creating <%= resourceNameCamel %>', { data });
48
+ const <%= resourceNameCamel %> = await <%= resourceNameCamel %>Service.create<%= resourceNamePascal %>(data);
49
+
50
+ return created(res, <%= resourceNameCamel %>);
51
+ } catch (error) {
52
+ next(error);
53
+ }
54
+ };
55
+
56
+ const update<%= resourceNamePascal %> = async (req, res, next) => {
57
+ try {
58
+ const { id } = req.params;
59
+ const data = req.body;
60
+
61
+ const <%= resourceNameCamel %> = await <%= resourceNameCamel %>Service.update<%= resourceNamePascal %>(id, data);
62
+
63
+ if (!<%= resourceNameCamel %>) {
64
+ throw new AppError('<%= resourceNamePascal %> not found', 404);
65
+ }
66
+
67
+ return success(res, <%= resourceNameCamel %>);
68
+ } catch (error) {
69
+ next(error);
70
+ }
71
+ };
72
+
73
+ const delete<%= resourceNamePascal %> = async (req, res, next) => {
74
+ try {
75
+ const { id } = req.params;
76
+ const deleted = await <%= resourceNameCamel %>Service.delete<%= resourceNamePascal %>(id);
77
+
78
+ if (!deleted) {
79
+ throw new AppError('<%= resourceNamePascal %> not found', 404);
80
+ }
81
+
82
+ return success(res, { message: '<%= resourceNamePascal %> deleted successfully' });
83
+ } catch (error) {
84
+ next(error);
85
+ }
86
+ };
87
+
88
+ module.exports = {
89
+ getAll<%= resourceNamePascal %>s,
90
+ get<%= resourceNamePascal %>ById,
91
+ create<%= resourceNamePascal %>,
92
+ update<%= resourceNamePascal %>,
93
+ delete<%= resourceNamePascal %>
94
+ };
@@ -0,0 +1,88 @@
1
+ const express = require('express');
2
+ const <%= resourceNameCamel %>Controller = require('../controllers/<%= resourceNameCamel %>Controller');
3
+ <% if (hasAuth) { %>const { authenticate } = require('../middleware/auth');<% } %>
4
+
5
+ const router = express.Router();
6
+
7
+ <% if (hasAuth) { %>// Apply authentication middleware to all routes
8
+ router.use(authenticate);
9
+ <% } %>
10
+ /**
11
+ * @swagger
12
+ * /<%= resourceNamePlural %>:
13
+ * get:
14
+ * summary: Get all <%= resourceNamePlural %>
15
+ * tags: [<%= resourceNamePascal %>]
16
+ * responses:
17
+ * 200:
18
+ * description: List of <%= resourceNamePlural %>
19
+ */
20
+ router.get('/', <%= resourceNameCamel %>Controller.getAll<%= resourceNamePascal %>s);
21
+
22
+ /**
23
+ * @swagger
24
+ * /<%= resourceNamePlural %>/{id}:
25
+ * get:
26
+ * summary: Get a <%= resourceNameCamel %> by ID
27
+ * tags: [<%= resourceNamePascal %>]
28
+ * parameters:
29
+ * - in: path
30
+ * name: id
31
+ * required: true
32
+ * schema:
33
+ * type: string
34
+ * responses:
35
+ * 200:
36
+ * description: <%= resourceNamePascal %> details
37
+ */
38
+ router.get('/:id', <%= resourceNameCamel %>Controller.get<%= resourceNamePascal %>ById);
39
+
40
+ /**
41
+ * @swagger
42
+ * /<%= resourceNamePlural %>:
43
+ * post:
44
+ * summary: Create a new <%= resourceNameCamel %>
45
+ * tags: [<%= resourceNamePascal %>]
46
+ * responses:
47
+ * 201:
48
+ * description: Created <%= resourceNameCamel %>
49
+ */
50
+ router.post('/', <%= resourceNameCamel %>Controller.create<%= resourceNamePascal %>);
51
+
52
+ /**
53
+ * @swagger
54
+ * /<%= resourceNamePlural %>/{id}:
55
+ * put:
56
+ * summary: Update a <%= resourceNameCamel %>
57
+ * tags: [<%= resourceNamePascal %>]
58
+ * parameters:
59
+ * - in: path
60
+ * name: id
61
+ * required: true
62
+ * schema:
63
+ * type: string
64
+ * responses:
65
+ * 200:
66
+ * description: Updated <%= resourceNameCamel %>
67
+ */
68
+ router.put('/:id', <%= resourceNameCamel %>Controller.update<%= resourceNamePascal %>);
69
+
70
+ /**
71
+ * @swagger
72
+ * /<%= resourceNamePlural %>/{id}:
73
+ * delete:
74
+ * summary: Delete a <%= resourceNameCamel %>
75
+ * tags: [<%= resourceNamePascal %>]
76
+ * parameters:
77
+ * - in: path
78
+ * name: id
79
+ * required: true
80
+ * schema:
81
+ * type: string
82
+ * responses:
83
+ * 200:
84
+ * description: <%= resourceNamePascal %> deleted successfully
85
+ */
86
+ router.delete('/:id', <%= resourceNameCamel %>Controller.delete<%= resourceNamePascal %>);
87
+
88
+ module.exports = router;
@@ -0,0 +1,77 @@
1
+ const { createLogger } = require('../utils/logger');
2
+
3
+ const logger = createLogger('<%= resourceNamePascal %>Service');
4
+
5
+ /**
6
+ * Get all <%= resourceNamePlural %> with pagination
7
+ */
8
+ const getAll<%= resourceNamePascal %>s = async (page = 1, limit = 10) => {
9
+ logger.info('Fetching all <%= resourceNamePlural %>', { page, limit });
10
+
11
+ // TODO: Implement database logic here
12
+ // Example (Prisma): return prisma.<%= resourceNameCamel %>.findMany({ skip: (page - 1) * limit, take: limit });
13
+ // Example (Mongoose): return <%= resourceNamePascal %>.find().skip((page - 1) * limit).limit(limit);
14
+
15
+ return {
16
+ <%= resourceNamePlural %>: [],
17
+ total: 0,
18
+ totalPages: 0,
19
+ currentPage: page,
20
+ };
21
+ };
22
+
23
+ /**
24
+ * Get <%= resourceNameCamel %> by ID
25
+ */
26
+ const get<%= resourceNamePascal %>ById = async (id) => {
27
+ logger.info('Fetching <%= resourceNameCamel %> by ID', { id });
28
+
29
+ // TODO: Implement database logic here
30
+ // Example (Prisma): return prisma.<%= resourceNameCamel %>.findUnique({ where: { id } });
31
+
32
+ return null;
33
+ };
34
+
35
+ /**
36
+ * Create new <%= resourceNameCamel %>
37
+ */
38
+ const create<%= resourceNamePascal %> = async (data) => {
39
+ logger.info('Creating new <%= resourceNameCamel %>');
40
+
41
+ // TODO: Implement database logic here
42
+ // Example (Prisma): return prisma.<%= resourceNameCamel %>.create({ data });
43
+
44
+ return { id: 'new-id', ...data };
45
+ };
46
+
47
+ /**
48
+ * Update <%= resourceNameCamel %> by ID
49
+ */
50
+ const update<%= resourceNamePascal %> = async (id, data) => {
51
+ logger.info('Updating <%= resourceNameCamel %>', { id });
52
+
53
+ // TODO: Implement database logic here
54
+ // Example (Prisma): return prisma.<%= resourceNameCamel %>.update({ where: { id }, data });
55
+
56
+ return { id, ...data };
57
+ };
58
+
59
+ /**
60
+ * Delete <%= resourceNameCamel %> by ID
61
+ */
62
+ const delete<%= resourceNamePascal %> = async (id) => {
63
+ logger.info('Deleting <%= resourceNameCamel %>', { id });
64
+
65
+ // TODO: Implement database logic here
66
+ // Example (Prisma): await prisma.<%= resourceNameCamel %>.delete({ where: { id } });
67
+
68
+ return true;
69
+ };
70
+
71
+ module.exports = {
72
+ getAll<%= resourceNamePascal %>s,
73
+ get<%= resourceNamePascal %>ById,
74
+ create<%= resourceNamePascal %>,
75
+ update<%= resourceNamePascal %>,
76
+ delete<%= resourceNamePascal %>,
77
+ };