From 92fe574ba1dafb60b4097ed942eb822f5971d5a5 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 28 Oct 2026 03:03:42 +0530 Subject: [PATCH 1/3] fix: make every starter query its database on first run The seed called db.connect() on top of a client that already connects on its first query. Once any other route had queried, that connect() threw DRIVER.ALREADY_CONNECTED and /users stayed broken until a restart. The Postgres and Mongo runtimes and Composer's client all connect lazily, so the starters drop connectDatabase() instead of guarding it. /users also hid every failure behind "Run contract:emit and apply your schema", sending people the wrong way. API starters now return the real error and pages point at the server log first. Running every template against real databases found two more breaks: - SvelteKit returned 500 on both providers ("exports is not defined"): ssr.noExternal forced the CommonJS pg and mongodb drivers through Vite's ESM dev runner. The build stays external and Composer traces runtime dependencies with dependencies: "external", as it does for Astro. - Next.js with Mongo returned 500: Turbopack's interop breaks mongodb-connection-string-url, so @prisma/orm-mongo is now a server external package. Signed-off-by: Aman Varshney --- templates/create/_shared/service.ts.hbs | 2 +- templates/create/_shared/src/prisma/db.ts.hbs | 10 ---------- templates/create/_shared/src/prisma/seed.ts.hbs | 4 +--- templates/create/astro/src/pages/index.astro.hbs | 4 ++-- templates/create/elysia/src/index.ts.hbs | 8 ++++---- templates/create/hono/src/index.ts.hbs | 8 ++++---- templates/create/next/next.config.ts | 7 ------- templates/create/next/next.config.ts.hbs | 11 +++++++++++ templates/create/next/src/app/page.tsx.hbs | 4 ++-- templates/create/nuxt/app/pages/index.vue.hbs | 3 ++- templates/create/nuxt/server/api/users.get.ts.hbs | 8 ++++---- templates/create/svelte/src/routes/+page.svelte.hbs | 4 ++-- templates/create/svelte/vite.config.ts | 3 --- .../create/tanstack-start/src/routes/index.tsx.hbs | 4 ++-- tests/install.test.ts | 9 +++++---- 15 files changed, 40 insertions(+), 49 deletions(-) delete mode 100644 templates/create/next/next.config.ts create mode 100644 templates/create/next/next.config.ts.hbs diff --git a/templates/create/_shared/service.ts.hbs b/templates/create/_shared/service.ts.hbs index 1a7d6bea..b0d8cc03 100644 --- a/templates/create/_shared/service.ts.hbs +++ b/templates/create/_shared/service.ts.hbs @@ -32,7 +32,7 @@ export default compute({ {{else if (eq template "turborepo")}} build: node({ module: import.meta.url, entry: "./apps/server/dist/server.mjs" }), {{else if (eq template "svelte")}} - build: node({ module: import.meta.url, dir: "./build", entry: "index.js" }), + build: node({ module: import.meta.url, dir: "./build", entry: "index.js", dependencies: "external" }), {{else if (eq template "astro")}} build: node({ module: import.meta.url, dir: "./dist", entry: "server/entry.mjs" }), {{else if (eq template "nuxt")}} diff --git a/templates/create/_shared/src/prisma/db.ts.hbs b/templates/create/_shared/src/prisma/db.ts.hbs index 04adc66c..11865ef3 100644 --- a/templates/create/_shared/src/prisma/db.ts.hbs +++ b/templates/create/_shared/src/prisma/db.ts.hbs @@ -59,13 +59,3 @@ const databaseUrl = getDatabaseUrl(); export const db = databaseUrl ? mongo({ contract, url: databaseUrl }) : mongo({ contract }); {{/if}} - -let connection: Promise | undefined; - -export function connectDatabase(): Promise { - connection ??= db.connect().then(() => undefined).catch((error: unknown) => { - connection = undefined; - throw error; - }); - return connection; -} diff --git a/templates/create/_shared/src/prisma/seed.ts.hbs b/templates/create/_shared/src/prisma/seed.ts.hbs index 8ad1eb98..9f87f2e6 100644 --- a/templates/create/_shared/src/prisma/seed.ts.hbs +++ b/templates/create/_shared/src/prisma/seed.ts.hbs @@ -1,4 +1,4 @@ -import { connectDatabase, db } from "./db.ts"; +import { db } from "./db.ts"; const users = [ { email: "alice@prisma.io", username: "alice", name: "Alice" }, @@ -9,8 +9,6 @@ const users = [ let pendingSeed: Promise | undefined; async function runSeed(): Promise { - await connectDatabase(); - {{#if (eq provider "mongo")}} for (const user of users) { const existingUser = await db.orm.users.where({ email: user.email }).first(); diff --git a/templates/create/astro/src/pages/index.astro.hbs b/templates/create/astro/src/pages/index.astro.hbs index 3183f436..b8688a35 100644 --- a/templates/create/astro/src/pages/index.astro.hbs +++ b/templates/create/astro/src/pages/index.astro.hbs @@ -40,8 +40,8 @@ const users = await listUsers(10).catch(() => undefined); {!users ? (

