create-backlist 6.0.6 → 6.0.8

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.
Files changed (46) hide show
  1. package/bin/index.js +141 -0
  2. package/package.json +4 -10
  3. package/src/analyzer.js +105 -308
  4. package/src/generators/dotnet.js +94 -120
  5. package/src/generators/java.js +109 -157
  6. package/src/generators/node.js +85 -262
  7. package/src/generators/template.js +2 -38
  8. package/src/templates/dotnet/partials/Controller.cs.ejs +14 -7
  9. package/src/templates/java-spring/partials/ApplicationSeeder.java.ejs +2 -7
  10. package/src/templates/java-spring/partials/AuthController.java.ejs +10 -23
  11. package/src/templates/java-spring/partials/Controller.java.ejs +6 -17
  12. package/src/templates/java-spring/partials/Dockerfile.ejs +1 -6
  13. package/src/templates/java-spring/partials/Entity.java.ejs +5 -15
  14. package/src/templates/java-spring/partials/JwtAuthFilter.java.ejs +7 -30
  15. package/src/templates/java-spring/partials/JwtService.java.ejs +10 -38
  16. package/src/templates/java-spring/partials/Repository.java.ejs +1 -10
  17. package/src/templates/java-spring/partials/Service.java.ejs +7 -45
  18. package/src/templates/java-spring/partials/User.java.ejs +4 -17
  19. package/src/templates/java-spring/partials/UserDetailsServiceImpl.java.ejs +4 -10
  20. package/src/templates/java-spring/partials/UserRepository.java.ejs +0 -8
  21. package/src/templates/java-spring/partials/docker-compose.yml.ejs +8 -16
  22. package/src/templates/node-ts-express/base/server.ts +6 -13
  23. package/src/templates/node-ts-express/base/tsconfig.json +3 -13
  24. package/src/templates/node-ts-express/partials/ApiDocs.ts.ejs +7 -17
  25. package/src/templates/node-ts-express/partials/App.test.ts.ejs +26 -49
  26. package/src/templates/node-ts-express/partials/Auth.controller.ts.ejs +62 -56
  27. package/src/templates/node-ts-express/partials/Auth.middleware.ts.ejs +10 -21
  28. package/src/templates/node-ts-express/partials/Controller.ts.ejs +40 -40
  29. package/src/templates/node-ts-express/partials/DbContext.cs.ejs +3 -3
  30. package/src/templates/node-ts-express/partials/Dockerfile.ejs +11 -9
  31. package/src/templates/node-ts-express/partials/Model.cs.ejs +7 -25
  32. package/src/templates/node-ts-express/partials/Model.ts.ejs +12 -20
  33. package/src/templates/node-ts-express/partials/PrismaController.ts.ejs +55 -72
  34. package/src/templates/node-ts-express/partials/PrismaSchema.prisma.ejs +12 -27
  35. package/src/templates/node-ts-express/partials/README.md.ejs +12 -9
  36. package/src/templates/node-ts-express/partials/Seeder.ts.ejs +64 -44
  37. package/src/templates/node-ts-express/partials/docker-compose.yml.ejs +16 -31
  38. package/src/templates/node-ts-express/partials/package.json.ejs +1 -3
  39. package/src/templates/node-ts-express/partials/routes.ts.ejs +24 -35
  40. package/src/utils.js +4 -19
  41. package/bin/backlist.js +0 -227
  42. package/src/db/prisma.ts +0 -4
  43. package/src/scanner/analyzeFrontend.js +0 -146
  44. package/src/scanner/index.js +0 -99
  45. package/src/templates/dotnet/partials/Dto.cs.ejs +0 -8
  46. package/src/templates/node-ts-express/partials/prismaClient.ts.ejs +0 -4
@@ -3,81 +3,87 @@ import { Request, Response } from 'express';
3
3
  import bcrypt from 'bcryptjs';
4
4
  import jwt from 'jsonwebtoken';
5
5
 
6
- <% if (dbType === 'mongoose') { -%>
6
+ <% if (dbType === 'mongoose') { %>
7
7
  import User from '../models/User.model';
8
- <% } else if (dbType === 'prisma') { -%>
9
- import { prisma } from '../db/prisma';
10
- <% } -%>
8
+ <% } else if (dbType === 'prisma') { %>
9
+ import { prisma } from '../server';
10
+ <% } %>
11
11
 
