stackkit-cli 0.4.3 → 0.4.4

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 (57) hide show
  1. package/bin/stackkit.js +10 -1
  2. package/dist/commands/add.js +138 -1
  3. package/dist/types/index.d.ts +14 -2
  4. package/modules/auth/better-auth/files/lib/auth.ts +13 -0
  5. package/modules/auth/better-auth/files/schemas/prisma-schema.prisma +63 -0
  6. package/modules/auth/better-auth/module.json +54 -0
  7. package/modules/auth/clerk/module.json +115 -0
  8. package/modules/database/mongoose-mongodb/files/lib/db.ts +44 -6
  9. package/modules/database/mongoose-mongodb/files/models/User.ts +39 -0
  10. package/modules/database/mongoose-mongodb/module.json +27 -12
  11. package/modules/database/prisma/files/lib/prisma.ts +6 -0
  12. package/modules/database/prisma/files/prisma/schema.prisma +8 -0
  13. package/modules/database/prisma/files/prisma.config.ts +12 -0
  14. package/modules/database/prisma/module.json +140 -0
  15. package/package.json +1 -1
  16. package/templates/express/.env.example +2 -10
  17. package/templates/express/package.json +12 -18
  18. package/templates/express/src/app.ts +9 -29
  19. package/templates/express/src/config/env.ts +3 -14
  20. package/templates/express/src/features/auth/auth.controller.ts +48 -0
  21. package/templates/express/src/features/auth/auth.route.ts +10 -0
  22. package/templates/express/src/features/auth/auth.service.ts +21 -0
  23. package/templates/express/src/middlewares/error.middleware.ts +2 -2
  24. package/templates/express/src/server.ts +1 -1
  25. package/templates/express/template.json +1 -5
  26. package/templates/express/tsconfig.json +0 -1
  27. package/modules/auth/better-auth-express/adapters/mongoose-mongodb.ts +0 -13
  28. package/modules/auth/better-auth-express/adapters/prisma-mongodb.ts +0 -15
  29. package/modules/auth/better-auth-express/adapters/prisma-postgresql.ts +0 -15
  30. package/modules/auth/better-auth-express/files/lib/auth.ts +0 -16
  31. package/modules/auth/better-auth-express/files/routes/auth.ts +0 -12
  32. package/modules/auth/better-auth-express/files/schemas/prisma-mongodb-schema.prisma +0 -72
  33. package/modules/auth/better-auth-express/files/schemas/prisma-postgresql-schema.prisma +0 -72
  34. package/modules/auth/better-auth-express/module.json +0 -61
  35. package/modules/auth/better-auth-nextjs/adapters/mongoose-mongodb.ts +0 -24
  36. package/modules/auth/better-auth-nextjs/adapters/prisma-mongodb.ts +0 -26
  37. package/modules/auth/better-auth-nextjs/adapters/prisma-postgresql.ts +0 -26
  38. package/modules/auth/better-auth-nextjs/files/lib/auth.ts +0 -26
  39. package/modules/auth/better-auth-nextjs/files/schemas/prisma-mongodb-schema.prisma +0 -72
  40. package/modules/auth/better-auth-nextjs/files/schemas/prisma-postgresql-schema.prisma +0 -72
  41. package/modules/auth/better-auth-nextjs/module.json +0 -62
  42. package/modules/auth/better-auth-react/files/lib/auth-client.ts +0 -9
  43. package/modules/auth/better-auth-react/module.json +0 -28
  44. package/modules/auth/clerk-express/module.json +0 -34
  45. package/modules/auth/clerk-nextjs/module.json +0 -64
  46. package/modules/auth/clerk-react/module.json +0 -28
  47. package/modules/database/prisma-mongodb/files/lib/db.ts +0 -9
  48. package/modules/database/prisma-mongodb/files/prisma/schema.prisma +0 -17
  49. package/modules/database/prisma-mongodb/module.json +0 -60
  50. package/modules/database/prisma-postgresql/files/lib/db.ts +0 -9
  51. package/modules/database/prisma-postgresql/files/prisma/schema.prisma +0 -17
  52. package/modules/database/prisma-postgresql/module.json +0 -60
  53. /package/modules/auth/{better-auth-nextjs → better-auth}/files/api/auth/[...all]/route.ts +0 -0
  54. /package/modules/auth/{clerk-express/files/lib → clerk/files/express}/auth.ts +0 -0
  55. /package/modules/auth/{clerk-nextjs/files/lib → clerk/files/nextjs}/auth-provider.tsx +0 -0
  56. /package/modules/auth/{clerk-nextjs/files → clerk/files/nextjs}/middleware.ts +0 -0
  57. /package/modules/auth/{clerk-react/files/lib → clerk/files/react}/auth-provider.tsx +0 -0
