1import { Injectable, OnModuleDestroy, OnModuleInit } from "@nestjs/common";
2import { PrismaClient } from "@prisma/client";
3import { loadEnv } from "../config/env.js";
4import { logger } from "./logger.js";
5
6
20@Injectable()
21export class PrismaService extends PrismaClient implements OnModuleInit, OnModuleDestroy {
22
23 readonly reader: PrismaClient;
24
25 readonly hasReplica: boolean;
26
27 constructor() {
28 const env = loadEnv();
29 super({
30 datasources: { db: { url: env.DATABASE_URL } },
31 log: process.env.NODE_ENV === "production" ? ["warn", "error"] : ["warn", "error"]
32 });
33 const replicaUrl = env.DATABASE_REPLICA_URL?.trim();
34 if (replicaUrl) {
35 this.reader = new PrismaClient({
36 datasources: { db: { url: replicaUrl } },
37 log: ["warn", "error"]
38 });
39 this.hasReplica = true;
40 } else {
41 this.reader = this;
42 this.hasReplica = false;
43 }
44 }
45
46
47 read(): PrismaClient {
48 return this.reader;
49 }
50
51
52 write(): PrismaClient {
53 return this;
54 }
55
56 async onModuleInit(): Promise<void> {
57 try {
58 await this.$connect();
59 if (this.hasReplica) {
60
61
62 await this.reader.$connect();
63 logger.info("prisma connected (primary + read-replica)");
64 } else {
65 logger.info("prisma connected");
66 }
67 } catch (err) {
68
69 logger.warn(`prisma connect failed: ${(err as Error).message}`);
70 if (process.env.NODE_ENV === "production") throw err;
71 }
72 }
73
74 async onModuleDestroy(): Promise<void> {
75 await this.$disconnect();
76 if (this.hasReplica) await this.reader.$disconnect();
77 }
78}
79