create-better-t-stack 2.40.3-canary.50a1b944 → 2.40.4-canary.49f23d48

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 (27) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/index.d.ts +2 -1
  3. package/dist/index.js +1 -1
  4. package/dist/{src-D59j3XG5.js → src-D32Mc1TH.js} +196 -53
  5. package/package.json +1 -1
  6. package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +32 -3
  7. package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +1 -1
  8. package/templates/backend/server/elysia/src/index.ts.hbs +31 -2
  9. package/templates/backend/server/express/src/index.ts.hbs +38 -4
  10. package/templates/backend/server/fastify/src/index.ts.hbs +33 -2
  11. package/templates/backend/server/hono/src/index.ts.hbs +40 -5
  12. package/templates/db/drizzle/mysql/src/db/index.ts.hbs +25 -0
  13. package/templates/db/prisma/mongodb/src/db/index.ts.hbs +5 -0
  14. package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +6 -0
  15. package/templates/db/prisma/mysql/src/db/index.ts.hbs +12 -0
  16. package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +3 -0
  17. package/templates/db/prisma/postgres/src/db/index.ts.hbs +5 -0
  18. package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +3 -0
  19. package/templates/db/prisma/sqlite/prisma.config.ts.hbs +18 -1
  20. package/templates/db/prisma/sqlite/src/db/index.ts.hbs +16 -0
  21. package/templates/deploy/alchemy/alchemy.run.ts.hbs +4 -0
  22. package/templates/deploy/wrangler/server/wrangler.jsonc.hbs +5 -0
  23. package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +2 -2
  24. package/templates/db/prisma/mongodb/prisma/index.ts.hbs +0 -5
  25. package/templates/db/prisma/mysql/prisma/index.ts +0 -5
  26. package/templates/db/prisma/postgres/prisma/index.ts +0 -5
  27. package/templates/db/prisma/sqlite/prisma/index.ts +0 -5
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { createBtsCli } from "./src-D59j3XG5.js";
2
+ import { createBtsCli } from "./src-D32Mc1TH.js";
3
3
 
4
4
  //#region src/cli.ts
5
5
  createBtsCli().run();