12
- function signToken(res: Response, userId: string) {
13
- const secret = process.env.JWT_SECRET;
14
- if (!secret) {
15
- return res.status(500).json({ message: 'JWT_SECRET is not configured' });
16
- }
17
-
18
- const payload = { user: { id: userId } };
19
-
20
- jwt.sign(payload, secret, { expiresIn: '5h' }, (err, token) => {
21
- if (err || !token) return res.status(500).json({ message: 'Failed to sign token' });
22
- return res.status(201).json({ token });
23
- });
24
- }
25
-
26
- // Register
12
+ // @desc Register a new user
27
13
  export const registerUser = async (req: Request, res: Response) => {
28
- const { name, email, password } = req.body || {};
14
+ const { name, email, password } = req.body;
29
15
 
30
16
  try {
31
- if (!name || !email || !password) {
32
- return res.status(400).json({ message: 'name, email, password are required' });
17
+ <% if (dbType === 'mongoose') { %>
18
+ // Mongoose Logic
19
+ let user = await User.findOne({ email });
20
+ if (user) {
21
+ return res.status(400).json({ message: 'User already exists' });
33
22
  }
34
-
35
- <% if (dbType === 'mongoose') { -%>
36
- const existing = await User.findOne({ email });
37
- if (existing) return res.status(400).json({ message: 'User already exists' });
38
-
39
- const user = new User({ name, email, password }); // password will be hashed by pre-save hook
23
+ user = new User({ name, email, password });
40
24
  await user.save();
41
- return signToken(res, String(user._id));
42
- <% } else if (dbType === 'prisma') { -%>
43
- const existing = await prisma.user.findUnique({ where: { email } });
44
- if (existing) return res.status(400).json({ message: 'User already exists' });
45
-
25
+ <% } else if (dbType === 'prisma') { %>
26
+ // Prisma Logic
27
+ const existingUser = await prisma.user.findUnique({ where: { email } });
28
+ if (existingUser) {
29
+ return res.status(400).json({ message: 'User already exists' });
30
+ }
46
31
  const hashedPassword = await bcrypt.hash(password, 10);
47
- const user = await prisma.user.create({ data: { name, email, password: hashedPassword } });
48
- return signToken(res, String(user.id));
49
- <% } -%>
50
- } catch (error: any) {
51
- return res.status(500).json({ message: 'Server Error', error: error?.message || error });
32
+ const user = await prisma.user.create({
33
+ data: { name, email, password: hashedPassword },
34
+ });
35
+ <% } %>
36
+
37
+ const payload = { user: { id: user.id } };
38
+ jwt.sign(
39
+ payload,
40
+ process.env.JWT_SECRET as string,
41
+ { expiresIn: '5h' },
42
+ (err, token) => {
43
+ if (err) throw err;
44
+ res.status(201).json({ token });
45
+ }
46
+ );
47
+ } catch (error) {
48
+ console.error(error);
49
+ res.status(500).send('Server Error');
52
50
  }
53
51
  };
54
52
 
55
- // Login
53
+ // @desc Authenticate user & get token (Login)
56
54
  export const loginUser = async (req: Request, res: Response) => {
57
- const { email, password } = req.body || {};
55
+ const { email, password } = req.body;
58
56
 
59
57
  try {
60
- if (!email || !password) {
61
- return res.status(400).json({ message: 'email and password are required' });
62
- }
63
-
64
- <% if (dbType === 'mongoose') { -%>
58
+ <% if (dbType === 'mongoose') { %>
59
+ // Mongoose Logic
65
60
  const user = await User.findOne({ email });
66
- <% } else if (dbType === 'prisma') { -%>
61
+ <% } else if (dbType === 'prisma') { %>
62
+ // Prisma Logic
67
63
  const user = await prisma.user.findUnique({ where: { email } });
68
- <% } -%>
64
+ <% } %>
69
65
 
70
- if (!user) return res.status(400).json({ message: 'Invalid Credentials' });
66
+ if (!user) {
67
+ return res.status(400).json({ message: 'Invalid Credentials' });
68
+ }
71
69
 
72
70
  const isMatch = await bcrypt.compare(password, user.password);
73
- if (!isMatch) return res.status(400).json({ message: 'Invalid Credentials' });
71
+ if (!isMatch) {
72
+ return res.status(400).json({ message: 'Invalid Credentials' });
73
+ }
74
74
 
75
- <% if (dbType === 'mongoose') { -%>
76
- return signToken(res, String(user._id));
77
- <% } else { -%>
78
- return signToken(res, String(user.id));
79
- <% } -%>
80
- } catch (error: any) {
81
- return res.status(500).json({ message: 'Server Error', error: error?.message || error });
75
+ const payload = { user: { id: user.id } };
76
+ jwt.sign(
77
+ payload,
78
+ process.env.JWT_SECRET as string,
79
+ { expiresIn: '5h' },
80
+ (err, token) => {
81
+ if (err) throw err;
82
+ res.json({ token });
83
+ }
84
+ );
85
+ } catch (error) {
86
+ console.error(error);
87
+ res.status(500).send('Server Error');
82
88
  }
83
89
  };
@@ -1,38 +1,27 @@
1
- // Auto-generated by create-backlist v5.1 on <%= new Date().toISOString() %>
1
+ // Auto-generated by create-backlist v3.0 on <%= new Date().toISOString() %>
2
2
  import { Request, Response, NextFunction } from 'express';
3
3
  import jwt from 'jsonwebtoken';
4
4
 
5
+ // Extend the default Request interface to include our 'user' property
5
6
  interface AuthRequest extends Request {
6
7
  user?: any;
7
8
  }
8
9
 
9
10
  export const protect = (req: AuthRequest, res: Response, next: NextFunction) => {
10
- const secret = process.env.JWT_SECRET;
11
- if (!secret) return res.status(500).json({ message: 'JWT_SECRET is not configured' });
12
-
13
- // Support both:
14
- // 1) Authorization: Bearer <token>
15
- // 2) x-auth-token: <token>
16
- const authHeader = req.header('authorization');
17
- const xToken = req.header('x-auth-token');
18
-
19
- let token: string | undefined;
20
-
21
- if (authHeader && authHeader.toLowerCase().startsWith('bearer ')) {
22
- token = authHeader.slice(7).trim();
23
- } else if (xToken) {
24
- token = xToken.trim();
25
- }
11
+ // Get token from header
12
+ const token = req.header('x-auth-token');
26
13
 
14
+ // Check if not token
27
15
  if (!token) {
28
16
  return res.status(401).json({ message: 'No token, authorization denied' });
29
17
  }
30
18
 
19
+ // Verify token
31
20
  try {
32
- const decoded: any = jwt.verify(token, secret);
21
+ const decoded = jwt.verify(token, process.env.JWT_SECRET as string);
33
22
  req.user = decoded.user;
34
- return next();
35
- } catch {
36
- return res.status(401).json({ message: 'Token is not valid' });
23
+ next();
24
+ } catch (err) {
25
+ res.status(401).json({ message: 'Token is not valid' });
37
26
  }
38
27
  };
@@ -1,50 +1,50 @@
1
1
  // Auto-generated by create-backlist on <%= new Date().toISOString() %>
2
2
  import { Request, Response } from 'express';
3
3
 
4
- /**
5
- * Controller: <%= controllerName %>
6
- * Generated from frontend AST scan.
7
- */
8
-
9
- <%
10
- function safeAction(name, fallback) {
11
- if (!name) return fallback;
12
- return String(name).replace(/[^a-zA-Z0-9_]/g, '');
13
- }
4
+ export const create<%= modelName %> = async (req: Request, res: Response) => {
5
+ try {
6
+ // TODO: implement create logic
7
+ res.status(201).json({ message: '<%= modelName %> created', data: req.body });
8
+ } catch (error) {
9
+ res.status(500).json({ message: 'Error creating <%= modelName %>', error });
10
+ }
11
+ };
14
12
 
15
- function hasBody(method) {
16
- const m = String(method || 'GET').toUpperCase();
17
- return ['POST', 'PUT', 'PATCH'].includes(m);
18
- }
19
- %>
13
+ export const getAll<%= modelName %>s = async (_req: Request, res: Response) => {
14
+ try {
15
+ // TODO: implement list logic
16
+ res.status(200).json([]);
17
+ } catch (error) {
18
+ res.status(500).json({ message: 'Error fetching <%= modelName %>s', error });
19
+ }
20
+ };
20
21
 
21
- <% if (!Array.isArray(endpoints) || endpoints.length === 0) { %>
22
- export async function health(_req: Request, res: Response) {
23
- return res.status(200).json({ ok: true });
24
- }
25
- <% } %>
22
+ export const get<%= modelName %>ById = async (req: Request, res: Response) => {
23
+ try {
24
+ const { id } = req.params;
25
+ // TODO: implement get by id logic
26
+ res.status(200).json({ id });
27
+ } catch (error) {
28
+ res.status(500).json({ message: 'Error fetching <%= modelName %>', error });
29
+ }
30
+ };
26
31
 
27
- <% (endpoints || []).forEach((ep, i) => {
28
- const method = String(ep.method || 'GET').toUpperCase();
29
- const actionName = safeAction(ep.actionName, `handler${i}`);
30
- const route = ep.route || ep.path || '';
31
- -%>
32
- /**
33
- * <%= method %> <%= route %>
34
- */
35
- export async function <%= actionName %>(req: Request, res: Response) {
32
+ export const update<%= modelName %>ById = async (req: Request, res: Response) => {
36
33
  try {
37
- <% if (hasBody(method)) { -%>
38
- // Body schema (inferred):
39
- // <%- JSON.stringify(ep.schemaFields || ep.requestBody?.fields || {}, null, 2).split('\n').map(l => '// ' + l).join('\n') %>
40
- const payload = req.body;
41
- return res.status(<%= method === 'POST' ? 201 : 200 %>).json({ message: 'TODO: implement', payload });
42
- <% } else { -%>
43
- return res.status(200).json({ message: 'TODO: implement', params: req.params, query: req.query });
44
- <% } -%>
34
+ const { id } = req.params;
35
+ // TODO: implement update logic
36
+ res.status(200).json({ id, ...req.body });
45
37
  } catch (error) {
46
- return res.status(500).json({ message: 'Internal Server Error', error });
38
+ res.status(500).json({ message: 'Error updating <%= modelName %>', error });
47
39
  }
48
- }
40
+ };
49
41
 
50
- <% }) -%>
42
+ export const delete<%= modelName %>ById = async (req: Request, res: Response) => {
43
+ try {
44
+ const { id } = req.params;
45
+ // TODO: implement delete logic
46
+ res.status(204).send();
47
+ } catch (error) {
48
+ res.status(500).json({ message: 'Error deleting <%= modelName %>', error });
49
+ }
50
+ };
@@ -8,8 +8,8 @@ namespace <%= projectName %>.Data
8
8
  {
9
9
  public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
10
10
 
11
- <% modelsToGenerate.forEach(model => { -%>
12
- public DbSet<<%= model.name %>> <%= model.name %> { get; set; } = default!;
13
- <% }); -%>
11
+ <% modelsToGenerate.forEach(model => { %>
12
+ public DbSet<<%= model.name %>> <%= model.name %>s { get; set; }
13
+ <% }); %>
14
14
  }
15
15
  }
