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.
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/{src-D59j3XG5.js → src-D32Mc1TH.js} +196 -53
- package/package.json +1 -1
- package/templates/api/orpc/server/next/src/app/rpc/[...all]/route.ts.hbs +32 -3
- package/templates/auth/better-auth/server/base/src/lib/auth.ts.hbs +1 -1
- package/templates/backend/server/elysia/src/index.ts.hbs +31 -2
- package/templates/backend/server/express/src/index.ts.hbs +38 -4
- package/templates/backend/server/fastify/src/index.ts.hbs +33 -2
- package/templates/backend/server/hono/src/index.ts.hbs +40 -5
- package/templates/db/drizzle/mysql/src/db/index.ts.hbs +25 -0
- package/templates/db/prisma/mongodb/src/db/index.ts.hbs +5 -0
- package/templates/db/prisma/mysql/prisma/schema/schema.prisma.hbs +6 -0
- package/templates/db/prisma/mysql/src/db/index.ts.hbs +12 -0
- package/templates/db/prisma/postgres/prisma/schema/schema.prisma.hbs +3 -0
- package/templates/db/prisma/postgres/src/db/index.ts.hbs +5 -0
- package/templates/db/prisma/sqlite/prisma/schema/schema.prisma.hbs +3 -0
- package/templates/db/prisma/sqlite/prisma.config.ts.hbs +18 -1
- package/templates/db/prisma/sqlite/src/db/index.ts.hbs +16 -0
- package/templates/deploy/alchemy/alchemy.run.ts.hbs +4 -0
- package/templates/deploy/wrangler/server/wrangler.jsonc.hbs +5 -0
- package/templates/examples/todo/server/prisma/base/src/routers/todo.ts.hbs +2 -2
- package/templates/db/prisma/mongodb/prisma/index.ts.hbs +0 -5
- package/templates/db/prisma/mysql/prisma/index.ts +0 -5
- package/templates/db/prisma/postgres/prisma/index.ts +0 -5
- package/templates/db/prisma/sqlite/prisma/index.ts +0 -5
package/dist/cli.js
CHANGED
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
|
@@ -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.
|
|
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,
|
|
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
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
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 (
|
|
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
|
|
3048
|
-
await execa(runCmd,
|
|
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: [
|
|
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)
|
|
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")
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
6385
|
-
|
|
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
|
+
"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
|
|
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
|
|
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
|
-
|
|
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 "
|
|
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
|
|
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
|
|
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
|
|
54
|
-
|
|
55
|
-
|
|
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
|
|
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
|
|
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
|
|
58
|
-
|
|
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
|
-
|
|
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}}
|
|
@@ -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;
|
|
@@ -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 "
|
|
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 "
|
|
55
|
+
import prisma from "@/db";
|
|
56
56
|
import { publicProcedure, router } from "../lib/trpc";
|
|
57
57
|
|
|
58
58
|
export const todoRouter = router({
|