better-ts-stack 1.1.4 → 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.
Files changed (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +113 -82
  3. package/dist/args.js +90 -0
  4. package/dist/builder/buildPlanner.js +71 -0
  5. package/dist/builder/configGenerator.js +19 -10
  6. package/dist/builder/dependencyInstaller.js +8 -15
  7. package/dist/builder/fileProcessor.js +47 -44
  8. package/dist/builder/gitInitializer.js +21 -35
  9. package/dist/builder/index.js +33 -91
  10. package/dist/builder/moduleSelector.js +11 -16
  11. package/dist/builder/templateContext.js +6 -27
  12. package/dist/index.js +47 -21
  13. package/dist/modules/registry.js +4 -6
  14. package/dist/output/nextSteps.js +10 -20
  15. package/dist/prompts/index.js +22 -59
  16. package/dist/prompts/stack.js +85 -71
  17. package/dist/types/index.js +47 -41
  18. package/dist/ui/index.js +18 -0
  19. package/dist/ui/messages.js +124 -0
  20. package/dist/ui/prompts.js +70 -0
  21. package/dist/ui/theme.js +23 -0
  22. package/dist/validators/index.js +15 -40
  23. package/package.json +71 -66
  24. package/templates/backend/express/.prettierrc +2 -2
  25. package/templates/backend/express/config.json +40 -44
  26. package/templates/backend/express/src/{index.ts → index.ts.hbs} +75 -76
  27. package/templates/backend/nestjs/.prettierignore +52 -0
  28. package/templates/backend/nestjs/.prettierrc +16 -0
  29. package/templates/backend/nestjs/config.json +41 -0
  30. package/templates/backend/nestjs/eslint.config.mjs +40 -0
  31. package/templates/backend/nestjs/gitignore +36 -0
  32. package/templates/backend/nestjs/nest-cli.json +9 -0
  33. package/templates/backend/nestjs/src/app.module.ts.hbs +34 -0
  34. package/templates/backend/nestjs/src/health/health.controller.ts +12 -0
  35. package/templates/backend/nestjs/src/main.ts +23 -0
  36. package/templates/backend/nestjs/tsconfig.eslint.json +9 -0
  37. package/templates/backend/nestjs/tsconfig.json +25 -0
  38. package/templates/frontend/nextjs/app/layout.tsx +13 -13
  39. package/templates/frontend/nextjs/app/page.tsx.hbs +97 -97
  40. package/templates/frontend/nextjs/components/ui/button.tsx +51 -51
  41. package/templates/frontend/nextjs/components/ui/card.tsx +60 -60
  42. package/templates/frontend/nextjs/components/ui/field.tsx +67 -67
  43. package/templates/frontend/nextjs/components/ui/input.tsx +18 -18
  44. package/templates/frontend/nextjs/config.json +37 -38
  45. package/templates/frontend/nextjs/eslint.config.mjs +11 -11
  46. package/templates/frontend/nextjs/next.config.ts.hbs +12 -12
  47. package/templates/frontend/tanstack/config.json +45 -48
  48. package/templates/modules/auth/express/config.json +20 -26
  49. package/templates/modules/auth/express/src/controllers/authController.ts.hbs +43 -57
  50. package/templates/modules/auth/nestjs/config.json +20 -0
  51. package/templates/modules/auth/nestjs/src/auth/auth.controller.ts +46 -0
  52. package/templates/modules/auth/nestjs/src/auth/auth.guard.ts +36 -0
  53. package/templates/modules/auth/nestjs/src/auth/auth.module.ts.hbs +22 -0
  54. package/templates/modules/auth/nestjs/src/auth/auth.schemas.ts +35 -0
  55. package/templates/modules/auth/nestjs/src/auth/auth.service.ts +70 -0
  56. package/templates/modules/auth/nestjs/src/auth/jwt.ts +27 -0
  57. package/templates/modules/auth/nestjs/src/auth/users.service.ts.hbs +144 -0
  58. package/templates/modules/auth/nestjs/src/auth/zod-validation.pipe.ts +16 -0
  59. package/templates/modules/auth/nextjs/config.json +18 -31
  60. package/templates/modules/auth/tanstack/config.json +2 -16
  61. package/templates/modules/docker/Dockerfile.hbs +135 -133
  62. package/templates/modules/docker/config.json +15 -19
  63. package/templates/modules/drizzle/express/config.json +26 -27
  64. package/templates/modules/drizzle/nestjs/config.json +26 -0
  65. package/templates/modules/drizzle/nestjs/drizzle.config.ts +13 -0
  66. package/templates/modules/drizzle/nestjs/src/database/database.module.ts +30 -0
  67. package/templates/modules/drizzle/nestjs/src/database/schema.ts +10 -0
  68. package/templates/modules/drizzle/nextjs/config.json +26 -29
  69. package/templates/modules/drizzle/tanstack/config.json +5 -5
  70. package/templates/modules/mongoose/express/config.json +15 -16
  71. package/templates/modules/mongoose/nestjs/config.json +15 -0
  72. package/templates/modules/mongoose/nestjs/src/users/user.schema.ts +21 -0
  73. package/templates/modules/mongoose/nextjs/config.json +15 -16
  74. package/templates/modules/mongoose/tanstack/config.json +16 -17
  75. package/templates/modules/prisma/express/config.json +27 -28
  76. package/templates/modules/prisma/nestjs/config.json +27 -0
  77. package/templates/modules/prisma/nestjs/prisma/schema.prisma +23 -0
  78. package/templates/modules/prisma/nestjs/prisma.config.ts +12 -0
  79. package/templates/modules/prisma/nestjs/src/prisma/prisma.module.ts +11 -0
  80. package/templates/modules/prisma/nestjs/src/prisma/prisma.service.ts +17 -0
  81. package/templates/modules/prisma/nextjs/config.json +28 -29
  82. package/templates/modules/prisma/tanstack/config.json +5 -6
  83. package/dist/builder/configGenerator.d.ts +0 -7
  84. package/dist/builder/configGenerator.d.ts.map +0 -1
  85. package/dist/builder/configGenerator.js.map +0 -1
  86. package/dist/builder/dependencyInstaller.d.ts +0 -3
  87. package/dist/builder/dependencyInstaller.d.ts.map +0 -1
  88. package/dist/builder/dependencyInstaller.js.map +0 -1
  89. package/dist/builder/fileProcessor.d.ts +0 -6
  90. package/dist/builder/fileProcessor.d.ts.map +0 -1
  91. package/dist/builder/fileProcessor.js.map +0 -1
  92. package/dist/builder/gitInitializer.d.ts +0 -2
  93. package/dist/builder/gitInitializer.d.ts.map +0 -1
  94. package/dist/builder/gitInitializer.js.map +0 -1
  95. package/dist/builder/index.d.ts +0 -3
  96. package/dist/builder/index.d.ts.map +0 -1
  97. package/dist/builder/index.js.map +0 -1
  98. package/dist/builder/moduleSelector.d.ts +0 -9
  99. package/dist/builder/moduleSelector.d.ts.map +0 -1
  100. package/dist/builder/moduleSelector.js.map +0 -1
  101. package/dist/builder/templateContext.d.ts +0 -21
  102. package/dist/builder/templateContext.d.ts.map +0 -1
  103. package/dist/builder/templateContext.js.map +0 -1
  104. package/dist/index.d.ts +0 -2
  105. package/dist/index.d.ts.map +0 -1
  106. package/dist/index.js.map +0 -1
  107. package/dist/intro.d.ts +0 -2
  108. package/dist/intro.d.ts.map +0 -1
  109. package/dist/intro.js +0 -27
  110. package/dist/intro.js.map +0 -1
  111. package/dist/modules/registry.d.ts +0 -6
  112. package/dist/modules/registry.d.ts.map +0 -1
  113. package/dist/modules/registry.js.map +0 -1
  114. package/dist/output/nextSteps.d.ts +0 -4
  115. package/dist/output/nextSteps.d.ts.map +0 -1
  116. package/dist/output/nextSteps.js.map +0 -1
  117. package/dist/prompts/index.d.ts +0 -4
  118. package/dist/prompts/index.d.ts.map +0 -1
  119. package/dist/prompts/index.js.map +0 -1
  120. package/dist/prompts/stack.d.ts +0 -3
  121. package/dist/prompts/stack.d.ts.map +0 -1
  122. package/dist/prompts/stack.js.map +0 -1
  123. package/dist/types/index.d.ts +0 -159
  124. package/dist/types/index.d.ts.map +0 -1
  125. package/dist/types/index.js.map +0 -1
  126. package/dist/validators/index.d.ts +0 -5
  127. package/dist/validators/index.d.ts.map +0 -1
  128. package/dist/validators/index.js.map +0 -1
  129. package/templates/backend/express/.eslintrc.js +0 -24
@@ -0,0 +1,144 @@
1
+ {{#if (eq database 'drizzle')}}
2
+ import { Inject, Injectable } from "@nestjs/common";
3
+ import { eq } from "drizzle-orm";
4
+
5
+ import { type Database, DRIZZLE } from "../database/database.module.js";
6
+ import { users } from "../database/schema.js";
7
+ {{/if}}
8
+ {{#if (eq database 'prisma')}}
9
+ import { Injectable } from "@nestjs/common";
10
+
11
+ import { PrismaService } from "../prisma/prisma.service.js";
12
+ {{/if}}
13
+ {{#if (eq database 'mongoose')}}
14
+ import { Injectable } from "@nestjs/common";
15
+ import { InjectModel } from "@nestjs/mongoose";
16
+ import type { Model } from "mongoose";
17
+
18
+ import { User } from "../users/user.schema.js";
19
+ {{/if}}
20
+ {{#if (eq database 'none')}}
21
+ import { Injectable } from "@nestjs/common";
22
+ import { randomUUID } from "node:crypto";
23
+ {{/if}}
24
+
25
+ export interface AuthUser {
26
+ id: string;
27
+ email: string;
28
+ name?: string | null;
29
+ password: string;
30
+ }
31
+
32
+ export interface CreateUserInput {
33
+ email: string;
34
+ name?: string | null;
35
+ password: string;
36
+ }
37
+
38
+ @Injectable()
39
+ export class UsersService {
40
+ {{#if (eq database 'drizzle')}}
41
+ constructor(@Inject(DRIZZLE) private readonly db: Database) {}
42
+
43
+ async findByEmail(email: string): Promise<AuthUser | null> {
44
+ const [user] = await this.db
45
+ .select()
46
+ .from(users)
47
+ .where(eq(users.email, email))
48
+ .limit(1);
49
+ return user ?? null;
50
+ }
51
+
52
+ async create(data: CreateUserInput): Promise<AuthUser> {
53
+ const [user] = await this.db.insert(users).values(data).returning();
54
+ return user;
55
+ }
56
+ {{/if}}
57
+ {{#if (eq database 'prisma')}}
58
+ constructor(private readonly prisma: PrismaService) {}
59
+
60
+ findByEmail(email: string): Promise<AuthUser | null> {
61
+ return this.prisma.user.findUnique({ where: { email } });
62
+ }
63
+
64
+ create(data: CreateUserInput): Promise<AuthUser> {
65
+ return this.prisma.user.create({ data });
66
+ }
67
+ {{/if}}
68
+ {{#if (eq database 'mongoose')}}
69
+ constructor(
70
+ @InjectModel(User.name) private readonly userModel: Model<User>
71
+ ) {}
72
+
73
+ async findByEmail(email: string): Promise<AuthUser | null> {
74
+ const user = await this.userModel.findOne({ email }).exec();
75
+ if (!user) return null;
76
+ return {
77
+ id: user._id.toString(),
78
+ email: user.email,
79
+ name: user.name,
80
+ password: user.password,
81
+ };
82
+ }
83
+
84
+ async create(data: CreateUserInput): Promise<AuthUser> {
85
+ const user = await this.userModel.create(data);
86
+ return {
87
+ id: user._id.toString(),
88
+ email: user.email,
89
+ name: user.name,
90
+ password: user.password,
91
+ };
92
+ }
93
+ {{/if}}
94
+ {{#if (eq database 'none')}}
95
+ // In-memory store for trying the API out: users are lost on restart, so add
96
+ // a database before going to production
97
+ private readonly users: AuthUser[] = [];
98
+
99
+ findByEmail(email: string): Promise<AuthUser | null> {
100
+ const user = this.users.find((candidate) => candidate.email === email);
101
+ return Promise.resolve(user ?? null);
102
+ }
103
+
104
+ create(data: CreateUserInput): Promise<AuthUser> {
105
+ // Enforce unique emails like a database constraint would, so a sign-up that
106
+ // raced past the findByEmail check is rejected instead of duplicated
107
+ if (this.users.some((candidate) => candidate.email === data.email)) {
108
+ return Promise.reject(
109
+ Object.assign(new Error("User already exists"), {
110
+ code: "DUPLICATE_EMAIL",
111
+ })
112
+ );
113
+ }
114
+
115
+ const user: AuthUser = { id: randomUUID(), ...data };
116
+ this.users.push(user);
117
+ return Promise.resolve(user);
118
+ }
119
+ {{/if}}
120
+ }
121
+
122
+ // Unique-constraint error codes: Prisma, PostgreSQL (Drizzle wraps it in `cause`),
123
+ // MongoDB, and the in-memory store
124
+ const DUPLICATE_KEY_CODES = new Set<unknown>([
125
+ "P2002",
126
+ "23505",
127
+ 11000,
128
+ "DUPLICATE_EMAIL",
129
+ ]);
130
+
131
+ // True when an insert failed because the email is already taken (e.g. concurrent sign-ups)
132
+ export function isDuplicateEmailError(error: unknown): boolean {
133
+ let current = error;
134
+ for (
135
+ let depth = 0;
136
+ depth < 5 && current && typeof current === "object";
137
+ depth++
138
+ ) {
139
+ if (DUPLICATE_KEY_CODES.has((current as { code?: unknown }).code))
140
+ return true;
141
+ current = (current as { cause?: unknown }).cause;
142
+ }
143
+ return false;
144
+ }
@@ -0,0 +1,16 @@
1
+ import { BadRequestException, type PipeTransform } from "@nestjs/common";
2
+ import type { ZodType } from "zod";
3
+
4
+ // Validates a request body against a Zod schema and returns the parsed value
5
+ export class ZodValidationPipe<T> implements PipeTransform<unknown, T> {
6
+ constructor(private readonly schema: ZodType<T>) {}
7
+
8
+ transform(value: unknown): T {
9
+ // The body is undefined when the request has none
10
+ const result = this.schema.safeParse(value ?? {});
11
+ if (!result.success) {
12
+ throw new BadRequestException(result.error.issues[0].message);
13
+ }
14
+ return result.data;
15
+ }
16
+ }
@@ -1,31 +1,18 @@
1
- {
2
- "id": "auth",
3
- "name": "Better Auth",
4
- "description": "Better Auth for Next.js with optional Prisma or Drizzle adapter",
5
- "type": "feature",
6
- "dependencies": {
7
- "@hookform/resolvers": "^5.9.1",
8
- "better-auth": "^1.7.3",
9
- "mongodb": "^7.6.0",
10
- "react-hook-form": "^7.87.0",
11
- "zod": "^4.5.4"
12
- },
13
- "devDependencies": {},
14
- "scripts": {},
15
- "envVars": {
16
- "BETTER_AUTH_SECRET": "please-change-me-to-a-random-string",
17
- "BETTER_AUTH_URL": "http://localhost:3000"
18
- },
19
- "templateFiles": [
20
- "lib/auth.ts.hbs",
21
- "lib/auth-client.ts",
22
- "app/api/auth/[...all]/route.ts",
23
- "app/dashboard/page.tsx",
24
- "app/sign-in/page.tsx",
25
- "app/sign-up/page.tsx",
26
- "components/auth/sign-in-form.tsx",
27
- "components/auth/sign-up-form.tsx",
28
- "components/auth/sign-out-button.tsx",
29
- "lib/auth-schema.ts"
30
- ]
31
- }
1
+ {
2
+ "id": "auth",
3
+ "name": "Better Auth",
4
+ "description": "Better Auth for Next.js with optional Prisma or Drizzle adapter",
5
+ "type": "feature",
6
+ "dependencies": {
7
+ "@hookform/resolvers": "^5.9.1",
8
+ "better-auth": "^1.7.3",
9
+ "react-hook-form": "^7.87.0",
10
+ "zod": "^4.6.5"
11
+ },
12
+ "devDependencies": {},
13
+ "scripts": {},
14
+ "envVars": {
15
+ "BETTER_AUTH_SECRET": "please-change-me-to-a-random-string",
16
+ "BETTER_AUTH_URL": "http://localhost:3000"
17
+ }
18
+ }
@@ -7,27 +7,13 @@
7
7
  "@hookform/resolvers": "^5.9.1",
8
8
  "better-auth": "^1.7.3",
9
9
  "dotenv": "^17.4.2",
10
- "mongodb": "^7.6.0",
11
10
  "react-hook-form": "^7.87.0",
12
- "zod": "^4.5.4"
11
+ "zod": "^4.6.5"
13
12
  },
14
13
  "devDependencies": {},
15
14
  "scripts": {},
16
15
  "envVars": {
17
16
  "BETTER_AUTH_SECRET": "please-change-me-to-a-random-string",
18
17
  "BETTER_AUTH_URL": "http://localhost:3000"
19
- },
20
- "templateFiles": [
21
- "src/lib/auth.ts.hbs",
22
- "src/lib/auth-client.ts",
23
- "src/lib/auth-functions.ts",
24
- "src/routes/api/auth/$.ts",
25
- "src/routes/dashboard.tsx",
26
- "src/routes/sign-in.tsx",
27
- "src/routes/sign-up.tsx",
28
- "src/components/auth/sign-in-form.tsx",
29
- "src/components/auth/sign-up-form.tsx",
30
- "src/components/auth/sign-out-button.tsx",
31
- "src/lib/auth-schema.ts"
32
- ]
18
+ }
33
19
  }
@@ -1,134 +1,136 @@
1
- {{#if (eq framework 'nextjs')}}
2
- # Base stage
3
- FROM node:24-alpine AS base
4
- WORKDIR /app
5
- RUN apk add --no-cache libc6-compat
6
-
7
- # Dependencies stage
8
- FROM base AS deps
9
- COPY package*.json ./
10
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
11
-
12
- # Build stage
13
- FROM base AS builder
14
- COPY --from=deps /app/node_modules ./node_modules
15
- COPY . .
16
- {{#if useAuth}}
17
- # next build loads the auth route, which initializes Better Auth. The build gets
18
- # a placeholder secret for this command only; the real one is supplied at runtime.
19
- {{/if}}
20
- {{#if (eq database 'prisma')}}
21
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
22
- {{else if (eq database 'mongoose')}}
23
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}MONGODB_URI="mongodb://localhost:27017/build" npm run build
24
- {{else}}
25
- RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}npm run build
26
- {{/if}}
27
-
28
- # Production stage
29
- FROM node:24-alpine AS runner
30
- WORKDIR /app
31
- ENV NODE_ENV=production
32
-
33
- RUN addgroup --system --gid 1001 nodejs && \
34
- adduser --system --uid 1001 nextjs
35
-
36
- COPY --from=builder /app/public ./public
37
- COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
38
- COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
39
-
40
- USER nextjs
41
- EXPOSE {{port}}
42
- ENV PORT={{port}} HOSTNAME=0.0.0.0
43
-
44
- CMD ["node", "server.js"]
45
-
46
- {{else if (eq framework 'tanstack')}}
47
- # Build stage
48
- FROM node:24-alpine AS builder
49
- WORKDIR /app
50
-
51
- COPY package*.json ./
52
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
53
-
54
- COPY . .
55
-
56
- # Only build if package.json has a build script
57
- {{#if (eq database 'prisma')}}
58
- RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
59
- {{else if (eq database 'mongoose')}}
60
- RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
61
- {{else}}
62
- RUN npm run build
63
- {{/if}}
64
-
65
- # Production stage
66
- FROM node:24-alpine
67
- WORKDIR /app
68
- ENV NODE_ENV=production
69
-
70
- # Create non-root user for security
71
- RUN addgroup --system --gid 1001 nodejs && \
72
- adduser --system --uid 1001 tanstackuser
73
-
74
- COPY package*.json ./
75
- # Clear the npm cache in the same layer so it does not ship in the image
76
- RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
77
- && npm cache clean --force
78
-
79
- # Copy built output (dist/server/server.js + dist/client)
80
- COPY --from=builder --chown=tanstackuser:nodejs /app/dist ./dist
81
-
82
- # Include the Prisma client generated during the build stage
83
- {{#if (eq database 'prisma')}}
84
- COPY --from=builder --chown=tanstackuser:nodejs /app/generated ./generated
85
- {{/if}}
86
-
87
- USER tanstackuser
88
- EXPOSE {{port}}
89
- ENV PORT={{port}}
90
-
91
- CMD ["node_modules/.bin/srvx", "--prod", "-s", "../client", "dist/server/server.js"]
92
-
93
- {{else}}
94
- # Build stage
95
- FROM node:24-alpine AS builder
96
- WORKDIR /app
97
-
98
- COPY package*.json ./
99
- RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
100
-
101
- COPY . .
102
-
103
- # Only build if package.json has a build script
104
- {{#if (eq database 'prisma')}}
105
- RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
106
- {{else if (eq database 'mongoose')}}
107
- RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
108
- {{else}}
109
- RUN npm run build
110
- {{/if}}
111
-
112
- # Production stage
113
- FROM node:24-alpine
114
- WORKDIR /app
115
- ENV NODE_ENV=production
116
-
117
- # Create non-root user for security
118
- RUN addgroup --system --gid 1001 nodejs && \
119
- adduser --system --uid 1001 expressuser
120
-
121
- COPY package*.json ./
122
- # Clear the npm cache in the same layer so it does not ship in the image
123
- RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
124
- && npm cache clean --force
125
-
126
- # Copy built files (if TypeScript) or source files (if JavaScript)
127
- COPY --from=builder --chown=expressuser:nodejs /app/dist ./dist
128
-
129
- USER expressuser
130
- EXPOSE {{port}}
131
- ENV PORT={{port}}
132
-
133
- CMD ["node", "dist/index.js"]
1
+ {{#if (eq framework 'nextjs')}}
2
+ # Base stage
3
+ FROM node:24-alpine AS base
4
+ WORKDIR /app
5
+ RUN apk add --no-cache libc6-compat
6
+
7
+ # Dependencies stage
8
+ FROM base AS deps
9
+ COPY package*.json ./
10
+ RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
11
+
12
+ # Build stage
13
+ FROM base AS builder
14
+ COPY --from=deps /app/node_modules ./node_modules
15
+ COPY . .
16
+ {{#if useAuth}}
17
+ # next build loads the auth route, which initializes Better Auth. The build gets
18
+ # a placeholder secret for this command only; the real one is supplied at runtime.
19
+ {{/if}}
20
+ {{#if (eq database 'prisma')}}
21
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
22
+ {{else if (eq database 'mongoose')}}
23
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}MONGODB_URI="mongodb://localhost:27017/build" npm run build
24
+ {{else}}
25
+ RUN {{#if useAuth}}BETTER_AUTH_SECRET="build-only-placeholder-secret-not-used-at-runtime" {{/if}}npm run build
26
+ {{/if}}
27
+
28
+ # Production stage
29
+ FROM node:24-alpine AS runner
30
+ WORKDIR /app
31
+ ENV NODE_ENV=production
32
+
33
+ RUN addgroup --system --gid 1001 nodejs && \
34
+ adduser --system --uid 1001 nextjs
35
+
36
+ COPY --from=builder /app/public ./public
37
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
38
+ COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
39
+
40
+ USER nextjs
41
+ EXPOSE {{port}}
42
+ ENV PORT={{port}} HOSTNAME=0.0.0.0
43
+
44
+ CMD ["node", "server.js"]
45
+
46
+ {{else if (eq framework 'tanstack')}}
47
+ # Build stage
48
+ FROM node:24-alpine AS builder
49
+ WORKDIR /app
50
+
51
+ COPY package*.json ./
52
+ RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
53
+
54
+ COPY . .
55
+
56
+ {{#if (eq database 'prisma')}}
57
+ RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
58
+ {{else if (eq database 'mongoose')}}
59
+ RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
60
+ {{else}}
61
+ RUN npm run build
62
+ {{/if}}
63
+
64
+ # Production stage
65
+ FROM node:24-alpine
66
+ WORKDIR /app
67
+ ENV NODE_ENV=production
68
+
69
+ # Create non-root user for security
70
+ RUN addgroup --system --gid 1001 nodejs && \
71
+ adduser --system --uid 1001 tanstackuser
72
+
73
+ COPY package*.json ./
74
+ # Clear the npm cache in the same layer so it does not ship in the image
75
+ RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
76
+ && npm cache clean --force
77
+
78
+ # Copy built output (dist/server/server.js + dist/client)
79
+ COPY --from=builder --chown=tanstackuser:nodejs /app/dist ./dist
80
+
81
+ # Include the Prisma client generated during the build stage
82
+ {{#if (eq database 'prisma')}}
83
+ COPY --from=builder --chown=tanstackuser:nodejs /app/generated ./generated
84
+ {{/if}}
85
+
86
+ USER tanstackuser
87
+ EXPOSE {{port}}
88
+ ENV PORT={{port}}
89
+
90
+ CMD ["node_modules/.bin/srvx", "--prod", "-s", "../client", "dist/server/server.js"]
91
+
92
+ {{else}}
93
+ # Build stage
94
+ FROM node:24-alpine AS builder
95
+ WORKDIR /app
96
+
97
+ COPY package*.json ./
98
+ RUN if [ -f package-lock.json ]; then npm ci; else npm install; fi
99
+
100
+ COPY . .
101
+
102
+ {{#if (eq database 'prisma')}}
103
+ RUN DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" npm run build
104
+ {{else if (eq database 'mongoose')}}
105
+ RUN MONGODB_URI="mongodb://localhost:27017/build" npm run build
106
+ {{else}}
107
+ RUN npm run build
108
+ {{/if}}
109
+
110
+ # Production stage
111
+ FROM node:24-alpine
112
+ WORKDIR /app
113
+ ENV NODE_ENV=production
114
+
115
+ # Create non-root user for security
116
+ RUN addgroup --system --gid 1001 nodejs && \
117
+ adduser --system --uid 1001 appuser
118
+
119
+ COPY package*.json ./
120
+ # Clear the npm cache in the same layer so it does not ship in the image
121
+ RUN if [ -f package-lock.json ]; then npm ci --omit=dev; else npm install --omit=dev; fi \
122
+ && npm cache clean --force
123
+
124
+ # Copy the compiled output
125
+ COPY --from=builder --chown=appuser:nodejs /app/dist ./dist
126
+
127
+ USER appuser
128
+ EXPOSE {{port}}
129
+ ENV PORT={{port}}
130
+
131
+ {{#if (eq framework 'nestjs')}}
132
+ CMD ["node", "dist/main.js"]
133
+ {{else}}
134
+ CMD ["node", "dist/index.js"]
135
+ {{/if}}
134
136
  {{/if}}
@@ -1,19 +1,15 @@
1
- {
2
- "id": "docker",
3
- "name": "Docker",
4
- "description": "Docker containerization with multi-stage build and docker-compose",
5
- "type": "feature",
6
- "dependencies": {},
7
- "devDependencies": {},
8
- "scripts": {
9
- "docker:build": "docker build -t {{projectName}} .",
10
- "docker:up": "docker compose up -d --build",
11
- "docker:down": "docker compose down",
12
- "docker:logs": "docker compose logs -f"
13
- },
14
- "envVars": {},
15
- "templateFiles": [
16
- "docker-compose.yml.hbs",
17
- "Dockerfile.hbs"
18
- ]
19
- }
1
+ {
2
+ "id": "docker",
3
+ "name": "Docker",
4
+ "description": "Docker containerization with multi-stage build and docker-compose",
5
+ "type": "feature",
6
+ "dependencies": {},
7
+ "devDependencies": {},
8
+ "scripts": {
9
+ "docker:build": "docker build -t {{projectName}} .",
10
+ "docker:up": "docker compose up -d --build",
11
+ "docker:down": "docker compose down",
12
+ "docker:logs": "docker compose logs -f"
13
+ },
14
+ "envVars": {}
15
+ }
@@ -1,27 +1,26 @@
1
- {
2
- "id": "drizzle",
3
- "name": "Drizzle ORM",
4
- "description": "Drizzle ORM for Express with PostgreSQL",
5
- "type": "database",
6
- "dependencies": {
7
- "drizzle-orm": "^0.45.2",
8
- "pg": "^8.23.0",
9
- "dotenv": "^17.4.2"
10
- },
11
- "devDependencies": {
12
- "drizzle-kit": "^0.31.10",
13
- "@types/pg": "^8.23.1"
14
- },
15
- "scripts": {
16
- "db:generate": "drizzle-kit generate",
17
- "db:migrate": "drizzle-kit migrate",
18
- "db:studio": "drizzle-kit studio"
19
- },
20
- "envVars": {
21
- "DATABASE_URL": "postgresql://user:password@localhost:5432/mydb?schema=public"
22
- },
23
- "templateFiles": [],
24
- "overrides": {
25
- "esbuild": "^0.28.2"
26
- }
27
- }
1
+ {
2
+ "id": "drizzle",
3
+ "name": "Drizzle ORM",
4
+ "description": "Drizzle ORM for Express with PostgreSQL",
5
+ "type": "database",
6
+ "dependencies": {
7
+ "drizzle-orm": "^0.45.2",
8
+ "pg": "^8.23.0",
9
+ "dotenv": "^17.4.2"
10
+ },
11
+ "devDependencies": {
12
+ "drizzle-kit": "^0.31.10",
13
+ "@types/pg": "^8.23.1"
14
+ },
15
+ "scripts": {
16
+ "db:generate": "drizzle-kit generate",
17
+ "db:migrate": "drizzle-kit migrate",
18
+ "db:studio": "drizzle-kit studio"
19
+ },
20
+ "envVars": {
21
+ "DATABASE_URL": "postgresql://postgres:postgres@localhost:5432/postgres"
22
+ },
23
+ "overrides": {
24
+ "esbuild": "^0.28.2"
25
+ }
26
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "id": "drizzle",
3
+ "name": "Drizzle ORM",
4
+ "description": "Drizzle ORM for NestJS with PostgreSQL",
5
+ "type": "database",
6
+ "dependencies": {
7
+ "drizzle-orm": "^0.45.2",
8
+ "pg": "^8.23.0",
9
+ "dotenv": "^17.4.2"
10
+ },
11
+ "devDependencies": {
12
+ "drizzle-kit": "^0.31.10",
13
+ "@types/pg": "^8.23.1"
14
+ },
15
+ "scripts": {
16
+ "db:generate": "drizzle-kit generate",
17
+ "db:migrate": "drizzle-kit migrate",
18
+ "db:studio": "drizzle-kit studio"
19
+ },
20
+ "envVars": {
21
+ "DATABASE_URL": "postgresql://postgres:postgres@localhost:5432/postgres"
22
+ },
23
+ "overrides": {
24
+ "esbuild": "^0.28.2"
25
+ }
26
+ }
@@ -0,0 +1,13 @@
1
+ import { config } from "dotenv";
2
+ import { defineConfig } from "drizzle-kit";
3
+
4
+ config({ path: ".env" });
5
+
6
+ export default defineConfig({
7
+ schema: "./src/database/schema.ts",
8
+ out: "./migrations",
9
+ dialect: "postgresql",
10
+ dbCredentials: {
11
+ url: process.env.DATABASE_URL!,
12
+ },
13
+ });