@@ -1,31 +1,33 @@
1
- # Auto-generated by create-backlist v5.1
1
+ # Auto-generated by create-backlist v5.0
2
2
 
3
+ # ---- Base Stage ----
3
4
  FROM node:18-alpine AS base
4
5
  WORKDIR /usr/src/app
5
6
  COPY package*.json ./
6
7
 
8
+ # ---- Dependencies Stage ----
7
9
  FROM base AS dependencies
8
- # Use npm ci for reproducible installs
9
- RUN npm ci
10
+ RUN npm install --frozen-lockfile
10
11
 
12
+ # ---- Build Stage ----
11
13
  FROM base AS build
12
14
  COPY --from=dependencies /usr/src/app/node_modules ./node_modules
13
15
  COPY . .
14
- <% if (dbType === 'prisma') { -%>
16
+ <% if (dbType === 'prisma') { %>
15
17
  RUN npx prisma generate
16
- <% } -%>
18
+ <% } %>
17
19
  RUN npm run build
18
20
 
21
+ # ---- Production Stage ----
19
22
  FROM node:18-alpine AS production
20
23
  WORKDIR /usr/src/app
21
- ENV NODE_ENV=production
22
-
23
24
  COPY --from=build /usr/src/app/dist ./dist
24
25
  COPY --from=dependencies /usr/src/app/node_modules ./node_modules
