Skip to content

Commit 86384a9

Browse files
authored
Merge pull request #40 from barisgit/development
DB connection improvements
2 parents 96d13dc + 65b679c commit 86384a9

4 files changed

Lines changed: 122 additions & 101 deletions

File tree

apps/web/src/app/(auth)/login/login-page.tsx

Lines changed: 0 additions & 54 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { NextResponse } from "next/server";
22

3+
export const dynamic = "force-static";
4+
35
export async function GET() {
46
return NextResponse.json({ message: "Not Found" }, { status: 404 });
57
}

apps/web/src/app/page.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,8 @@ import { WikiFolderTree } from "~/components/wiki/WikiFolderTree";
1010
import { getServerSession } from "next-auth/next";
1111
import { authOptions } from "~/lib/auth";
1212

13-
export const revalidate = 300; // Revalidate every 5 minutes
14-
export const fetchCache = "force-cache";
15-
export const dynamic = "auto";
13+
export const revalidate = 900; // Revalidate every 15 minutes
14+
export const dynamic = "force-static";
1615

1716
export default async function Home() {
1817
const recentPages = await dbService.wiki.getRecentPages(5);

packages/db/src/index.ts

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,59 @@ import {
1111
import { createPool as createVercelPool } from "@vercel/postgres";
1212
import pg from "pg";
1313
import * as schema from "./schema/index.js";
14+
import { logger } from "@repo/logger";
1415

1516
// Load environment variables from .env file if present
1617
dotenv.config({ path: [".env.local", ".env"] });
1718

19+
// --- Configuration ---
20+
1821
// Define environment variables we check
1922
const databaseUrl = process.env.DATABASE_URL;
2023
const vercelPostgresUrl = process.env.POSTGRES_URL; // Vercel's own managed DB
2124
const runningOnVercel = !!process.env.VERCEL;
2225

23-
// Validate *at least* DATABASE_URL is set (needed as fallback or for non-Vercel/non-Neon)
26+
/**
27+
* Enum representing the different database connection types.
28+
*/
29+
export enum ConnectionType {
30+
VERCEL_POSTGRES = "VERCEL_POSTGRES", // Vercel's managed Postgres or external pooler via POSTGRES_URL
31+
NEON = "NEON", // Neon DB via DATABASE_URL
32+
VERCEL_EXTERNAL_POOL = "VERCEL_EXTERNAL_POOL", // External DB on Vercel via DATABASE_URL (e.g., PgBouncer)
33+
STANDARD_POOL = "STANDARD_POOL", // Standard node-postgres pool (local/non-Vercel)
34+
INVALID = "INVALID", // Configuration is invalid
35+
}
36+
37+
/**
38+
* Determines the database connection type based on environment variables.
39+
* @param dbUrl - The DATABASE_URL environment variable.
40+
* @param vercelUrl - The POSTGRES_URL environment variable.
41+
* @param isOnVercel - Boolean indicating if running on Vercel.
42+
* @returns The determined ConnectionType.
43+
*/
44+
const getConnectionType = (
45+
dbUrl: string | undefined,
46+
vercelUrl: string | undefined,
47+
isOnVercel: boolean
48+
): ConnectionType => {
49+
if (vercelUrl) {
50+
return ConnectionType.VERCEL_POSTGRES;
51+
}
52+
if (dbUrl && dbUrl.includes(".neon.tech")) {
53+
return ConnectionType.NEON;
54+
}
55+
if (isOnVercel && dbUrl) {
56+
return ConnectionType.VERCEL_EXTERNAL_POOL;
57+
}
58+
if (dbUrl) {
59+
return ConnectionType.STANDARD_POOL;
60+
}
61+
return ConnectionType.INVALID;
62+
};
63+
64+
// --- Database Initialization ---
65+
66+
// Validate *at least* one URL is set
2467
if (!databaseUrl && !vercelPostgresUrl) {
2568
console.error("At least one of DATABASE_URL or POSTGRES_URL must be set!");
2669
throw new Error(
@@ -30,55 +73,86 @@ if (!databaseUrl && !vercelPostgresUrl) {
3073

3174
// Define a union type for all possible database types
3275
export type DatabaseType =
33-
| VercelPgDatabase<typeof schema> // Used for Vercel managed DB OR Vercel deployment with external pooler
76+
| VercelPgDatabase<typeof schema>
3477
| NeonHttpDatabase<typeof schema>
35-
| NodePgDatabase<typeof schema>; // Used for local/standard non-Vercel hosting
78+
| NodePgDatabase<typeof schema>;
3679

3780
let db: DatabaseType;
81+
const connectionType = getConnectionType(
82+
databaseUrl,
83+
vercelPostgresUrl,
84+
runningOnVercel
85+
);
3886

39-
// Determine which driver to use based on priority
40-
if (vercelPostgresUrl) {
41-
// --- Priority 1: Using Vercel's integrated Postgres service ---
42-
console.log("Using Vercel Postgres driver (POSTGRES_URL detected)");
43-
const vercelPool = createVercelPool({ connectionString: vercelPostgresUrl });
44-
db = drizzleVercel(vercelPool, { schema });
45-
} else if (databaseUrl && databaseUrl.includes(".neon.tech")) {
46-
// --- Priority 2: Using Neon DB (checked via DATABASE_URL) ---
47-
console.log("Using Neon database driver (DATABASE_URL contains .neon.tech)");
48-
neonConfig.fetchConnectionCache = true;
49-
const sql = neon(databaseUrl);
50-
db = drizzleNeon(sql, { schema });
51-
} else if (runningOnVercel) {
52-
// --- Priority 3: On Vercel, but NOT using Vercel's integrated DB or Neon. ---
53-
// USE STANDARD pg.Pool. This simplifies setup but RISKS connection limits on Vercel.
54-
// User MUST ensure their DB can handle potential connections from many function instances by eg. providing a PgBouncer or other pooler.
55-
console.warn(
56-
"WARNING: Running on Vercel without Vercel Postgres or Neon DB. Using standard pg.Pool (DATABASE_URL)."
57-
);
58-
console.warn(
59-
"Ensure your database connection limit is high enough for potential Vercel scaling or that you have a PgBouncer or other pooler!"
60-
);
61-
const poolSize = 3; // Keep pool size very small for Vercel fallback
62-
const pool = new pg.Pool({
63-
connectionString: databaseUrl,
64-
max: poolSize,
65-
});
66-
db = drizzlePg(pool, { schema });
67-
} else {
68-
// --- Priority 4: Standard/Local setup (Not on Vercel, Not Neon) ---
69-
// Use the standard node-postgres pool.
70-
console.log(
71-
"Using standard PostgreSQL driver (pg.Pool) with DATABASE_URL (Not on Vercel/Neon)"
72-
);
73-
const poolSize = 10;
74-
const pool = new pg.Pool({
75-
connectionString: databaseUrl, // Use the main DATABASE_URL
76-
max: poolSize,
77-
});
78-
db = drizzlePg(pool, { schema });
87+
// const poolSize = process.env.DATABASE_POOL_SIZE
88+
// ? parseInt(process.env.DATABASE_POOL_SIZE, 10)
89+
// : 10;
90+
91+
const poolSize = 10;
92+
93+
switch (connectionType) {
94+
case ConnectionType.VERCEL_POSTGRES:
95+
logger.log("Using Vercel Postgres pool driver (POSTGRES_URL detected)");
96+
// Use createPool as recommended for Vercel's own Postgres
97+
// We know vercelPostgresUrl is defined here due to getConnectionType logic
98+
db = drizzleVercel(
99+
createVercelPool({
100+
connectionString: vercelPostgresUrl!,
101+
}),
102+
{ schema }
103+
);
104+
break;
105+
106+
case ConnectionType.NEON:
107+
logger.log("Using Neon database driver (DATABASE_URL contains .neon.tech)");
108+
// We know databaseUrl is defined here due to getConnectionType logic
109+
neonConfig.fetchConnectionCache = true;
110+
db = drizzleNeon(neon(databaseUrl!), { schema });
111+
break;
112+
113+
case ConnectionType.VERCEL_EXTERNAL_POOL:
114+
logger.warn(
115+
"Using standard pg.Pool with external DATABASE_URL on Vercel. Ensure the URL points to a pooler."
116+
);
117+
logger.warn(
118+
"[CRITICAL] Ensure the pooler mode is set to 'transaction' or that you have a very beefy database server otherwise IT WILL use all your database connections."
119+
);
120+
// We know databaseUrl is defined here due to getConnectionType logic
121+
db = drizzlePg(
122+
new pg.Pool({
123+
connectionString: databaseUrl!,
124+
max: 1, // Recommended for Vercel serverless functions connecting to external DBs
125+
}),
126+
{ schema }
127+
);
128+
break;
129+
130+
case ConnectionType.STANDARD_POOL:
131+
logger.log(
132+
"Using standard PostgreSQL driver (pg.Pool) with DATABASE_URL (Not on Vercel/Neon)"
133+
);
134+
// We know databaseUrl is defined here due to getConnectionType logic
135+
logger.log(`Using pg.Pool with pool size: ${poolSize}`);
136+
db = drizzlePg(
137+
new pg.Pool({
138+
connectionString: databaseUrl!,
139+
max: poolSize,
140+
}),
141+
{ schema }
142+
);
143+
break;
144+
145+
case ConnectionType.INVALID:
146+
default:
147+
// This case should theoretically not be reached due to the initial validation
148+
// and the logic in getConnectionType, but it's good practice to handle it.
149+
logger.error("Could not determine database connection method.");
150+
throw new Error("Invalid database configuration state.");
79151
}
80152

81-
// Export the configured db
153+
// --- Exports ---
154+
155+
// Export the configured db as a named export
82156
export { db };
83157

84158
// Re-export the seed function

0 commit comments

Comments
 (0)