package/dist/index.d.ts CHANGED
@@ -80,6 +80,7 @@ declare const DatabaseSetupSchema: z.ZodEnum<{
80
80
  turso: "turso";
81
81
  neon: "neon";
82
82
  "prisma-postgres": "prisma-postgres";
83
+ planetscale: "planetscale";
83
84
  "mongodb-atlas": "mongodb-atlas";
84
85
  supabase: "supabase";
85
86
  d1: "d1";
@@ -218,7 +219,7 @@ declare const router: trpcServer.TRPCBuiltRouter<{
218
219
  git?: boolean | undefined;
219
220
  packageManager?: "npm" | "pnpm" | "bun" | undefined;
220
221
  install?: boolean | undefined;
221
- dbSetup?: "none" | "turso" | "neon" | "prisma-postgres" | "mongodb-atlas" | "supabase" | "d1" | "docker" | undefined;
222
+ dbSetup?: "none" | "turso" | "neon" | "prisma-postgres" | "planetscale" | "mongodb-atlas" | "supabase" | "d1" | "docker" | undefined;
222
223
  backend?: "none" | "next" | "hono" | "express" | "fastify" | "elysia" | "convex" | undefined;
223
224
  runtime?: "none" | "bun" | "node" | "workers" | undefined;
224
225
  api?: "none" | "trpc" | "orpc" | undefined;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { builder, createBtsCli, docs, init, router, sponsors } from "./src-D59j3XG5.js";
2
+ import { builder, createBtsCli, docs, init, router, sponsors } from "./src-D32Mc1TH.js";
3
3
 
4
4
  export { builder, createBtsCli, docs, init, router, sponsors };
@@ -67,6 +67,7 @@ const dependencyVersionMap = {
67
67
  "@clerk/clerk-expo": "^2.14.25",
68
68
  "drizzle-orm": "^0.44.2",
69
69
  "drizzle-kit": "^0.31.2",
70
+ "@planetscale/database": "^1.19.0",
70
71
  "@libsql/client": "^0.15.9",
71
72
  "@neondatabase/serverless": "^1.0.1",
72
73
  pg: "^8.14.1",
@@ -76,7 +77,10 @@ const dependencyVersionMap = {
76
77
  mysql2: "^3.14.0",
77
78
  "@prisma/client": "^6.15.0",
78
79
  prisma: "^6.15.0",
80
+ "@prisma/adapter-d1": "^6.15.0",
79
81
  "@prisma/extension-accelerate": "^2.0.2",
82
+ "@prisma/adapter-planetscale": "^6.15.0",
83
+ undici: "^7.15.0",
80
84
  mongoose: "^8.14.0",
81
85
  "vite-plugin-pwa": "^1.0.1",
82
86
  "@vite-pwa/assets-generator": "^1.0.0",
@@ -110,6 +114,8 @@ const dependencyVersionMap = {
110
114
  streamdown: "^1.1.6",
111
115
  "@orpc/server": "^1.8.6",
112
116
  "@orpc/client": "^1.8.6",
117
+ "@orpc/openapi": "^1.8.6",
118
+ "@orpc/zod": "^1.8.6",
113
119
  "@orpc/tanstack-query": "^1.8.6",
114
120
  "@trpc/tanstack-react-query": "^11.5.0",
115
121
  "@trpc/server": "^11.5.0",
@@ -134,7 +140,7 @@ const dependencyVersionMap = {
134
140
  "nitro-cloudflare-dev": "^0.2.2",
135
141
  "@sveltejs/adapter-cloudflare": "^7.2.1",
136
142
  "@cloudflare/workers-types": "^4.20250822.0",
137
- alchemy: "^0.63.0",
143
+ alchemy: "^0.65.0",
138
144
  nitropack: "^2.12.4",
139
145
  dotenv: "^17.2.1"
140
146
  };
@@ -233,6 +239,7 @@ const DatabaseSetupSchema = z.enum([
233
239
  "turso",
234
240
  "neon",
235
241
  "prisma-postgres",
242
+ "planetscale",
236
243
  "mongodb-atlas",
237
244
  "supabase",
238
245
  "d1",
@@ -737,11 +744,10 @@ async function getDatabaseChoice(database, backend, runtime) {
737
744
 
738
745
  //#endregion
739
746
  //#region src/prompts/database-setup.ts
740
- async function getDBSetupChoice(databaseType, dbSetup, orm, backend, runtime) {
747
+ async function getDBSetupChoice(databaseType, dbSetup, _orm, backend, runtime) {
741
748
  if (backend === "convex") return "none";
742
749
  if (dbSetup !== void 0) return dbSetup;
743
750
  if (databaseType === "none") return "none";
744
- if (databaseType === "sqlite" && orm === "prisma") return "none";
745
751
  let options = [];
746
752
  if (databaseType === "sqlite") options = [
747
753
  {
@@ -766,6 +772,11 @@ async function getDBSetupChoice(databaseType, dbSetup, orm, backend, runtime) {
766
772
  label: "Neon Postgres",
767
773
  hint: "Serverless Postgres with branching capability"
768
774
  },
775
+ {
776
+ value: "planetscale",
777
+ label: "PlanetScale",
778
+ hint: "Serverless MySQL platform with branching (Postgres compatible)"
779
+ },
769
780
  {
770
781
  value: "supabase",
771
782
  label: "Supabase",
@@ -787,15 +798,23 @@ async function getDBSetupChoice(databaseType, dbSetup, orm, backend, runtime) {
787
798
  hint: "Manual setup"
788
799
  }
789
800
  ];
790
- else if (databaseType === "mysql") options = [{
791
- value: "docker",
792
- label: "Docker",
793
- hint: "Run locally with docker compose"
794
- }, {
795
- value: "none",
796
- label: "None",
797
- hint: "Manual setup"
798
- }];
801
+ else if (databaseType === "mysql") options = [
802
+ {
803
+ value: "planetscale",
804
+ label: "PlanetScale",
805
+ hint: "Serverless MySQL platform with branching"
806
+ },
807
+ {
808
+ value: "docker",
809
+ label: "Docker",
810
+ hint: "Run locally with docker compose"
811
+ },
812
+ {
813
+ value: "none",
814
+ label: "None",
815
+ hint: "Manual setup"
816
+ }
817
+ ];
799
818
  else if (databaseType === "mongodb") options = [
800
819
  {
801
820
  value: "mongodb-atlas",
@@ -1651,6 +1670,7 @@ function validateDatabaseSetup(config, providedFlags) {
1651
1670
  database: "postgres",
1652
1671
  errorMessage: "Prisma PostgreSQL setup requires PostgreSQL database. Please use '--database postgres' or choose a different setup."
1653
1672
  },
1673
+ planetscale: { errorMessage: "PlanetScale setup requires PostgreSQL or MySQL database. Please use '--database postgres' or '--database mysql' or choose a different setup." },
1654
1674
  "mongodb-atlas": {
1655
1675
  database: "mongodb",
1656
1676
  errorMessage: "MongoDB Atlas setup requires MongoDB database. Please use '--database mongodb' or choose a different setup."
@@ -1669,7 +1689,9 @@ function validateDatabaseSetup(config, providedFlags) {
1669
1689
  };
1670
1690
  if (dbSetup && dbSetup !== "none") {
1671
1691
  const validation = setupValidations[dbSetup];
1672
- if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
1692
+ if (dbSetup === "planetscale") {
1693
+ if (database !== "postgres" && database !== "mysql") exitWithError(validation.errorMessage);
1694
+ } else if (validation.database && database !== validation.database) exitWithError(validation.errorMessage);
1673
1695
  if (validation.runtime && runtime !== validation.runtime) exitWithError(validation.errorMessage);
1674
1696
  if (dbSetup === "docker") {
1675
1697
  if (database === "sqlite") exitWithError("Docker setup is not compatible with SQLite database. SQLite is file-based and doesn't require Docker. Please use '--database postgres', '--database mysql', '--database mongodb', or choose a different setup.");
@@ -3044,8 +3066,11 @@ async function generateCloudflareWorkerTypes({ serverDir, packageManager }) {
3044
3066
  const s = spinner();
3045
3067
  try {
3046
3068
  s.start("Generating Cloudflare Workers types...");
3047
- const runCmd = packageManager === "npm" ? "npm" : packageManager;
3048
- await execa(runCmd, ["run", "cf-typegen"], { cwd: serverDir });
3069
+ const runCmd = getPackageExecutionCommand(packageManager, "wrangler types --env-interface CloudflareBindings");
3070
+ await execa(runCmd, {
3071
+ cwd: serverDir,
3072
+ shell: true
3073
+ });
3049
3074
  s.stop("Cloudflare Workers types generated successfully!");
3050
3075
  } catch {
3051
3076
  s.stop(pc.yellow("Failed to generate Cloudflare Workers types"));
@@ -3093,8 +3118,7 @@ async function setupNextAlchemyDeploy(projectDir, _packageManager, options) {
3093
3118
  if (!options?.skipAppScripts) pkg.scripts = {
3094
3119
  ...pkg.scripts,
3095
3120
  deploy: "alchemy deploy",
3096
- destroy: "alchemy destroy",
3097
- dev: "alchemy dev"
3121
+ destroy: "alchemy destroy"
3098
3122
  };
3099
3123
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3100
3124
  }
@@ -3119,8 +3143,7 @@ async function setupNuxtAlchemyDeploy(projectDir, _packageManager, options) {
3119
3143
  if (!options?.skipAppScripts) pkg.scripts = {
3120
3144
  ...pkg.scripts,
3121
3145
  deploy: "alchemy deploy",
3122
- destroy: "alchemy destroy",
3123
- dev: "alchemy dev"
3146
+ destroy: "alchemy destroy"
3124
3147
  };
3125
3148
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3126
3149
  }
@@ -3183,8 +3206,7 @@ async function setupReactRouterAlchemyDeploy(projectDir, _packageManager, option
3183
3206
  if (!options?.skipAppScripts) pkg.scripts = {
3184
3207
  ...pkg.scripts,
3185
3208
  deploy: "alchemy deploy",
3186
- destroy: "alchemy destroy",
3187
- dev: "alchemy dev"
3209
+ destroy: "alchemy destroy"
3188
3210
  };
3189
3211
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3190
3212
  }
@@ -3205,8 +3227,7 @@ async function setupSolidAlchemyDeploy(projectDir, _packageManager, options) {
3205
3227
  if (!options?.skipAppScripts) pkg.scripts = {
3206
3228
  ...pkg.scripts,
3207
3229
  deploy: "alchemy deploy",
3208
- destroy: "alchemy destroy",
3209
- dev: "alchemy dev"
3230
+ destroy: "alchemy destroy"
3210
3231
  };
3211
3232
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3212
3233
  }
@@ -3231,8 +3252,7 @@ async function setupSvelteAlchemyDeploy(projectDir, _packageManager, options) {
3231
3252
  if (!options?.skipAppScripts) pkg.scripts = {
3232
3253
  ...pkg.scripts,
3233
3254
  deploy: "alchemy deploy",
3234
- destroy: "alchemy destroy",
3235
- dev: "alchemy dev"
3255
+ destroy: "alchemy destroy"
3236
3256
  };
3237
3257
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3238
3258
  }
@@ -3298,8 +3318,7 @@ async function setupTanStackRouterAlchemyDeploy(projectDir, _packageManager, opt
3298
3318
  if (!options?.skipAppScripts) pkg.scripts = {
3299
3319
  ...pkg.scripts,
3300
3320
  deploy: "alchemy deploy",
3301
- destroy: "alchemy destroy",
3302
- dev: "alchemy dev"
3321
+ destroy: "alchemy destroy"
3303
3322
  };
3304
3323
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3305
3324
  }
@@ -3324,8 +3343,7 @@ async function setupTanStackStartAlchemyDeploy(projectDir, _packageManager, opti
3324
3343
  if (!options?.skipAppScripts) pkg.scripts = {
3325
3344
  ...pkg.scripts,
3326
3345
  deploy: "alchemy deploy",
3327
- destroy: "alchemy destroy",
3328
- dev: "alchemy dev"
3346
+ destroy: "alchemy destroy"
3329
3347
  };
3330
3348
  await fs.writeJson(pkgPath, pkg, { spaces: 2 });
3331
3349
  }
@@ -3889,7 +3907,12 @@ function getFrontendType(frontend) {
3889
3907
  }
3890
3908
  function getApiDependencies(api, frontendType) {
3891
3909
  const deps = {};
3892
- if (api === "orpc") deps.server = { dependencies: ["@orpc/server", "@orpc/client"] };
3910
+ if (api === "orpc") deps.server = { dependencies: [
3911
+ "@orpc/server",
3912
+ "@orpc/client",
3913
+ "@orpc/openapi",
3914
+ "@orpc/zod"
3915
+ ] };
3893
3916
  else if (api === "trpc") deps.server = { dependencies: ["@trpc/server", "@trpc/client"] };
3894
3917
  if (frontendType.hasReactWeb) {
3895
3918
  if (api === "orpc") deps.web = { dependencies: ["@orpc/tanstack-query", "@orpc/client"] };
@@ -4374,7 +4397,7 @@ async function setupEnvironmentVariables(config) {
4374
4397
  //#endregion
4375
4398
  //#region src/helpers/database-providers/d1-setup.ts
4376
4399
  async function setupCloudflareD1(config) {
4377
- const { projectDir, serverDeploy } = config;
4400
+ const { projectDir, serverDeploy, orm } = config;
4378
4401
  if (serverDeploy === "wrangler") {
4379
4402
  const envPath = path.join(projectDir, "apps/server", ".env");
4380
4403
  const variables = [
@@ -4398,6 +4421,22 @@ async function setupCloudflareD1(config) {
4398
4421
  await addEnvVariablesToFile(envPath, variables);
4399
4422
  } catch (_err) {}
4400
4423
  }
4424
+ if ((serverDeploy === "wrangler" || serverDeploy === "alchemy") && orm === "prisma") {
4425
+ const envPath = path.join(projectDir, "apps/server", ".env");
4426
+ const variables = [{
4427
+ key: "DATABASE_URL",
4428
+ value: "file:./local.db",
4429
+ condition: true
4430
+ }];
4431
+ try {
4432
+ await addEnvVariablesToFile(envPath, variables);
4433
+ } catch (_err) {}
4434
+ const serverDir = path.join(projectDir, "apps/server");
4435
+ await addPackageDependency({
4436
+ dependencies: ["@prisma/adapter-d1"],
4437
+ projectDir: serverDir
4438
+ });
4439
+ }
4401
4440
  }
4402
4441
 
4403
4442
  //#endregion
@@ -4703,6 +4742,71 @@ async function setupNeonPostgres(config) {
4703
4742
  }
4704
4743
  }
4705
4744
 
4745
+ //#endregion
4746
+ //#region src/helpers/database-providers/planetscale-setup.ts
4747
+ async function setupPlanetScale(config) {
4748
+ const { projectDir, database, orm } = config;
4749
+ const envPath = path.join(projectDir, "apps/server", ".env");
4750
+ if (database === "mysql" && orm === "drizzle") {
4751
+ const variables = [
4752
+ {
4753
+ key: "# enable foreign key constraints in database settings",
4754
+ value: "",
4755
+ condition: true
4756
+ },
4757
+ {
4758
+ key: "DATABASE_URL",
4759
+ value: "mysql://username:password@host/database?ssl={\"rejectUnauthorized\":true}",
4760
+ condition: true
4761
+ },
4762
+ {
4763
+ key: "DATABASE_HOST",
4764
+ value: "",
4765
+ condition: true
4766
+ },
4767
+ {
4768
+ key: "DATABASE_USERNAME",
4769
+ value: "",
4770
+ condition: true
4771
+ },
4772
+ {
4773
+ key: "DATABASE_PASSWORD",
4774
+ value: "",
4775
+ condition: true
4776
+ }
4777
+ ];
4778
+ await fs.ensureDir(path.join(projectDir, "apps/server"));
4779
+ await addEnvVariablesToFile(envPath, variables);
4780
+ }
4781
+ if (database === "postgres" && orm === "prisma") {
4782
+ const variables = [{
4783
+ key: "DATABASE_URL",
4784
+ value: "postgresql://username:password@host/database?sslaccept=strict",
4785
+ condition: true
4786
+ }];
4787
+ await fs.ensureDir(path.join(projectDir, "apps/server"));
4788
+ await addEnvVariablesToFile(envPath, variables);
4789
+ }
4790
+ if (database === "postgres" && orm === "drizzle") {
4791
+ const variables = [{
4792
+ key: "DATABASE_URL",
4793
+ value: "postgresql://username:password@host/database?sslmode=verify-full",
4794
+ condition: true
4795
+ }];
4796
+ await fs.ensureDir(path.join(projectDir, "apps/server"));
4797
+ await addEnvVariablesToFile(envPath, variables);
4798
+ }
4799
+ if (database === "mysql" && orm === "prisma") {
4800
+ const variables = [{
4801
+ key: "DATABASE_URL",
4802
+ value: "mysql://username:password@host/database?sslaccept=strict",
4803
+ condition: true
4804
+ }];
4805
+ await fs.ensureDir(path.join(projectDir, "apps/server"));
4806
+ await addEnvVariablesToFile(envPath, variables);
4807
+ }
4808
+ }
4809
+
4706
4810
  //#endregion
4707
4811
  //#region src/helpers/database-providers/prisma-postgres-setup.ts
4708
4812
  const AVAILABLE_REGIONS = [
@@ -5251,7 +5355,16 @@ async function setupDatabase(config) {
5251
5355
  const serverDir = path.join(projectDir, "apps/server");
5252
5356
  if (!await fs.pathExists(serverDir)) return;
5253
5357
  try {
5254
- if (orm === "prisma") await addPackageDependency({
5358
+ if (orm === "prisma") if (database === "mysql" && dbSetup === "planetscale") await addPackageDependency({
5359
+ dependencies: [
5360
+ "@prisma/client",
5361
+ "@prisma/adapter-planetscale",
5362
+ "undici"
5363
+ ],
5364
+ devDependencies: ["prisma"],
5365
+ projectDir: serverDir
5366
+ });
5367
+ else await addPackageDependency({
5255
5368
  dependencies: ["@prisma/client"],
5256
5369
  devDependencies: ["prisma"],
5257
5370
  projectDir: serverDir
@@ -5271,12 +5384,22 @@ async function setupDatabase(config) {
5271
5384
  devDependencies: ["drizzle-kit", "@types/ws"],
5272
5385
  projectDir: serverDir
5273
5386
  });
5387
+ else if (dbSetup === "planetscale") await addPackageDependency({
5388
+ dependencies: ["drizzle-orm", "pg"],
5389
+ devDependencies: ["drizzle-kit", "@types/pg"],
5390
+ projectDir: serverDir
5391
+ });
5274
5392
  else await addPackageDependency({
5275
5393
  dependencies: ["drizzle-orm", "pg"],
5276
5394
  devDependencies: ["drizzle-kit", "@types/pg"],
5277
5395
  projectDir: serverDir
5278
5396
  });
5279
- else if (database === "mysql") await addPackageDependency({
5397
+ else if (database === "mysql") if (dbSetup === "planetscale") await addPackageDependency({
5398
+ dependencies: ["drizzle-orm", "@planetscale/database"],
5399
+ devDependencies: ["drizzle-kit"],
5400
+ projectDir: serverDir
5401
+ });
5402
+ else await addPackageDependency({
5280
5403
  dependencies: ["drizzle-orm", "mysql2"],
5281
5404
  devDependencies: ["drizzle-kit"],
5282
5405
  projectDir: serverDir
@@ -5292,7 +5415,10 @@ async function setupDatabase(config) {
5292
5415
  else if (database === "postgres") {
5293
5416
  if (dbSetup === "prisma-postgres") await setupPrismaPostgres(config);
5294
5417
  else if (dbSetup === "neon") await setupNeonPostgres(config);
5418
+ else if (dbSetup === "planetscale") await setupPlanetScale(config);
5295
5419
  else if (dbSetup === "supabase") await setupSupabase(config);
5420
+ } else if (database === "mysql") {
5421
+ if (dbSetup === "planetscale") await setupPlanetScale(config);
5296
5422
  } else if (database === "mongodb" && dbSetup === "mongodb-atlas") await setupMongoDBAtlas(config);
5297
5423
  } catch (error) {
5298
5424
  s.stop(pc.red("Failed to set up database"));
@@ -5737,7 +5863,7 @@ async function getDockerStatus(database) {
5737
5863
  //#endregion
5738
5864
  //#region src/helpers/core/post-installation.ts
5739
5865
  async function displayPostInstallInstructions(config) {
5740
- const { database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
5866
+ const { api, database, relativePath, packageManager, depsInstalled, orm, addons, runtime, frontend, backend, dbSetup, webDeploy, serverDeploy } = config;
5741
5867
  const isConvex = backend === "convex";
5742
5868
  const runCmd = packageManager === "npm" ? "npm run" : packageManager === "pnpm" ? "pnpm run" : "bun run";
5743
5869
  const cdCmd = `cd ${relativePath}`;
@@ -5785,7 +5911,11 @@ async function displayPostInstallInstructions(config) {
5785
5911
  output += `${pc.bold("Your project will be available at:")}\n`;
5786
5912
  if (hasWeb) output += `${pc.cyan("•")} Frontend: http://localhost:${webPort}\n`;
5787
5913
  else if (!hasNative && !addons?.includes("starlight")) output += `${pc.yellow("NOTE:")} You are creating a backend-only app\n (no frontend selected)\n`;
5788
- if (!isConvex) output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
5914
+ if (!isConvex) {
5915
+ output += `${pc.cyan("•")} Backend API: http://localhost:3000\n`;
5916
+ if (api === "orpc") if (backend === "next") output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/rpc/api\n`;
5917
+ else output += `${pc.cyan("•")} OpenAPI (Scalar UI): http://localhost:3000/api\n`;
5918
+ }
5789
5919
  if (addons?.includes("starlight")) output += `${pc.cyan("•")} Docs: http://localhost:4321\n`;
5790
5920
  if (addons?.includes("fumadocs")) output += `${pc.cyan("•")} Fumadocs: http://localhost:4000\n`;
5791
5921
  if (nativeInstructions) output += `\n${nativeInstructions.trim()}\n`;
@@ -5834,7 +5964,13 @@ async function getDatabaseInstructions(database, orm, runCmd, _runtime, dbSetup,
5834
5964
  instructions.push(`${pc.cyan("5.")} Apply migrations locally: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME --local`)}`);
5835
5965
  instructions.push(`${pc.cyan("6.")} Apply migrations to production: ${pc.white(`${packageManager} wrangler d1 migrations apply YOUR_DB_NAME`)}`);
5836
5966
  }
5837
- if (dbSetup === "d1" && serverDeploy === "alchemy") instructions.push(`${pc.yellow("NOTE:")} D1 migrations are automatically handled by Alchemy`);
5967
+ if (dbSetup === "d1" && serverDeploy === "alchemy") {
5968
+ if (orm === "drizzle") instructions.push(`${pc.yellow("NOTE:")} D1 migrations are automatically handled by Alchemy`);
5969
+ else if (orm === "prisma") {
5970
+ instructions.push(`${pc.cyan("•")} Generate migrations: ${`${runCmd} db:generate`}`);
5971
+ instructions.push(`${pc.cyan("•")} Apply migrations: ${`${runCmd} db:migrate`}`);
5972
+ }
5973
+ }
5838
5974
  if (orm === "prisma") {
5839
5975
  if (dbSetup === "turso") instructions.push(`${pc.yellow("NOTE:")} Turso support with Prisma is in Early Access and requires\n additional setup. Learn more at:\n https://www.prisma.io/docs/orm/overview/databases/turso`);
5840
5976
  if (database === "mongodb" && dbSetup === "docker") instructions.push(`${pc.yellow("WARNING:")} Prisma + MongoDB + Docker combination\n may not work.`);
@@ -5921,7 +6057,7 @@ async function updateRootPackageJson(projectDir, options) {
5921
6057
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `turbo -F ${backendPackageName} db:studio`;
5922
6058
  if (options.orm === "prisma") {
5923
6059
  scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
5924
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
6060
+ scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
5925
6061
  } else if (options.orm === "drizzle") {
5926
6062
  scripts["db:generate"] = `turbo -F ${backendPackageName} db:generate`;
5927
6063
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `turbo -F ${backendPackageName} db:migrate`;
@@ -5946,7 +6082,7 @@ async function updateRootPackageJson(projectDir, options) {
5946
6082
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `pnpm --filter ${backendPackageName} db:studio`;
5947
6083
  if (options.orm === "prisma") {
5948
6084
  scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
5949
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
6085
+ scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
5950
6086
  } else if (options.orm === "drizzle") {
5951
6087
  scripts["db:generate"] = `pnpm --filter ${backendPackageName} db:generate`;
5952
6088
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `pnpm --filter ${backendPackageName} db:migrate`;
@@ -5971,7 +6107,7 @@ async function updateRootPackageJson(projectDir, options) {
5971
6107
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `npm run db:studio --workspace ${backendPackageName}`;
5972
6108
  if (options.orm === "prisma") {
5973
6109
  scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
5974
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
6110
+ scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
5975
6111
  } else if (options.orm === "drizzle") {
5976
6112
  scripts["db:generate"] = `npm run db:generate --workspace ${backendPackageName}`;
5977
6113
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `npm run db:migrate --workspace ${backendPackageName}`;
@@ -5996,7 +6132,7 @@ async function updateRootPackageJson(projectDir, options) {
5996
6132
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = `bun run --filter ${backendPackageName} db:studio`;
5997
6133
  if (options.orm === "prisma") {
5998
6134
  scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
5999
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6135
+ scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
6000
6136
  } else if (options.orm === "drizzle") {
6001
6137
  scripts["db:generate"] = `bun run --filter ${backendPackageName} db:generate`;
6002
6138
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = `bun run --filter ${backendPackageName} db:migrate`;
@@ -6039,7 +6175,7 @@ async function updateServerPackageJson(projectDir, options) {
6039
6175
  scripts["db:push"] = "prisma db push";
6040
6176
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = "prisma studio";
6041
6177
  scripts["db:generate"] = "prisma generate";
6042
- if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:migrate"] = "prisma migrate dev";
6178
+ scripts["db:migrate"] = "prisma migrate dev";
6043
6179
  } else if (options.orm === "drizzle") {
6044
6180
  scripts["db:push"] = "drizzle-kit push";
6045
6181
  if (!(options.dbSetup === "d1" && options.serverDeploy === "alchemy")) scripts["db:studio"] = "drizzle-kit studio";
@@ -6362,7 +6498,7 @@ async function openUrl(url) {
6362
6498
 
6363
6499
  //#endregion
6364
6500
  //#region src/utils/sponsors.ts
6365
- const SPONSORS_JSON_URL = "https://sponsors.amanv.dev/sponsors.json";
6501
+ const SPONSORS_JSON_URL = "https://sponsors.better-t-stack.dev/sponsors.json";
6366
6502
  async function fetchSponsors(url = SPONSORS_JSON_URL) {
6367
6503
  const s = spinner();
6368
6504
  s.start("Fetching sponsors…");
@@ -6376,23 +6512,30 @@ async function fetchSponsors(url = SPONSORS_JSON_URL) {
6376
6512
  return sponsors$1;
6377
6513
  }
6378
6514
  function displaySponsors(sponsors$1) {
6379
- if (sponsors$1.length === 0) {
6515
+ const { total_sponsors } = sponsors$1.summary;
6516
+ if (total_sponsors === 0) {
6380
6517
  log.info("No sponsors found. You can be the first one! ✨");
6381
6518
  outro(pc.cyan("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
6382
6519
  return;
6383
6520
  }
6384
- sponsors$1.forEach((entry, idx) => {
6385
- const sponsor = entry.sponsor;
6386
- const displayName = sponsor.name ?? sponsor.login;
6387
- const tier = entry.tierName ? ` (${entry.tierName})` : "";
6388
- log.step(`${idx + 1}. ${pc.green(displayName)}${pc.yellow(tier)}`);
6389
- log.message(` ${pc.dim("GitHub:")} https://github.com/${sponsor.login}`);
6390
- const website = sponsor.websiteUrl ?? sponsor.linkUrl;
6391
- if (website) log.message(` ${pc.dim("Website:")} ${website}`);
6392
- });
6393
- log.message("");
6521
+ displaySponsorsBox(sponsors$1);
6522
+ if (total_sponsors - sponsors$1.specialSponsors.length > 0) log.message(pc.blue(`+${total_sponsors - sponsors$1.specialSponsors.length} more amazing sponsors.\n`));
6394
6523
  outro(pc.magenta("Visit https://github.com/sponsors/AmanVarshney01 to become a sponsor."));
6395
6524
  }
6525
+ function displaySponsorsBox(sponsors$1) {
6526
+ if (sponsors$1.specialSponsors.length === 0) return;
6527
+ let output = `${pc.bold(pc.cyan("-> Special Sponsors"))}\n\n`;
6528
+ sponsors$1.specialSponsors.forEach((sponsor, idx) => {
6529
+ const displayName = sponsor.name ?? sponsor.githubId;
6530
+ const tier = sponsor.tierName ? ` ${pc.yellow(`(${sponsor.tierName})`)}` : "";
6531
+ output += `${pc.green(`• ${displayName}`)}${tier}\n`;
6532
+ output += ` ${pc.dim("GitHub:")} https://github.com/${sponsor.githubId}\n`;
6533
+ const website = sponsor.websiteUrl ?? sponsor.githubUrl;
6534
+ if (website) output += ` ${pc.dim("Website:")} ${website}\n`;
6535
+ if (idx < sponsors$1.specialSponsors.length - 1) output += "\n";
6536
+ });
6537
+ consola$1.box(output);
6538
+ }
6396
6539
 
6397
6540
  //#endregion
6398
6541
  //#region src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-better-t-stack",
3
- "version": "2.40.3-canary.50a1b944",
3
+ "version": "2.40.4-canary.49f23d48",
4
4
  "description": "A modern CLI tool for scaffolding end-to-end type-safe TypeScript projects with best practices and customizable configurations",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -2,18 +2,47 @@
2
2
  import { createContext } from '@/lib/context'
3
3
  {{/if}}
4
4
  import { appRouter } from '@/routers'
5
+ import { OpenAPIHandler } from '@orpc/openapi/fetch'
6
+ import { OpenAPIReferencePlugin } from '@orpc/openapi/plugins'
7
+ import { ZodToJsonSchemaConverter } from '@orpc/zod/zod4'
5
8
  import { RPCHandler } from '@orpc/server/fetch'
9
+ import { onError } from '@orpc/server'
6
10
  import { NextRequest } from 'next/server'
7
11
 
8
- const handler = new RPCHandler(appRouter)
12
+ const rpcHandler = new RPCHandler(appRouter, {
13
+ interceptors: [
14
+ onError((error) => {
15
+ console.error(error)
16
+ }),
17
+ ],
18
+ })
19
+ const apiHandler = new OpenAPIHandler(appRouter, {
20
+ plugins: [
21
+ new OpenAPIReferencePlugin({
22
+ schemaConverters: [new ZodToJsonSchemaConverter()],
23
+ }),
24
+ ],
25
+ interceptors: [
26
+ onError((error) => {
27
+ console.error(error)
28
+ }),
29
+ ],
30
+ })
9
31
 
10
32
  async function handleRequest(req: NextRequest) {
11
- const { response } = await handler.handle(req, {
33
+ const rpcResult = await rpcHandler.handle(req, {
12
34
  prefix: '/rpc',
13
35
  context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
14
36
  })
37
+ if (rpcResult.response) return rpcResult.response
15
38
 
16
- return response ?? new Response('Not found', { status: 404 })
39
+ const apiResult = await apiHandler.handle(req, {
40
+ prefix: '/rpc/api',
41
+ context: {{#if (eq auth "better-auth")}}await createContext(req){{else}}{}{{/if}},
42
+ })
43
+ if (apiResult.response) return apiResult.response
44
+
45
+ return new Response('Not found', { status: 404 })
17
46
  }
18
47
 
19
48
  export const GET = handleRequest
@@ -4,7 +4,7 @@ import { prismaAdapter } from "better-auth/adapters/prisma";
4
4
  {{#if (or (includes frontend "native-nativewind") (includes frontend "native-unistyles"))}}
5
5
  import { expo } from "@better-auth/expo";
6
6
  {{/if}}
7
- import prisma from "../../prisma";
7
+ import prisma from "@/db";
8
8
 
9
9
  export const auth = betterAuth({
10
10
  database: prismaAdapter(prisma, {
@@ -10,7 +10,11 @@ import { appRouter } from "./routers/index";
10
10
  import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
11
11
  {{/if}}
12
12
  {{#if (eq api "orpc")}}
13
+ import { OpenAPIHandler } from "@orpc/openapi/fetch";
14
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
15
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
13
16
  import { RPCHandler } from "@orpc/server/fetch";
17
+ import { onError } from "@orpc/server";
14
18
  import { appRouter } from "./routers";
15
19
  import { createContext } from "./lib/context";
16
20
  {{/if}}
@@ -19,7 +23,25 @@ import { auth } from "./lib/auth";
19
23
  {{/if}}
20
24
 
21
25
  {{#if (eq api "orpc")}}
22
- const handler = new RPCHandler(appRouter);
26
+ const rpcHandler = new RPCHandler(appRouter, {
27
+ interceptors: [
28
+ onError((error) => {
29
+ console.error(error);
30
+ }),
31
+ ],
32
+ });
33
+ const apiHandler = new OpenAPIHandler(appRouter, {
34
+ plugins: [
35
+ new OpenAPIReferencePlugin({
36
+ schemaConverters: [new ZodToJsonSchemaConverter()],
37
+ }),
38
+ ],
39
+ interceptors: [
40
+ onError((error) => {
41
+ console.error(error);
42
+ }),
43
+ ],
44
+ });
23
45
  {{/if}}
24
46
 
25
47
  {{#if (eq runtime "node")}}
@@ -48,12 +70,19 @@ const app = new Elysia()
48
70
  {{/if}}
49
71
  {{#if (eq api "orpc")}}
50
72
  .all('/rpc*', async (context) => {
51
- const { response } = await handler.handle(context.request, {
73
+ const { response } = await rpcHandler.handle(context.request, {
52
74
  prefix: '/rpc',
53
75
  context: await createContext({ context })
54
76
  })
55
77
  return response ?? new Response('Not Found', { status: 404 })
56
78
  })
79
+ .all('/api*', async (context) => {
80
+ const { response } = await apiHandler.handle(context.request, {
81
+ prefix: '/api',
82
+ context: await createContext({ context })
83
+ })
84
+ return response ?? new Response('Not Found', { status: 404 })
85
+ })
57
86
  {{/if}}
58
87
  {{#if (eq api "trpc")}}
59
88
  .all("/trpc/*", async (context) => {
@@ -5,7 +5,11 @@ import { createContext } from "./lib/context";
5
5
  import { appRouter } from "./routers/index";
6
6
  {{/if}}
7
7
  {{#if (eq api "orpc")}}
8
+ import { OpenAPIHandler } from "@orpc/openapi/node";
9
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
10
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
8
11
  import { RPCHandler } from "@orpc/server/node";
12
+ import { onError } from "@orpc/server";
9
13
  import { appRouter } from "./routers";
10
14
  {{#if (eq auth "better-auth")}}
11
15
  import { createContext } from "./lib/context";
@@ -50,9 +54,28 @@ app.use(
50
54
  {{/if}}
51
55
 
52
56
  {{#if (eq api "orpc")}}
53
- const handler = new RPCHandler(appRouter);
54
- app.use("/rpc{*path}", async (req, res, next) => {
55
- const { matched } = await handler.handle(req, res, {
57
+ const rpcHandler = new RPCHandler(appRouter, {
58
+ interceptors: [
59
+ onError((error) => {
60
+ console.error(error);
61
+ }),
62
+ ],
63
+ });
64
+ const apiHandler = new OpenAPIHandler(appRouter, {
65
+ plugins: [
66
+ new OpenAPIReferencePlugin({
67
+ schemaConverters: [new ZodToJsonSchemaConverter()],
68
+ }),
69
+ ],
70
+ interceptors: [
71
+ onError((error) => {
72
+ console.error(error);
73
+ }),
74
+ ],
75
+ });
76
+
77
+ app.use(async (req, res, next) => {
78
+ const rpcResult = await rpcHandler.handle(req, res, {
56
79
  prefix: "/rpc",
57
80
  {{#if (eq auth "better-auth")}}
58
81
  context: await createContext({ req }),
@@ -60,7 +83,18 @@ app.use("/rpc{*path}", async (req, res, next) => {
60
83
  context: {},
61
84
  {{/if}}
62
85
  });
63
- if (matched) return;
86
+ if (rpcResult.matched) return;
87
+
88
+ const apiResult = await apiHandler.handle(req, res, {
89
+ prefix: "/api",
90
+ {{#if (eq auth "better-auth")}}
91
+ context: await createContext({ req }),
92
+ {{else}}
93
+ context: {},
94
+ {{/if}}
95
+ });
96
+ if (apiResult.matched) return;
97
+
64
98
  next();
65
99
  });
66
100
  {{/if}}
@@ -9,8 +9,12 @@ import { appRouter, type AppRouter } from "./routers/index";
9
9
  {{/if}}
10
10
 
11
11
  {{#if (eq api "orpc")}}
12
+ import { OpenAPIHandler } from "@orpc/openapi/node";
13
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
14
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
12
15
  import { RPCHandler } from "@orpc/server/node";
13
16
  import { CORSPlugin } from "@orpc/server/plugins";
17
+ import { onError } from "@orpc/server";
14
18
  import { appRouter } from "./routers/index";
15
19
  import { createServer } from "node:http";
16
20
  {{#if (eq auth "better-auth")}}
@@ -40,7 +44,7 @@ const baseCorsConfig = {
40
44
  };
41
45
 
42
46
  {{#if (eq api "orpc")}}
43
- const handler = new RPCHandler(appRouter, {
47
+ const rpcHandler = new RPCHandler(appRouter, {
44
48
  plugins: [
45
49
  new CORSPlugin({
46
50
  origin: process.env.CORS_ORIGIN,
@@ -48,13 +52,31 @@ const handler = new RPCHandler(appRouter, {
48
52
  allowHeaders: ["Content-Type", "Authorization"],
49
53
  }),
50
54
  ],
55
+ interceptors: [
56
+ onError((error) => {
57
+ console.error(error);
58
+ }),
59
+ ],
60
+ });
61
+
62
+ const apiHandler = new OpenAPIHandler(appRouter, {
63
+ plugins: [
64
+ new OpenAPIReferencePlugin({
65
+ schemaConverters: [new ZodToJsonSchemaConverter()],
66
+ }),
67
+ ],
68
+ interceptors: [
69
+ onError((error) => {
70
+ console.error(error);
71
+ }),
72
+ ],
51
73
  });
52
74
 
53
75
  const fastify = Fastify({
54
76
  logger: true,
55
77
  serverFactory: (fastifyHandler) => {
56
78
  const server = createServer(async (req, res) => {
57
- const { matched } = await handler.handle(req, res, {
79
+ const { matched } = await rpcHandler.handle(req, res, {
58
80
  context: await createContext(req.headers),
59
81
  prefix: "/rpc",
60
82
  });
@@ -63,6 +85,15 @@ const fastify = Fastify({
63
85
  return;
64
86
  }
65
87
 
88
+ const apiResult = await apiHandler.handle(req, res, {
89
+ context: await createContext(req.headers),
90
+ prefix: "/api",
91
+ });
92
+
93
+ if (apiResult.matched) {
94
+ return;
95
+ }
96
+
66
97
  fastifyHandler(req, res);
67
98
  });
68
99
 
@@ -5,7 +5,11 @@ import "dotenv/config";
5
5
  import { env } from "cloudflare:workers";
6
6
  {{/if}}
7
7
  {{#if (eq api "orpc")}}
8
+ import { OpenAPIHandler } from "@orpc/openapi/fetch";
9
+ import { OpenAPIReferencePlugin } from "@orpc/openapi/plugins";
10
+ import { ZodToJsonSchemaConverter } from "@orpc/zod/zod4";
8
11
  import { RPCHandler } from "@orpc/server/fetch";
12
+ import { onError } from "@orpc/server";
9
13
  import { createContext } from "./lib/context";
10
14
  import { appRouter } from "./routers/index";
11
15
  {{/if}}
@@ -54,17 +58,48 @@ app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));
54
58
  {{/if}}
55
59
 
56
60
  {{#if (eq api "orpc")}}
57
- const handler = new RPCHandler(appRouter);
58
- app.use("/rpc/*", async (c, next) => {
61
+ export const apiHandler = new OpenAPIHandler(appRouter, {
62
+ plugins: [
63
+ new OpenAPIReferencePlugin({
64
+ schemaConverters: [new ZodToJsonSchemaConverter()],
65
+ }),
66
+ ],
67
+ interceptors: [
68
+ onError((error) => {
69
+ console.error(error);
70
+ }),
71
+ ],
72
+ });
73
+
74
+ export const rpcHandler = new RPCHandler(appRouter, {
75
+ interceptors: [
76
+ onError((error) => {
77
+ console.error(error);
78
+ }),
79
+ ],
80
+ });
81
+
82
+ app.use("/*", async (c, next) => {
59
83
  const context = await createContext({ context: c });
60
- const { matched, response } = await handler.handle(c.req.raw, {
84
+
85
+ const rpcResult = await rpcHandler.handle(c.req.raw, {
61
86
  prefix: "/rpc",
62
87
  context: context,
63
88
  });
64
89
 
65
- if (matched) {
66
- return c.newResponse(response.body, response);
90
+ if (rpcResult.matched) {
91
+ return c.newResponse(rpcResult.response.body, rpcResult.response);
67
92
  }
93
+
94
+ const apiResult = await apiHandler.handle(c.req.raw, {
95
+ prefix: "/api",
96
+ context: context,
97
+ });
98
+
99
+ if (apiResult.matched) {
100
+ return c.newResponse(apiResult.response.body, apiResult.response);
101
+ }
102
+
68
103
  await next();
69
104
  });
70
105
  {{/if}}
@@ -1,4 +1,15 @@
1
1
  {{#if (or (eq runtime "bun") (eq runtime "node"))}}
2
+ {{#if (eq dbSetup "planetscale")}}
3
+ import { drizzle } from "drizzle-orm/planetscale-serverless";
4
+
5
+ export const db = drizzle({
6
+ connection: {
7
+ host: process.env.DATABASE_HOST,
8
+ username: process.env.DATABASE_USERNAME,
9
+ password: process.env.DATABASE_PASSWORD,
10
+ },
11
+ });
12
+ {{else}}
2
13
  import { drizzle } from "drizzle-orm/mysql2";
3
14
 
4
15
  export const db = drizzle({
@@ -7,8 +18,21 @@ export const db = drizzle({
7
18
  },
8
19
  });
9
20
  {{/if}}
21
+ {{/if}}
10
22
 
11
23
  {{#if (eq runtime "workers")}}
24
+ {{#if (eq dbSetup "planetscale")}}
25
+ import { drizzle } from "drizzle-orm/planetscale-serverless";
26
+ import { env } from "cloudflare:workers";
27
+
28
+ export const db = drizzle({
29
+ connection: {
30
+ host: env.DATABASE_HOST,
31
+ username: env.DATABASE_USERNAME,
32
+ password: env.DATABASE_PASSWORD,
33
+ },
34
+ });
35
+ {{else}}
12
36
  import { drizzle } from "drizzle-orm/mysql2";
13
37
  import { env } from "cloudflare:workers";
14
38
 
@@ -18,3 +42,4 @@ export const db = drizzle({
18
42
  },
19
43
  });
20
44
  {{/if}}
45
+ {{/if}}
@@ -0,0 +1,5 @@
1
+ import { PrismaClient } from "../../prisma/generated/client";
2
+
3
+ const prisma = new PrismaClient();
4
+
5
+ export default prisma;
@@ -11,9 +11,15 @@ generator client {
11
11
  {{#if (eq runtime "workers")}}
12
12
  runtime = "workerd"
13
13
  {{/if}}
14
+ {{#if (eq dbSetup "planetscale")}}
15
+ previewFeatures = ["driverAdapters"]
16
+ {{/if}}
14
17
  }
15
18
 
16
19
  datasource db {
17
20
  provider = "mysql"
18
21
  url = env("DATABASE_URL")
22
+ {{#if (eq dbSetup "planetscale")}}
23
+ relationMode = "prisma"
24
+ {{/if}}
19
25
  }
@@ -0,0 +1,12 @@
1
+ import { PrismaClient } from "../../prisma/generated/client";
2
+ {{#if (eq dbSetup "planetscale")}}
3
+ import { PrismaPlanetScale } from '@prisma/adapter-planetscale'
4
+ import { fetch as undiciFetch } from 'undici'
5
+
6
+ const adapter = new PrismaPlanetScale({ url: process.env.DATABASE_URL, fetch: undiciFetch })
7
+ const prisma = new PrismaClient({adapter});
8
+ {{else}}
9
+ const prisma = new PrismaClient();
10
+ {{/if}}
11
+
12
+ export default prisma;
@@ -19,4 +19,7 @@ datasource db {
19
19
  {{#if (eq dbSetup "supabase")}}
20
20
  directUrl = env("DIRECT_URL")
21
21
  {{/if}}
22
+ {{#if (eq dbSetup "planetscale")}}
23
+ relationMode = "prisma"
24
+ {{/if}}
22
25
  }
@@ -0,0 +1,5 @@
1
+ import { PrismaClient } from "../../prisma/generated/client";
2
+
3
+ const prisma = new PrismaClient();
4
+
5
+ export default prisma;
@@ -10,6 +10,9 @@ generator client {
10
10
  {{/if}}
11
11
  {{#if (eq runtime "workers")}}
12
12
  runtime = "workerd"
13
+ {{#if (eq dbSetup "d1")}}
14
+ previewFeatures = ["driverAdapters"]
15
+ {{/if}}
13
16
  {{/if}}
14
17
  }
15
18
 
@@ -1,10 +1,27 @@
1
1
  import "dotenv/config";
2
2
  import path from "node:path";
3
3
  import type { PrismaConfig } from "prisma";
4
+ {{#if (eq serverDeploy "wrangler")}}
5
+ import { PrismaD1 } from "@prisma/adapter-d1";
6
+ {{/if}}
4
7
 
5
8
  export default {
9
+ {{#if (eq serverDeploy "wrangler")}}
10
+ experimental: {
11
+ adapter: true
12
+ },
13
+ {{/if}}
6
14
  schema: path.join("prisma", "schema"),
7
15
  migrations: {
8
16
  path: path.join("prisma", "migrations"),
9
- }
17
+ },
18
+ {{#if (eq serverDeploy "wrangler")}}
19
+ async adapter() {
20
+ return new PrismaD1({
21
+ CLOUDFLARE_D1_TOKEN: process.env.CLOUDFLARE_D1_TOKEN!,
22
+ CLOUDFLARE_ACCOUNT_ID: process.env.CLOUDFLARE_ACCOUNT_ID!,
23
+ CLOUDFLARE_DATABASE_ID: process.env.CLOUDFLARE_DATABASE_ID!,
24
+ });
25
+ },
26
+ {{/if}}
10
27
  } satisfies PrismaConfig;
@@ -0,0 +1,16 @@
1
+ {{#if (and (eq dbSetup "d1") (eq runtime "workers"))}}
2
+ import { env } from "cloudflare:workers";
3
+ import { PrismaD1 } from "@prisma/adapter-d1";
4
+ import { PrismaClient } from "../../prisma/generated/client";
5
+
6
+ const adapter = new PrismaD1(env.DB);
7
+ const prisma = new PrismaClient({ adapter });
8
+
9
+ export default prisma;
10
+ {{else}}
11
+ import { PrismaClient } from "../../prisma/generated/client";
12
+
13
+ const prisma = new PrismaClient();
14
+
15
+ export default prisma;
16
+ {{/if}}
@@ -44,7 +44,11 @@ await Exec("db-generate", {
44
44
  });
45
45
 
46
46
  const db = await D1Database("database", {
47
+ {{#if (eq orm "prisma")}}
48
+ migrationsDir: "apps/server/prisma/migrations",
49
+ {{else if (eq orm "drizzle")}}
47
50
  migrationsDir: "apps/server/src/db/migrations",
51
+ {{/if}}
48
52
  });
49
53
  {{/if}}
50
54
 
@@ -27,7 +27,12 @@
27
27
  "database_name": "YOUR_DB_NAME",
28
28
  "database_id": "YOUR_DB_ID",
29
29
  "preview_database_id": "local-test-db",
30
+ {{#if (eq orm "drizzle")}}
30
31
  "migrations_dir": "./src/db/migrations"
32
+ {{/if}}
33
+ {{#if (eq orm "prisma")}}
34
+ "migrations_dir": "./prisma/migrations"
35
+ {{/if}}
31
36
  }
32
37
  ]
33
38
  {{/if}}
@@ -1,6 +1,6 @@
1
1
  {{#if (eq api "orpc")}}
2
2
  import z from "zod";
3
- import prisma from "../../prisma";
3
+ import prisma from "@/db";
4
4
  import { publicProcedure } from "../lib/orpc";
5
5
 
6
6
  export const todoRouter = {
@@ -52,7 +52,7 @@ export const todoRouter = {
52
52
  {{#if (eq api "trpc")}}
53
53
  import { TRPCError } from "@trpc/server";
54
54
  import z from "zod";
55
- import prisma from "../../prisma";
55
+ import prisma from "@/db";
56
56
  import { publicProcedure, router } from "../lib/trpc";
57
57
 
58
58
  export const todoRouter = router({
@@ -1,5 +0,0 @@
1
- import { PrismaClient } from "./generated/client";
2
-
3
- const prisma = new PrismaClient();
4
-
5
- export default prisma;
@@ -1,5 +0,0 @@
1
- import { PrismaClient } from "./generated/client";
2
-
3
- const prisma = new PrismaClient();
4
-
5
- export default prisma;
@@ -1,5 +0,0 @@
1
- import { PrismaClient } from "./generated/client";
2
-
3
- const prisma = new PrismaClient();
4
-
5
- export default prisma;
@@ -1,5 +0,0 @@
1
- import { PrismaClient } from "./generated/client";
2
-
3
- const prisma = new PrismaClient();
4
-
5
- export default prisma;