25
26
  COPY package*.json ./
26
- <% if (dbType === 'prisma') { -%>
27
+ <% if (dbType === 'prisma') { %>
28
+ # Copy Prisma schema for runtime
27
29
  COPY prisma ./prisma
28
- <% } -%>
30
+ <% } %>
29
31
 
30
32
  EXPOSE <%= port %>
31
33
  CMD ["node", "dist/server.js"]
@@ -1,34 +1,16 @@
1
1
  // Auto-generated by create-backlist
2
- using System;
3
-
4
2
  namespace <%= projectName %>.Models
5
3
  {
6
4
  public class <%= modelName %>
7
5
  {
8
- public Guid Id { get; set; } = Guid.NewGuid();
9
-
10
- <%
11
- function pascal(s){
12
- return String(s || '')
13
- .replace(/[-_]+(.)/g, (_,c)=>c.toUpperCase())
14
- .replace(/^\w/, c => c.toUpperCase())
15
- .replace(/[^a-zA-Z0-9]/g,'');
16
- }
17
-
18
- function mapType(t){
19
- if (!t) return "string";
20
- const x = String(t).toLowerCase();
21
- if (x === 'number' || x === 'int' || x === 'integer') return "int";
22
- if (x === 'float' || x === 'double') return "double";
23
- if (x === 'boolean' || x === 'bool') return "bool";
24
- if (x === 'date' || x === 'datetime') return "DateTime";
25
- return "string";
26
- }
27
- -%>
6
+ public Guid Id { get; set; }
28
7
 
29
- <% (model.fields || []).forEach(field => { -%>
30
- public <%= mapType(field.type) %> <%= pascal(field.name) %> { get; set; }
31
- <% }); -%>
8
+ <% model.fields.forEach(field => { %>
9
+ <% let csharpType = 'string'; %>
10
+ <% if (field.type === 'Number') csharpType = 'int'; %>
11
+ <% if (field.type === 'Boolean') csharpType = 'bool'; %>
12
+ public <%= csharpType %> <%= field.name %> { get; set; }
13
+ <% }); %>
32
14
 
33
15
  public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
34
16
  public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
@@ -1,30 +1,22 @@
1
1
  // Auto-generated by create-backlist on <%= new Date().toISOString() %>
2
2
  import mongoose, { Schema, Document } from 'mongoose';
3
3
 
4
- type FieldType = 'string' | 'number' | 'boolean';
5
-
4
+ // Define the interface for the Document
6
5
  export interface I<%= modelName %> extends Document {
7
- <% Object.keys(schema).forEach(key => {
8
- const t = String(schema[key] || 'string').toLowerCase();
9
- const tsType = (t === 'number') ? 'number' : (t === 'boolean') ? 'boolean' : 'string';
10
- -%>
11
- <%= key %>: <%= tsType %>;
6
+ <% Object.keys(schema).forEach(key => { %>
7
+ <%= key %>: <%= schema[key].toLowerCase() %>;
12
8
  <% }); %>
13
9
  }
14
10
 
15
- const <%= modelName %>Schema: Schema = new Schema(
16
- {
17
- <% Object.keys(schema).forEach(key => {
18
- const t = String(schema[key] || 'string').toLowerCase();
19
- const mongoType = (t === 'number') ? 'Number' : (t === 'boolean') ? 'Boolean' : 'String';
20
- -%>
21
- <%= key %>: {
22
- type: <%= mongoType %>,
23
- required: false,
24
- },
25
- <% }); %>
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
26
17
  },
27
- { timestamps: true }
28
- );
18
+ <% }); %>
19
+ }, { timestamps: true });
29
20
 