- Could not query users yet. Run contract:emit and apply your schema, - then refresh. + Could not query users. Check the server log for the error. If the schema isn't + applied yet, run contract:emit and apply it, then refresh.

) : users.length === 0 ? (

No users found.

diff --git a/templates/create/elysia/src/index.ts.hbs b/templates/create/elysia/src/index.ts.hbs index aec919cc..d7e5245d 100644 --- a/templates/create/elysia/src/index.ts.hbs +++ b/templates/create/elysia/src/index.ts.hbs @@ -16,14 +16,14 @@ const app = new Elysia({ adapter: "Bun" in globalThis ? undefined : node() }) }) .get("/users", async ({ set }) => { - const users = await listUsers(10).catch((error) => { + const users = await listUsers(10).catch((error: unknown) => { console.error("Failed to query users:", error); - return undefined; + return error instanceof Error ? error : new Error(String(error)); }); - if (!users) { + if (users instanceof Error) { set.status = 500; - return { error: "Could not query users yet. Run contract:emit and apply your schema first." }; + return { error: `Could not query users: ${users.message}` }; } return users; diff --git a/templates/create/hono/src/index.ts.hbs b/templates/create/hono/src/index.ts.hbs index 0918fbe7..f155b7ea 100644 --- a/templates/create/hono/src/index.ts.hbs +++ b/templates/create/hono/src/index.ts.hbs @@ -12,13 +12,13 @@ app.get("/", (c) => { }); app.get("/users", async (c) => { - const users = await listUsers(10).catch((error) => { + const users = await listUsers(10).catch((error: unknown) => { console.error("Failed to query users:", error); - return undefined; + return error instanceof Error ? error : new Error(String(error)); }); - if (!users) { - return c.json({ error: "Could not query users yet. Run contract:emit and apply your schema first." }, 500); + if (users instanceof Error) { + return c.json({ error: `Could not query users: ${users.message}` }, 500); } return c.json(users); diff --git a/templates/create/next/next.config.ts b/templates/create/next/next.config.ts deleted file mode 100644 index 68a6c64d..00000000 --- a/templates/create/next/next.config.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { NextConfig } from "next"; - -const nextConfig: NextConfig = { - output: "standalone", -}; - -export default nextConfig; diff --git a/templates/create/next/next.config.ts.hbs b/templates/create/next/next.config.ts.hbs new file mode 100644 index 00000000..641b6a3c --- /dev/null +++ b/templates/create/next/next.config.ts.hbs @@ -0,0 +1,11 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + output: "standalone", +{{#if (eq provider "mongo")}} + // Turbopack's ESM interop breaks mongodb-connection-string-url, which the Mongo runtime imports. + serverExternalPackages: ["@prisma/orm-mongo"], +{{/if}} +}; + +export default nextConfig; diff --git a/templates/create/next/src/app/page.tsx.hbs b/templates/create/next/src/app/page.tsx.hbs index eaf18288..0a588fbc 100644 --- a/templates/create/next/src/app/page.tsx.hbs +++ b/templates/create/next/src/app/page.tsx.hbs @@ -29,8 +29,8 @@ export default async function Home() { {!users ? (

- Could not query users yet. Run contract:emit and apply your schema, - then refresh. + Could not query users. Check the server log for the error. If the schema isn't + applied yet, run contract:emit and apply it, then refresh.

) : users.length === 0 ? (

No users found.

diff --git a/templates/create/nuxt/app/pages/index.vue.hbs b/templates/create/nuxt/app/pages/index.vue.hbs index eee9aa12..40ea602e 100644 --- a/templates/create/nuxt/app/pages/index.vue.hbs +++ b/templates/create/nuxt/app/pages/index.vue.hbs @@ -49,7 +49,8 @@ function formatCreatedAt(value: string): string {

- \{{ data.error }} Run contract:emit and apply your schema, then refresh. + \{{ data.error }} If the schema isn't applied yet, run contract:emit and apply + it, then refresh.

No users found. diff --git a/templates/create/nuxt/server/api/users.get.ts.hbs b/templates/create/nuxt/server/api/users.get.ts.hbs index 9d69e5dc..35ab0565 100644 --- a/templates/create/nuxt/server/api/users.get.ts.hbs +++ b/templates/create/nuxt/server/api/users.get.ts.hbs @@ -8,14 +8,14 @@ export default defineEventHandler(async () => { createdAt: user.createdAt ?? null, })) ) - .catch((error) => { + .catch((error: unknown) => { console.error("Failed to query users:", error); - return undefined; + return error instanceof Error ? error : new Error(String(error)); }); - if (!users) { + if (users instanceof Error) { return { - error: "Could not query users yet.", + error: `Could not query users: ${users.message}`, users: [], }; } diff --git a/templates/create/svelte/src/routes/+page.svelte.hbs b/templates/create/svelte/src/routes/+page.svelte.hbs index a15171b9..981ddc1c 100644 --- a/templates/create/svelte/src/routes/+page.svelte.hbs +++ b/templates/create/svelte/src/routes/+page.svelte.hbs @@ -34,8 +34,8 @@ {#if !data.users}

- Could not query users yet. Run contract:emit and apply your schema, then - refresh. + Could not query users. Check the server log for the error. If the schema isn't applied + yet, run contract:emit and apply it, then refresh.

{:else if data.users.length === 0}

No users found.

diff --git a/templates/create/svelte/vite.config.ts b/templates/create/svelte/vite.config.ts index 0ccdfa10..c0a84671 100644 --- a/templates/create/svelte/vite.config.ts +++ b/templates/create/svelte/vite.config.ts @@ -4,7 +4,4 @@ import { defineConfig } from "vite"; export default defineConfig({ plugins: [sveltekit({ adapter: adapter() })], - ssr: { - noExternal: true, - }, }); diff --git a/templates/create/tanstack-start/src/routes/index.tsx.hbs b/templates/create/tanstack-start/src/routes/index.tsx.hbs index 2336c281..b1bedca3 100644 --- a/templates/create/tanstack-start/src/routes/index.tsx.hbs +++ b/templates/create/tanstack-start/src/routes/index.tsx.hbs @@ -50,8 +50,8 @@ function Home() { {!users ? (

- Could not query users yet. Run contract:emit and apply your schema, - then refresh. + Could not query users. Check the server log for the error. If the schema isn't + applied yet, run contract:emit and apply it, then refresh.

) : users.length === 0 ? (

No users found.

diff --git a/tests/install.test.ts b/tests/install.test.ts index c7600100..54cb61f5 100644 --- a/tests/install.test.ts +++ b/tests/install.test.ts @@ -415,7 +415,7 @@ describe("generated templates", () => { expect(packageJson.scripts?.deploy).toBeUndefined(); expect(dbSource).toContain('Deno.env.get("DATABASE_URL")'); expect(dbSource).not.toContain('import service from "../../service.ts"'); - expect(seedSource).toContain("await connectDatabase()"); + expect(seedSource).not.toContain("connect()"); expect(usersSource).toContain("await seed()"); continue; } @@ -443,8 +443,8 @@ describe("generated templates", () => { expect(prismaConfig.includes("nextjsBuild()")).toBe(template === "next"); expect(tsconfig).toContain('"node"'); expect(serviceSource).toContain("compute({"); - expect(dbSource).toContain("export function connectDatabase()"); - expect(seedSource).toContain("await connectDatabase()"); + expect(dbSource).not.toContain(".connect("); + expect(seedSource).not.toContain("connect"); expect(seedSource).toContain("export function seed()"); expect(usersSource).toContain("await seed()"); expect( @@ -547,7 +547,8 @@ describe("generated templates", () => { } if (template === "svelte") { const viteConfig = await readFile(path.join(projectDir, "vite.config.ts"), "utf8"); - expect(viteConfig).toContain("noExternal: true"); + expect(viteConfig).not.toContain("noExternal"); + expect(serviceSource).toContain('dependencies: "external"'); } if (template === "nuxt") { expect(packageJson.scripts?.postinstall).toBe( From 196873f9469392e088d6e5c714945a08ff56b5d6 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 28 Oct 2026 03:14:19 +0530 Subject: [PATCH 2/3] fix: ship Astro's runtime dependencies to Compute Astro's Node adapter leaves packages in node_modules, but the deploy copied only dist/, so the app crash-looped on Compute with "Cannot find package 'devalue'" and served 502. Composer now traces the entry's dependencies with dependencies: "external", as for SvelteKit. Signed-off-by: Aman Varshney --- templates/create/_shared/service.ts.hbs | 2 +- tests/install.test.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/templates/create/_shared/service.ts.hbs b/templates/create/_shared/service.ts.hbs index b0d8cc03..60b0b6ed 100644 --- a/templates/create/_shared/service.ts.hbs +++ b/templates/create/_shared/service.ts.hbs @@ -34,7 +34,7 @@ export default compute({ {{else if (eq template "svelte")}} build: node({ module: import.meta.url, dir: "./build", entry: "index.js", dependencies: "external" }), {{else if (eq template "astro")}} - build: node({ module: import.meta.url, dir: "./dist", entry: "server/entry.mjs" }), + build: node({ module: import.meta.url, dir: "./dist", entry: "server/entry.mjs", dependencies: "external" }), {{else if (eq template "nuxt")}} build: node({ module: import.meta.url, dir: "./.output", entry: "server/index.mjs" }), {{else if (eq template "tanstack-start")}} diff --git a/tests/install.test.ts b/tests/install.test.ts index 54cb61f5..ed6258f0 100644 --- a/tests/install.test.ts +++ b/tests/install.test.ts @@ -550,6 +550,9 @@ describe("generated templates", () => { expect(viteConfig).not.toContain("noExternal"); expect(serviceSource).toContain('dependencies: "external"'); } + if (template === "astro") { + expect(serviceSource).toContain('dependencies: "external"'); + } if (template === "nuxt") { expect(packageJson.scripts?.postinstall).toBe( `nuxt prepare && ${getRunScriptCommand(packageManager, "skills:sync")}`, From 90f2f4354a0c20744640bbc44932cfe0a3cc2db2 Mon Sep 17 00:00:00 2001 From: Aman Varshney Date: Wed, 28 Oct 2026 03:25:03 +0530 Subject: [PATCH 3/3] fix: log starter query failures and keep database errors out of responses Pages pointed people at the server log but caught the error without logging it, and the API starters returned the raw database message, which can carry host, port and user names. Every starter now logs the failure, and the API starters return a fixed message. Signed-off-by: Aman Varshney --- templates/create/astro/src/pages/index.astro.hbs | 5 ++++- templates/create/elysia/src/index.ts.hbs | 6 +++--- templates/create/hono/src/index.ts.hbs | 6 +++--- templates/create/next/src/app/page.tsx.hbs | 5 ++++- templates/create/nuxt/server/api/users.get.ts.hbs | 6 +++--- templates/create/svelte/src/routes/+page.server.ts.hbs | 5 ++++- templates/create/tanstack-start/src/routes/index.tsx.hbs | 5 ++++- 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/templates/create/astro/src/pages/index.astro.hbs b/templates/create/astro/src/pages/index.astro.hbs index b8688a35..57280189 100644 --- a/templates/create/astro/src/pages/index.astro.hbs +++ b/templates/create/astro/src/pages/index.astro.hbs @@ -7,7 +7,10 @@ const formatter = new Intl.DateTimeFormat("en", { timeStyle: "short", }); -const users = await listUsers(10).catch(() => undefined); +const users = await listUsers(10).catch((error: unknown) => { + console.error("Failed to query users:", error); + return undefined; +}); --- diff --git a/templates/create/elysia/src/index.ts.hbs b/templates/create/elysia/src/index.ts.hbs index d7e5245d..259d78cf 100644 --- a/templates/create/elysia/src/index.ts.hbs +++ b/templates/create/elysia/src/index.ts.hbs @@ -18,12 +18,12 @@ const app = new Elysia({ adapter: "Bun" in globalThis ? undefined : node() }) .get("/users", async ({ set }) => { const users = await listUsers(10).catch((error: unknown) => { console.error("Failed to query users:", error); - return error instanceof Error ? error : new Error(String(error)); + return undefined; }); - if (users instanceof Error) { + if (!users) { set.status = 500; - return { error: `Could not query users: ${users.message}` }; + return { error: "Could not query users. Check the server log for the error." }; } return users; diff --git a/templates/create/hono/src/index.ts.hbs b/templates/create/hono/src/index.ts.hbs index f155b7ea..c8f1432f 100644 --- a/templates/create/hono/src/index.ts.hbs +++ b/templates/create/hono/src/index.ts.hbs @@ -14,11 +14,11 @@ app.get("/", (c) => { app.get("/users", async (c) => { const users = await listUsers(10).catch((error: unknown) => { console.error("Failed to query users:", error); - return error instanceof Error ? error : new Error(String(error)); + return undefined; }); - if (users instanceof Error) { - return c.json({ error: `Could not query users: ${users.message}` }, 500); + if (!users) { + return c.json({ error: "Could not query users. Check the server log for the error." }, 500); } return c.json(users); diff --git a/templates/create/next/src/app/page.tsx.hbs b/templates/create/next/src/app/page.tsx.hbs index 0a588fbc..06c60c07 100644 --- a/templates/create/next/src/app/page.tsx.hbs +++ b/templates/create/next/src/app/page.tsx.hbs @@ -7,7 +7,10 @@ export default async function Home() { dateStyle: "medium", timeStyle: "short", }); - const users = await listUsers(10).catch(() => undefined); + const users = await listUsers(10).catch((error: unknown) => { + console.error("Failed to query users:", error); + return undefined; + }); return (
diff --git a/templates/create/nuxt/server/api/users.get.ts.hbs b/templates/create/nuxt/server/api/users.get.ts.hbs index 35ab0565..462d7b3f 100644 --- a/templates/create/nuxt/server/api/users.get.ts.hbs +++ b/templates/create/nuxt/server/api/users.get.ts.hbs @@ -10,12 +10,12 @@ export default defineEventHandler(async () => { ) .catch((error: unknown) => { console.error("Failed to query users:", error); - return error instanceof Error ? error : new Error(String(error)); + return undefined; }); - if (users instanceof Error) { + if (!users) { return { - error: `Could not query users: ${users.message}`, + error: "Could not query users. Check the server log for the error.", users: [], }; } diff --git a/templates/create/svelte/src/routes/+page.server.ts.hbs b/templates/create/svelte/src/routes/+page.server.ts.hbs index e563deca..680962fd 100644 --- a/templates/create/svelte/src/routes/+page.server.ts.hbs +++ b/templates/create/svelte/src/routes/+page.server.ts.hbs @@ -10,7 +10,10 @@ export const load: PageServerLoad = async () => { createdAt: user.createdAt ?? null, })) ) - .catch(() => undefined); + .catch((error: unknown) => { + console.error("Failed to query users:", error); + return undefined; + }); return { users }; }; diff --git a/templates/create/tanstack-start/src/routes/index.tsx.hbs b/templates/create/tanstack-start/src/routes/index.tsx.hbs index b1bedca3..749bdf82 100644 --- a/templates/create/tanstack-start/src/routes/index.tsx.hbs +++ b/templates/create/tanstack-start/src/routes/index.tsx.hbs @@ -11,7 +11,10 @@ const listUsers = createServerFn({ method: "GET" }).handler(async () => { createdAt: user.createdAt ?? null, })) ) - .catch(() => undefined); + .catch((error: unknown) => { + console.error("Failed to query users:", error); + return undefined; + }); }); export const Route = createFileRoute("/")({