Skip to content

Commit 52735a1

Browse files
authored
fix: add database URL parsing to PrismaService (hoppscotch#5656)
* fix: add database URL parsing to PrismaService * fix: feedback * chore: add pool connectivity check to PrismaService
1 parent 77d0956 commit 52735a1

File tree

2 files changed

+71
-4
lines changed

2 files changed

+71
-4
lines changed

packages/hoppscotch-backend/src/prisma/prisma.service.ts

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,35 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
22
import { PrismaClient, Prisma } from 'src/generated/prisma/client';
33
import { PrismaPg } from '@prisma/adapter-pg';
44
import pg from 'pg';
5+
import { parseIntSafe } from 'src/utils';
56

67
@Injectable()
78
export class PrismaService
89
extends PrismaClient
910
implements OnModuleInit, OnModuleDestroy
1011
{
11-
private pool: pg.Pool;
12+
private readonly pool: pg.Pool;
1213

1314
constructor() {
15+
const databaseUrl = process.env.DATABASE_URL;
16+
17+
if (!databaseUrl) {
18+
throw new Error('DATABASE_URL environment variable is not set');
19+
}
20+
21+
const { connectionString, schema, connectionLimit, connectTimeout } =
22+
PrismaService.parseDatabaseUrl(databaseUrl);
23+
1424
const pool = new pg.Pool({
15-
connectionString: process.env.DATABASE_URL,
25+
connectionString,
26+
max: connectionLimit ?? 20,
27+
idleTimeoutMillis: 30000,
28+
connectionTimeoutMillis: connectTimeout ?? 5000,
29+
});
30+
31+
const adapter = new PrismaPg(pool, {
32+
schema,
1633
});
17-
const adapter = new PrismaPg(pool);
1834

1935
super({
2036
adapter,
@@ -26,8 +42,46 @@ export class PrismaService
2642

2743
this.pool = pool;
2844
}
45+
46+
private static parseDatabaseUrl(databaseUrl: string): {
47+
connectionString: string;
48+
schema: string;
49+
connectionLimit?: number;
50+
connectTimeout?: number;
51+
} {
52+
try {
53+
const url = new URL(databaseUrl);
54+
const schema = url.searchParams.get('schema') || 'public';
55+
const connectionLimit = url.searchParams.get('connection_limit');
56+
const connectTimeout = url.searchParams.get('connect_timeout');
57+
58+
url.searchParams.delete('schema');
59+
url.searchParams.delete('connection_limit');
60+
url.searchParams.delete('connect_timeout');
61+
62+
return {
63+
connectionString: url.toString(),
64+
schema,
65+
connectionLimit: parseIntSafe(connectionLimit),
66+
connectTimeout: parseIntSafe(connectTimeout),
67+
};
68+
} catch (error) {
69+
throw new Error(
70+
`Invalid DATABASE_URL format: ${error instanceof Error ? error.message : 'Unknown error'}`,
71+
);
72+
}
73+
}
74+
2975
async onModuleInit() {
30-
await this.$connect();
76+
try {
77+
// Verify pool connectivity
78+
const client = await this.pool.connect();
79+
client.release();
80+
81+
await this.$connect();
82+
} catch (error) {
83+
throw new Error(`Database connection failed: ${error.message}`);
84+
}
3185
}
3286

3387
async onModuleDestroy() {

packages/hoppscotch-backend/src/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,19 @@ export function delay(ms: number): Promise<void> {
2222
return new Promise((resolve) => setTimeout(resolve, ms));
2323
}
2424

25+
/**
26+
* Safely parses a string to an integer, returning undefined if the value is null, undefined, or results in NaN.
27+
* @param value The string value to parse
28+
* @returns The parsed integer or undefined
29+
*/
30+
export function parseIntSafe(
31+
value: string | null | undefined,
32+
): number | undefined {
33+
if (!value) return undefined;
34+
const parsed = parseInt(value, 10);
35+
return !isNaN(parsed) ? parsed : undefined;
36+
}
37+
2538
/**
2639
* A workaround to throw an exception in an expression.
2740
* JS throw keyword creates a statement not an expression.

0 commit comments

Comments
 (0)