fix: add database URL parsing to PrismaService (#5656)
* fix: add database URL parsing to PrismaService * fix: feedback * chore: add pool connectivity check to PrismaService
This commit is contained in:
parent
77d0956180
commit
52735a166d
2 changed files with 71 additions and 4 deletions
|
|
@ -2,19 +2,35 @@ import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
|
|||
import { PrismaClient, Prisma } from 'src/generated/prisma/client';
|
||||
import { PrismaPg } from '@prisma/adapter-pg';
|
||||
import pg from 'pg';
|
||||
import { parseIntSafe } from 'src/utils';
|
||||
|
||||
@Injectable()
|
||||
export class PrismaService
|
||||
extends PrismaClient
|
||||
implements OnModuleInit, OnModuleDestroy
|
||||
{
|
||||
private pool: pg.Pool;
|
||||
private readonly pool: pg.Pool;
|
||||
|
||||
constructor() {
|
||||
const databaseUrl = process.env.DATABASE_URL;
|
||||
|
||||
if (!databaseUrl) {
|
||||
throw new Error('DATABASE_URL environment variable is not set');
|
||||
}
|
||||
|
||||
const { connectionString, schema, connectionLimit, connectTimeout } =
|
||||
PrismaService.parseDatabaseUrl(databaseUrl);
|
||||
|
||||
const pool = new pg.Pool({
|
||||
connectionString: process.env.DATABASE_URL,
|
||||
connectionString,
|
||||
max: connectionLimit ?? 20,
|
||||
idleTimeoutMillis: 30000,
|
||||
connectionTimeoutMillis: connectTimeout ?? 5000,
|
||||
});
|
||||
|
||||
const adapter = new PrismaPg(pool, {
|
||||
schema,
|
||||
});
|
||||
const adapter = new PrismaPg(pool);
|
||||
|
||||
super({
|
||||
adapter,
|
||||
|
|
@ -26,8 +42,46 @@ export class PrismaService
|
|||
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
private static parseDatabaseUrl(databaseUrl: string): {
|
||||
connectionString: string;
|
||||
schema: string;
|
||||
connectionLimit?: number;
|
||||
connectTimeout?: number;
|
||||
} {
|
||||
try {
|
||||
const url = new URL(databaseUrl);
|
||||
const schema = url.searchParams.get('schema') || 'public';
|
||||
const connectionLimit = url.searchParams.get('connection_limit');
|
||||
const connectTimeout = url.searchParams.get('connect_timeout');
|
||||
|
||||
url.searchParams.delete('schema');
|
||||
url.searchParams.delete('connection_limit');
|
||||
url.searchParams.delete('connect_timeout');
|
||||
|
||||
return {
|
||||
connectionString: url.toString(),
|
||||
schema,
|
||||
connectionLimit: parseIntSafe(connectionLimit),
|
||||
connectTimeout: parseIntSafe(connectTimeout),
|
||||
};
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Invalid DATABASE_URL format: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
await this.$connect();
|
||||
try {
|
||||
// Verify pool connectivity
|
||||
const client = await this.pool.connect();
|
||||
client.release();
|
||||
|
||||
await this.$connect();
|
||||
} catch (error) {
|
||||
throw new Error(`Database connection failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,19 @@ export function delay(ms: number): Promise<void> {
|
|||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely parses a string to an integer, returning undefined if the value is null, undefined, or results in NaN.
|
||||
* @param value The string value to parse
|
||||
* @returns The parsed integer or undefined
|
||||
*/
|
||||
export function parseIntSafe(
|
||||
value: string | null | undefined,
|
||||
): number | undefined {
|
||||
if (!value) return undefined;
|
||||
const parsed = parseInt(value, 10);
|
||||
return !isNaN(parsed) ? parsed : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* A workaround to throw an exception in an expression.
|
||||
* JS throw keyword creates a statement not an expression.
|
||||
|
|
|
|||
Loading…
Reference in a new issue