package/bin/stackkit.js CHANGED
@@ -1,2 +1,11 @@
1
1
  #!/usr/bin/env node
2
- require("../dist/index.js");
2
+
3
+ try {
4
+ // eslint-disable-next-line
5
+ require("ts-node/register");
6
+ // eslint-disable-next-line
7
+ require("../src/index.ts");
8
+ } catch {
9
+ // eslint-disable-next-line
10
+ require("../dist/index.js");
11
+ }
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.addCommand = addCommand;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
+ const child_process_1 = require("child_process");
8
9
  const fs_extra_1 = __importDefault(require("fs-extra"));
9
10
  const inquirer_1 = __importDefault(require("inquirer"));
10
11
  const path_1 = __importDefault(require("path"));
@@ -27,6 +28,81 @@ async function addCommand(module, options) {
27
28
  logger_1.logger.error(`Module "${module}" not found`);
28
29
  process.exit(1);
29
30
  }
31
+ // For database modules, ensure provider is selected
32
+ let selectedProvider = options.provider;
33
+ if (moduleMetadata.category === "database" && !selectedProvider) {
34
+ if (typeof moduleMetadata.dependencies === "object" &&
35
+ "providers" in moduleMetadata.dependencies) {
36
+ const providers = Object.keys(moduleMetadata.dependencies.providers || {});
37
+ if (providers.length > 0) {
38
+ const { provider } = await inquirer_1.default.prompt([
39
+ {
40
+ type: "list",
41
+ name: "provider",
42
+ message: "Select database provider:",
43
+ choices: providers.map((p) => ({ name: p, value: p })),
44
+ },
45
+ ]);
46
+ selectedProvider = provider;
47
+ }
48
+ }
49
+ }
50
+ // Merge dependencies based on provider
51
+ const mergedDeps = {};
52
+ const mergedDevDeps = {};
53
+ if (typeof moduleMetadata.dependencies === "object" &&
54
+ !("common" in moduleMetadata.dependencies)) {
55
+ // Already flat
56
+ Object.assign(mergedDeps, moduleMetadata.dependencies);
57
+ }
58
+ else if (typeof moduleMetadata.dependencies === "object" &&
59
+ "common" in moduleMetadata.dependencies) {
60
+ Object.assign(mergedDeps, moduleMetadata.dependencies.common);
61
+ if (selectedProvider &&
62
+ typeof moduleMetadata.dependencies === "object" &&
63
+ "providers" in moduleMetadata.dependencies &&
64
+ typeof moduleMetadata.dependencies.providers === "object" &&
65
+ selectedProvider in moduleMetadata.dependencies.providers) {
66
+ Object.assign(mergedDeps, moduleMetadata.dependencies.providers[selectedProvider]);
67
+ }
68
+ }
69
+ if (moduleMetadata.devDependencies) {
70
+ if (typeof moduleMetadata.devDependencies === "object" &&
71
+ !("common" in moduleMetadata.devDependencies)) {
72
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies);
73
+ }
74
+ else if (typeof moduleMetadata.devDependencies === "object" &&
75
+ "common" in moduleMetadata.devDependencies) {
76
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies.common);
77
+ if (selectedProvider &&
78
+ typeof moduleMetadata.devDependencies === "object" &&
79
+ "providers" in moduleMetadata.devDependencies &&
80
+ typeof moduleMetadata.devDependencies.providers === "object" &&
81
+ selectedProvider in moduleMetadata.devDependencies.providers) {
82
+ Object.assign(mergedDevDeps, moduleMetadata.devDependencies.providers[selectedProvider]);
83
+ }
84
+ }
85
+ }
86
+ // Update metadata with merged deps
87
+ moduleMetadata.dependencies = mergedDeps;
88
+ moduleMetadata.devDependencies = mergedDevDeps;
89
+ // Set variables for replacements
90
+ const variables = {};
91
+ if (selectedProvider) {
92
+ variables.provider = selectedProvider;
93
+ if (selectedProvider === "postgresql") {
94
+ variables.connectionString = "postgresql://user:password@localhost:5432/mydb?schema=public";
95
+ }
96
+ else if (selectedProvider === "mongodb") {
97
+ variables.connectionString = "mongodb://localhost:27017/mydb";
98
+ }
99
+ else if (selectedProvider === "mysql") {
100
+ variables.connectionString = "mysql://user:password@localhost:3306/mydb";
101
+ }
102
+ else if (selectedProvider === "sqlite") {
103
+ variables.connectionString = "file:./dev.db";
104
+ }
105
+ }
30
106
  // Check if framework is supported