21
+ // Create and export the Model
30
22
  export default mongoose.model<I<%= modelName %>>('<%= modelName %>', <%= modelName %>Schema);
@@ -1,83 +1,66 @@
1
- // Auto-generated by create-backlist v5.1 (Prisma, From Endpoints)
1
+ // Auto-generated by create-backlist v5.0 (Prisma Version)
2
2
  import { Request, Response } from 'express';
3
- import { prisma } from '../db/prisma';
3
+ import { prisma } from '../server'; // Import the Prisma client instance
4
4
 
5
- <%
6
- function safeAction(name, fallback) {
7
- if (!name) return fallback;
8
- return String(name).replace(/[^a-zA-Z0-9_]/g, '');
9
- }
10
-
11
- function hasIdInRoute(route) {
12
- return /:\w+/.test(route || '');
13
- }
14
-
15
- function hasBody(method) {
16
- const m = String(method || 'GET').toUpperCase();
17
- return ['POST', 'PUT', 'PATCH'].includes(m);
18
- }
19
-
20
- // prisma client accessor name. Usually lowerCamelCase in prisma client.
21
- const prismaAccessor = controllerName.charAt(0).toLowerCase() + controllerName.slice(1);
22
- %>
23
-
24
- /**
25
- * Prisma model: <%= controllerName %> (client: prisma.<%= prismaAccessor %>)
26
- * NOTE: If your prisma model name differs, adjust mapping in generator.
27
- */
5
+ // @desc Create a new <%= modelName %>
6
+ export const create<%= modelName %> = async (req: Request, res: Response) => {
7
+ try {
8
+ const newDoc = await prisma.<%= modelName.toLowerCase() %>.create({
9
+ data: req.body,
10
+ });
11
+ res.status(201).json(newDoc);
12
+ } catch (error) {
13
+ res.status(500).json({ message: 'Error creating document', error });
14
+ }
15
+ };
28
16
 