31
107
  if (!moduleMetadata.supportedFrameworks.includes(projectInfo.framework)) {
32
108
  logger_1.logger.error(`Module "${module}" does not support ${projectInfo.framework}. Supported: ${moduleMetadata.supportedFrameworks.join(", ")}`);
@@ -54,6 +130,24 @@ async function addCommand(module, options) {
54
130
  }
55
131
  // Apply module patches
56
132
  await applyModulePatches(projectRoot, projectInfo, moduleMetadata, modulesDir, module, options);
133
+ // Apply framework patches
134
+ if (moduleMetadata.frameworkPatches && !options.dryRun) {
135
+ await applyFrameworkPatches(projectRoot, moduleMetadata.frameworkPatches, projectInfo.framework);
136
+ }
137
+ // Run post-install commands
138
+ if (moduleMetadata.postInstall && moduleMetadata.postInstall.length > 0 && !options.dryRun) {
139
+ const postInstallSpinner = logger_1.logger.startSpinner("Running post-install commands...");
140
+ try {
141
+ for (const command of moduleMetadata.postInstall) {
142
+ (0, child_process_1.execSync)(command, { cwd: projectRoot, stdio: "pipe" });
143
+ }
144
+ postInstallSpinner.succeed("Post-install commands completed");
145
+ }
146
+ catch (error) {
147
+ postInstallSpinner.fail("Failed to run post-install commands");
148
+ throw error;
149
+ }
150
+ }
57
151
  // Add dependencies
58
152
  if (Object.keys(moduleMetadata.dependencies).length > 0 && options.install !== false) {
59
153
  const deps = Object.entries(moduleMetadata.dependencies).map(([name, version]) => `${name}@${version}`);
@@ -78,8 +172,13 @@ async function addCommand(module, options) {
78
172
  }
79
173
  // Add environment variables
80
174
  if (moduleMetadata.envVars.length > 0) {
175
+ // Replace variables in envVars
176
+ const processedEnvVars = moduleMetadata.envVars.map((envVar) => ({
177
+ ...envVar,
178
+ value: envVar.value?.replace(/\{\{(\w+)\}\}/g, (match, key) => variables[key] || match),
179
+ }));
81
180
  if (!options.dryRun) {
82
- await (0, env_editor_1.addEnvVariables)(projectRoot, moduleMetadata.envVars, { force: options.force });
181
+ await (0, env_editor_1.addEnvVariables)(projectRoot, processedEnvVars, { force: options.force });
83
182
  }
84
183
  else {
85
184
  logger_1.logger.log(` ${chalk_1.default.dim("~")} .env.example`);
@@ -208,3 +307,41 @@ async function findModulePath(modulesDir, moduleName, provider) {
208
307
  }
209
308
  return null;
210
309
  }
310
+ async function applyFrameworkPatches(projectRoot, patches, framework) {
311
+ const frameworkKey = framework;
312
+ const frameworkPatches = patches[frameworkKey];
313
+ if (!frameworkPatches)
314
+ return;
315
+ for (const [filename, patchConfig] of Object.entries(frameworkPatches)) {
316
+ const filePath = path_1.default.join(projectRoot, filename);
317
+ if (await fs_extra_1.default.pathExists(filePath)) {
318
+ const fileContent = await fs_extra_1.default.readJson(filePath);
319
+ if (patchConfig.merge) {
320
+ const merged = deepMerge(fileContent, patchConfig.merge);
321
+ await fs_extra_1.default.writeJson(filePath, merged, { spaces: 2 });
322
+ const relativePath = path_1.default.relative(projectRoot, filePath);
323
+ logger_1.logger.log(` ${chalk_1.default.blue("~")} ${relativePath}`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ function deepMerge(target, source) {
329
+ const output = { ...target };
330
+ for (const key in source) {
331
+ if (source[key] && typeof source[key] === "object" && !Array.isArray(source[key])) {
332
+ if (target[key]) {
333
+ output[key] = deepMerge(target[key], source[key]);
334
+ }
335
+ else {
336
+ output[key] = source[key];
337
+ }
338
+ }
339
+ else if (Array.isArray(source[key])) {
340
+ output[key] = Array.from(new Set([...(target[key] || []), ...source[key]]));
341
+ }
342
+ else {
343
+ output[key] = source[key];
344
+ }
345
+ }
346
+ return output;
347
+ }
@@ -12,10 +12,22 @@ export interface ModuleMetadata {
12
12
  description: string;
13
13
  category: "auth" | "database" | "ui" | "other";
14
14
  supportedFrameworks: string[];
15
- dependencies: Record<string, string>;
16
- devDependencies?: Record<string, string>;
15
+ dependencies: {
16
+ common?: Record<string, string>;
17
+ providers?: Record<string, Record<string, string>>;
18
+ } | Record<string, string>;
19
+ devDependencies?: {
20
+ common?: Record<string, string>;
21
+ providers?: Record<string, Record<string, string>>;
22
+ } | Record<string, string>;
17
23
  envVars: EnvVar[];
18
24
  patches: ModulePatch[];
25
+ frameworkPatches?: Record<string, {
26
+ [file: string]: {
27
+ merge?: Record<string, unknown>;
28
+ };
29
+ }>;
30
+ postInstall?: string[];
19
31
  }
20
32
  export interface EnvVar {
21
33
  key: string;
@@ -0,0 +1,13 @@
1
+ import { betterAuth } from "better-auth";
2
+
3
+ {{databaseAdapter}}
4
+ emailAndPassword: {
5
+ enabled: true,
6
+ },
7
+ socialProviders: {
8
+ google: {
9
+ clientId: process.env.GOOGLE_CLIENT_ID!,
10
+ clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
11
+ },
12
+ },
13
+ });
@@ -0,0 +1,63 @@
1
+ // Better Auth models
2
+ model User {
3
+ id String @id {{idDefault}}
4
+ name String
5
+ email String
6
+ emailVerified Boolean @default(false)
7
+ image String?
8
+ createdAt DateTime @default(now())
9
+ updatedAt DateTime @updatedAt
10
+ sessions Session[]
11
+ accounts Account[]
12
+ role String @default("USER")
13
+
14
+ @@unique([email])
15
+ @@map("user")
16
+ }
17
+
18
+ model Session {
19
+ id String @id {{idDefault}}
20
+ expiresAt DateTime
21
+ token String @unique
22
+ createdAt DateTime @default(now())
23
+ updatedAt DateTime @updatedAt
24
+ ipAddress String?
25
+ userAgent String?
26
+ userId String {{userIdType}}
27
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
28
+
29
+ @@index([userId])
30
+ @@map("session")
31
+ }
32
+
33
+ model Account {
34
+ id String @id {{idDefault}}
35
+ accountId String
36
+ providerId String
37
+ userId String {{userIdType}}
38
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
39
+ accessToken String?
40
+ refreshToken String?
41
+ idToken String?
42
+ accessTokenExpiresAt DateTime?
43
+ refreshTokenExpiresAt DateTime?
44
+ scope String?
45
+ password String?
46
+ createdAt DateTime @default(now())
47
+ updatedAt DateTime @updatedAt
48
+
49
+ @@index([userId])
50
+ @@map("account")
51
+ }
52
+
53
+ model Verification {
54
+ id String @id {{idDefault}}
55
+ identifier String
56
+ value String
57
+ expiresAt DateTime
58
+ createdAt DateTime @default(now())
59
+ updatedAt DateTime @updatedAt
60
+
61
+ @@index([identifier])
62
+ @@map("verification")
63
+ }
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "better-auth",
3
+ "displayName": "Better Auth",
4
+ "description": "Modern authentication with Better Auth",
5
+ "category": "auth",
6
+ "provider": "better-auth",
7
+ "supportedFrameworks": ["nextjs", "express", "react-vite"],
8
+ "databaseAdapters": {
9
+ "prisma-postgresql": {
10
+ "adapterCode": "import { prisma } from \"{{dbImport}}\";\nimport { prismaAdapter } from \"@better-auth/prisma\";\n\nexport const auth = betterAuth({\n database: prismaAdapter(prisma, {\n provider: \"postgresql\",\n }),",
11
+ "schema": "files/schemas/prisma-schema.prisma",
12
+ "schemaDestination": "prisma/schema.prisma",
13
+ "dependencies": {
14
+ "better-auth": "^1.1.4"
15
+ }
16
+ },
17
+ "prisma-mongodb": {
18
+ "adapterCode": "import { prisma } from \"{{dbImport}}\";\nimport { prismaAdapter } from \"@better-auth/prisma\";\n\nexport const auth = betterAuth({\n database: prismaAdapter(prisma, {\n provider: \"mongodb\",\n }),",
19
+ "schema": "files/schemas/prisma-schema.prisma",
20
+ "schemaDestination": "prisma/schema.prisma",
21
+ "dependencies": {
22
+ "better-auth": "^1.1.4"
23
+ }
24
+ },
25
+ "mongoose-mongodb": {
26
+ "adapterCode": "import { client } from \"{{dbImport}}\";\nimport { mongodbAdapter } from \"better-auth/adapters/mongodb\";\n\nexport const auth = betterAuth({\n database: mongodbAdapter(client),",
27
+ "dependencies": {
28
+ "better-auth": "^1.1.4"
29
+ }
30
+ }
31
+ },
32
+ "envVars": [
33
+ {
34
+ "key": "BETTER_AUTH_SECRET",
35
+ "value": "",
36
+ "description": "Secret key for Better Auth. Generate with: openssl rand -base64 32",
37
+ "required": true
38
+ },
39
+ {
40
+ "key": "BETTER_AUTH_URL",
41
+ "value": "http://localhost:3000",
42
+ "description": "Base URL of your application (change in production)",
43
+ "required": true
44
+ }
45
+ ],
46
+ "patches": [
47
+ {
48
+ "type": "create-file",
49
+ "description": "Create Better Auth configuration",
50
+ "source": "files/lib/auth.ts",
51
+ "destination": "{{lib}}/auth.ts"
52
+ }
53
+ ]
54
+ }
@@ -0,0 +1,115 @@
1
+ {
2
+ "name": "clerk",
3
+ "displayName": "Clerk",
4
+ "description": "Clerk Authentication",
5
+ "category": "auth",
6
+ "provider": "clerk",
7
+ "supportedFrameworks": ["nextjs", "express", "react-vite"],
8
+ "frameworkConfigs": {
9
+ "nextjs": {
10
+ "dependencies": {
11
+ "@clerk/nextjs": "^6.10.2"
12
+ },
13
+ "envVars": [
14
+ {
15
+ "key": "NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY",
16
+ "value": "",
17
+ "description": "Clerk publishable key (from Clerk dashboard)",
18
+ "required": true
19
+ },
20
+ {
21
+ "key": "CLERK_SECRET_KEY",
22
+ "value": "",
23
+ "description": "Clerk secret key (from Clerk dashboard)",
24
+ "required": true
25
+ },
26
+ {
27
+ "key": "NEXT_PUBLIC_CLERK_SIGN_IN_URL",
28
+ "value": "/sign-in",
29
+ "description": "Sign in page URL",
30
+ "required": true
31
+ },
32
+ {
33
+ "key": "NEXT_PUBLIC_CLERK_SIGN_UP_URL",
34
+ "value": "/sign-up",
35
+ "description": "Sign up page URL",
36
+ "required": true
37
+ },
38
+ {
39
+ "key": "NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL",
40
+ "value": "/",
41
+ "description": "Redirect URL after sign in",
42
+ "required": true
43
+ },
44
+ {
45
+ "key": "NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL",
46
+ "value": "/",
47
+ "description": "Redirect URL after sign up",
48
+ "required": true
49
+ }
50
+ ],
51
+ "patches": [
52
+ {
53
+ "type": "create-file",
54
+ "description": "Create Clerk auth provider",
55
+ "source": "nextjs/auth-provider.tsx",
56
+ "destination": "{{lib}}/auth-provider.tsx"
57
+ },
58
+ {
59
+ "type": "create-file",
60
+ "description": "Create Clerk middleware",
61
+ "source": "nextjs/middleware.ts",
62
+ "destination": "middleware.ts"
63
+ }
64
+ ]
65
+ },
66
+ "express": {
67
+ "dependencies": {
68
+ "@clerk/express": "^1.3.0"
69
+ },
70
+ "envVars": [
71
+ {
72
+ "key": "CLERK_PUBLISHABLE_KEY",
73
+ "value": "",
74
+ "description": "Clerk publishable key (from Clerk dashboard)",
75
+ "required": true
76
+ },
77
+ {
78
+ "key": "CLERK_SECRET_KEY",
79
+ "value": "",
80
+ "description": "Clerk secret key (from Clerk dashboard)",
81
+ "required": true
82
+ }
83
+ ],
84
+ "patches": [
85
+ {
86
+ "type": "create-file",
87
+ "description": "Create Clerk auth utilities",
88
+ "source": "express/auth.ts",
89
+ "destination": "{{lib}}/auth.ts"
90
+ }
91
+ ]
92
+ },
93
+ "react-vite": {
94
+ "dependencies": {
95
+ "@clerk/clerk-react": "^5.15.0"
96
+ },
97
+ "envVars": [
98
+ {
99
+ "key": "VITE_CLERK_PUBLISHABLE_KEY",
100
+ "value": "",
101
+ "description": "Clerk publishable key (from Clerk dashboard)",
102
+ "required": true
103
+ }
104
+ ],
105
+ "patches": [
106
+ {
107
+ "type": "create-file",
108
+ "description": "Create Clerk auth provider",
109
+ "source": "react/auth-provider.tsx",
110
+ "destination": "{{lib}}/auth-provider.tsx"
111
+ }
112
+ ]
113
+ }
114
+ }
115
+ }
@@ -1,28 +1,45 @@
1
+ import "dotenv/config";
1
2
  import mongoose from "mongoose";
2
3
 
3
- const MONGODB_URI = process.env.MONGODB_URI || "mongodb://localhost:27017/myapp";
4
+ const MONGODB_URI = process.env.DATABASE_URL || "mongodb://localhost:27017/myapp";
4
5
 
5
6
  if (!MONGODB_URI) {
6
- throw new Error("Please define the MONGODB_URI environment variable");
7
+ throw new Error("Please define the DATABASE_URL environment variable");
7
8
  }
8
9
 
9
- let cached = global.mongoose;
10
+ interface MongooseCache {
11
+ conn: typeof mongoose | null;
12
+ promise: Promise<typeof mongoose> | null;
13
+ }
14
+
15
+ declare global {
16
+ var mongoose: MongooseCache | undefined;
17
+ }
18
+
19
+ const cached: MongooseCache = global.mongoose || { conn: null, promise: null };
10
20
 
11
- if (!cached) {
12
- cached = global.mongoose = { conn: null, promise: null };
21
+ if (!global.mongoose) {
22
+ global.mongoose = cached;
13
23
  }
14
24
 
15
- async function connectDB() {
25
+ async function connectDB(): Promise<typeof mongoose> {
16
26
  if (cached.conn) {
27
+ console.log("Using existing MongoDB connection");
17
28
  return cached.conn;
18
29
  }
19
30
 
20
31
  if (!cached.promise) {
21
32
  const opts = {
22
33
  bufferCommands: false,
34
+ maxPoolSize: 10, // Maintain up to 10 socket connections
35
+ serverSelectionTimeoutMS: 5000, // Keep trying to send operations for 5 seconds
36
+ socketTimeoutMS: 45000, // Close sockets after 45 seconds of inactivity
37
+ family: 4, // Use IPv4, skip trying IPv6
23
38
  };
24
39
 
40
+ console.log("Creating new MongoDB connection");
25
41
  cached.promise = mongoose.connect(MONGODB_URI, opts).then((mongoose) => {
42
+ console.log("MongoDB connected successfully");
26
43
  return mongoose;
27
44
  });
28
45
  }
@@ -31,10 +48,31 @@ async function connectDB() {
31
48
  cached.conn = await cached.promise;
32
49
  } catch (e) {
33
50
  cached.promise = null;
51
+ console.error("MongoDB connection error:", e);
34
52
  throw e;
35
53
  }
36
54
 
37
55
  return cached.conn;
38
56
  }
39
57
 
58
+ // Handle connection events
59
+ mongoose.connection.on("connected", () => {
60
+ console.log("Mongoose connected to MongoDB");
61
+ });
62
+
63
+ mongoose.connection.on("error", (err) => {
64
+ console.error("Mongoose connection error:", err);
65
+ });
66
+
67
+ mongoose.connection.on("disconnected", () => {
68
+ console.log("Mongoose disconnected");
69
+ });
70
+
71
+ // Close connection on app termination
72
+ process.on("SIGINT", async () => {
73
+ await mongoose.connection.close();
74
+ console.log("MongoDB connection closed due to app termination");
75
+ process.exit(0);
76
+ });
77
+
40
78
  export default connectDB;
@@ -0,0 +1,39 @@
1
+ import mongoose, { Document, Schema } from "mongoose";
2
+
3
+ export interface IUser extends Document {
4
+ name: string;
5
+ email: string;
6
+ password: string;
7
+ createdAt: Date;
8
+ updatedAt: Date;
9
+ }
10
+
11
+ const UserSchema: Schema = new Schema(
12
+ {
13
+ name: {
14
+ type: String,
15
+ required: true,
16
+ trim: true,
17
+ },
18
+ email: {
19
+ type: String,
20
+ required: true,
21
+ unique: true,
22
+ lowercase: true,
23
+ trim: true,
24
+ },
25
+ password: {
26
+ type: String,
27
+ required: true,
28
+ },
29
+ },
30
+ {
31
+ timestamps: true,
32
+ },
33
+ );
34
+
35
+ // Add indexes for better performance
36
+ UserSchema.index({ email: 1 });
37
+ UserSchema.index({ createdAt: -1 });
38
+
39
+ export default mongoose.models.User || mongoose.model<IUser>("User", UserSchema);
@@ -7,28 +7,35 @@
7
7
  "database": "mongodb",
8
8
  "supportedFrameworks": ["nextjs", "express"],
9
9
  "dependencies": {
10
- "mongoose": "^8.8.4"
10
+ "mongoose": "^8.8.4",
11
+ "dotenv": "^17.2.3"
12
+ },
13
+ "devDependencies": {
14
+ "@types/mongoose": "^5.11.97"
15
+ },
16
+ "envVars": {
17
+ "common": [
18
+ {
19
+ "key": "DATABASE_URL",
20
+ "value": "mongodb://localhost:27017/myapp",
21
+ "description": "MongoDB connection string",
22
+ "required": true
23
+ }
24
+ ]
11
25
  },
12
- "devDependencies": {},
13
- "envVars": [
14
- {
15
- "key": "MONGODB_URI",
16
- "value": "mongodb://localhost:27017/myapp",
17
- "description": "MongoDB connection string",
18
- "required": true
19
- }
20
- ],
21
26
  "frameworkPatches": {
22
27
  "express": {
23
28
  "tsconfig.json": {
24
29
  "merge": {
25
30
  "compilerOptions": {
31
+ "baseUrl": ".",
26
32
  "paths": {
27
33
  "@/*": ["./src/*"],
28
34
  "@/models/*": ["./src/models/*"]
29
35
  }
30
36
  },
31
- "include": ["src/**/*", "src/models/**/*"]
37
+ "include": ["src/**/*", "src/models/**/*"],
38
+ "exclude": ["node_modules", "dist"]
32
39
  }
33
40
  }
34
41
  },
@@ -36,10 +43,12 @@
36
43
  "tsconfig.json": {
37
44
  "merge": {
38
45
  "compilerOptions": {
46
+ "baseUrl": ".",
39
47
  "paths": {
40
48
  "@/models/*": ["./models/*"]
41
49
  }
42
- }
50
+ },
51
+ "exclude": ["node_modules", ".next", "out"]
43
52
  }
44
53
  }
45
54
  }
@@ -50,6 +59,12 @@
50
59
  "description": "Create MongoDB connection with Mongoose",
51
60
  "source": "lib/db.ts",
52
61
  "destination": "{{lib}}/db.ts"
62
+ },
63
+ {
64
+ "type": "create-file",
65
+ "description": "Create sample User model",
66
+ "source": "models/User.ts",
67
+ "destination": "{{models}}/User.ts"
53
68
  }
54
69
  ]
55
70
  }
@@ -0,0 +1,6 @@
1
+ import "dotenv/config";
2
+ import { PrismaClient } from "@/generated/prisma/client";
3
+
4
+ {{prismaClientInit}}
5
+
6
+ export { prisma };
@@ -0,0 +1,8 @@
1
+ generator client {
2
+ provider = "prisma-client"
3
+ output = "../generated/prisma"
4
+ }
5
+
6
+ datasource db {
7
+ provider = "{{provider}}"
8
+ }
@@ -0,0 +1,12 @@
1
+ import "dotenv/config";
2
+ import { defineConfig } from "prisma/config";
3
+
4
+ export default defineConfig({
5
+ schema: "prisma/schema.prisma",
6
+ migrations: {
7
+ path: "prisma/migrations",
8
+ },
9
+ datasource: {
10
+ url: process.env["DATABASE_URL"],
11
+ },
12
+ });