29
- <% (endpoints || []).forEach((ep, i) => {
30
- const method = String(ep.method || 'GET').toUpperCase();
31
- const actionName = safeAction(ep.actionName, `handler${i}`);
32
- const route = (ep.route || ep.path || '').replace(/^\/api/, '');
33
- const usesId = hasIdInRoute(route);
34
- -%>
35
- /**
36
- * <%= method %> <%= ep.route || ep.path || '' %>
37
- */
38
- export async function <%= actionName %>(req: Request, res: Response) {
17
+ // @desc Get all <%= modelName %>s
18
+ export const getAll<%= modelName %>s = async (req: Request, res: Response) => {
39
19
  try {
40
- <% if (method === 'POST') { -%>
41
- const created = await prisma.<%= prismaAccessor %>.create({ data: req.body });
42
- return res.status(201).json(created);
43
- <% } else if (method === 'GET' && !usesId) { -%>
44
- const list = await prisma.<%= prismaAccessor %>.findMany();
45
- return res.status(200).json(list);
46
- <% } else if (method === 'GET' && usesId) { -%>
47
- const idRaw = req.params.id;
48
- // Try number first; fallback to string
49
- const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
20
+ const docs = await prisma.<%= modelName.toLowerCase() %>.findMany();
21
+ res.status(200).json(docs);
22
+ } catch (error) {
23
+ res.status(500).json({ message: 'Error fetching documents', error });
24
+ }
25
+ };
50
26
 
51
- const found = await prisma.<%= prismaAccessor %>.findUnique({ where: { id } });
52
- if (!found) return res.status(404).json({ message: 'Not found' });
53
- return res.status(200).json(found);
54
- <% } else if ((method === 'PUT' || method === 'PATCH') && usesId) { -%>
55
- const idRaw = req.params.id;
56
- const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
27
+ // @desc Get a single <%= modelName %> by ID
28
+ export const get<%= modelName %>ById = async (req: Request, res: Response) => {
29
+ try {
30
+ const { id } = req.params;
31
+ const doc = await prisma.<%= modelName.toLowerCase() %>.findUnique({
32
+ where: { id },
33
+ });
34
+ if (!doc) return res.status(404).json({ message: 'Document not found' });
35
+ res.status(200).json(doc);
36
+ } catch (error) {
37
+ res.status(500).json({ message: 'Error fetching document', error });
38
+ }
39
+ };
57
40
 
58
- const updated = await prisma.<%= prismaAccessor %>.update({
41
+ // @desc Update a <%= modelName %> by ID
42
+ export const update<%= modelName %>ById = async (req: Request, res: Response) => {
43
+ try {
44
+ const { id } = req.params;
45
+ const doc = await prisma.<%= modelName.toLowerCase() %>.update({
59
46
  where: { id },
60
47
  data: req.body,
61
48
  });
62
- return res.status(200).json(updated);
63
- <% } else if (method === 'DELETE' && usesId) { -%>
64
- const idRaw = req.params.id;
65
- const id: any = (idRaw !== undefined && idRaw !== null && String(Number(idRaw)) === idRaw) ? Number(idRaw) : idRaw;
66
-
67
- await prisma.<%= prismaAccessor %>.delete({ where: { id } });
68
- return res.status(200).json({ message: 'Deleted' });
69
- <% } else { -%>
70
- // Non-CRUD or route not matching the default patterns
71
- // TODO: implement custom logic (query params, nested routes, etc.)
72
- <% if (hasBody(method)) { -%>
73
- return res.status(200).json({ message: 'TODO: implement', body: req.body, params: req.params, query: req.query });
74
- <% } else { -%>
75
- return res.status(200).json({ message: 'TODO: implement', params: req.params, query: req.query });
76
- <% } -%>
77
- <% } -%>
78
- } catch (error: any) {
79
- return res.status(500).json({ message: 'Internal Server Error', error: error?.message || error });
49
+ res.status(200).json(doc);
50
+ } catch (error) {
51
+ res.status(500).json({ message: 'Error updating document', error });
80
52
  }
81
- }
53
+ };
82
54
 
83
- <% }) -%>
55
+ // @desc Delete a <%= modelName %> by ID
56
+ export const delete<%= modelName %>ById = async (req: Request, res: Response) => {
57
+ try {
58
+ const { id } = req.params;
59
+ await prisma.<%= modelName.toLowerCase() %>.delete({
60
+ where: { id },
61
+ });
62
+ res.status(200).json({ message: 'Document deleted successfully' });
63
+ } catch (error) {
64
+ res.status(500).json({ message: 'Error deleting document', error });
65
+ }
66
+ };
@@ -1,41 +1,26 @@
1
- // Auto-generated by create-backlist v5.1
1
+ // Auto-generated by create-backlist v5.0
2
2
 
3
3
  generator client {
4
4
  provider = "prisma-client-js"
5
5
  }
6
6
 
7
7
  datasource db {
8
- provider = "postgresql"
8
+ provider = "postgresql" // User can change to "mysql", "sqlite", "sqlserver", etc.
9
9
  url = env("DATABASE_URL")
10
10
  }
11
11
 
12
- <%
13
- function mapPrismaType(t) {
14
- const x = String(t || '').toLowerCase();
15
- if (x === 'number' || x === 'int' || x === 'integer') return 'Int';
16
- if (x === 'float' || x === 'double') return 'Float';
17
- if (x === 'boolean' || x === 'bool') return 'Boolean';
18
- if (x === 'date' || x === 'datetime') return 'DateTime';
19
- return 'String';
20
- }
21
-
22
- function safeName(n) {
23
- return String(n || '').replace(/[^a-zA-Z0-9_]/g, '');
24
- }
25
- %>
26
-
12
+ <%# Loop through each model identified by the analyzer %>
27
13
  <% modelsToGenerate.forEach(model => { %>
28
- model <%= safeName(model.name) %> {
14
+ model <%= model.name %> {
29
15
  id String @id @default(cuid())
30
-
31
- <% (model.fields || []).forEach(field => {
32
- const fname = safeName(field.name);
33
- const prismaType = mapPrismaType(field.type);
34
- const optional = field.isOptional ? '?' : '';
35
- const unique = field.isUnique ? ' @unique' : '';
36
- -%>
37
- <%= fname %> <%= prismaType %><%= optional %><%= unique %>
38
- <% }); -%>
16
+ <%# Loop through each field in the model %>
17
+ <% model.fields.forEach(field => { %>
18
+ <%# Map JS types to Prisma types. This is a basic mapping. %>
19
+ <% let prismaType = 'String'; %>
20
+ <% if (field.type === 'Number') prismaType = 'Int'; %>
21
+ <% if (field.type === 'Boolean') prismaType = 'Boolean'; %>
22
+ <%= field.name.padEnd(10) %> <%= prismaType %><%- field.isOptional ? '?' : '' %><%- field.isUnique ? ' @unique' : '' %>
23
+ <% }); %>
39
24
 
40
25
  createdAt DateTime @default(now())
41
26
  updatedAt DateTime @updatedAt
@@ -2,12 +2,15 @@
2
2
 
3
3
  This backend was auto-generated by **Backlist**.
4
4
 
5
- ## Requirements
6
- - Node.js 18+
7
- - npm 9+
8
-
9
- <% if (dbType === 'prisma') { -%>
10
- ## Database (Prisma + PostgreSQL)
11
- 1. Copy env:
12
- ```bash
13
- cp .env.example .env
5
+ ## 🚀 Getting Started
6
+
7
+ 1. **Navigate to the directory:**
8
+ ```bash
9
+ cd <%= projectName %>
10
+ ```
11
+
12
+ 2. **Run the development server:**
13
+ ```bash
14
+ npm run dev
15
+ ```
16
+ The server will start on `http://localhost:8000`.