@makebelieve21213-packages/prisma-client
v1.0.1
Published
Prisma Client wrapper for database
Maintainers
Readme
@packages/prisma-client
Легковесный Prisma Client wrapper для NestJS с паттерном Singleton и удобной интеграцией для работы с PostgreSQL базой данных.
📋 Содержание
- Возможности
- Требования
- Установка
- Структура пакета
- Быстрый старт
- Использование модулей и сервисов
- API Reference
- Типы и интерфейсы
- Troubleshooting
- Тестирование
🚀 Возможности
- ✅ Singleton pattern - единый экземпляр Prisma Client для всего приложения
- ✅ NestJS интеграция - глобальный модуль с поддержкой
forRootAsync - ✅ Type-safe API - полная типобезопасность TypeScript
- ✅ Generic типы - поддержка кастомных Prisma Client экземпляров
- ✅ 100% покрытие тестами - надежность и качество кода
- ✅ Graceful shutdown - корректное отключение при остановке приложения
- ✅ Гибкая конфигурация - использование фабрики для создания клиента
📋 Требования
- Node.js: >= 22.11.0
- NestJS: >= 11.0.0
- Prisma: >= 6.0.0
- PostgreSQL: >= 12.0.0
📦 Установка
npm install @packages/prisma-clientЗависимости
Пакет требует следующие peer dependencies:
{
"@nestjs/common": "^11.0.0",
"@prisma/client": "^6.0.0",
"reflect-metadata": "^0.1.13 || ^0.2.0"
}📁 Структура пакета
src/
├── main/ # Основная логика
│ ├── prisma.module.ts # PrismaModule - глобальный NestJS модуль
│ └── prisma.service.ts # PrismaService - Singleton сервис
│
├── types/ # TypeScript типы и интерфейсы
│ ├── module-options.interface.ts # Опции конфигурации модуля
│ └── prisma-client.interface.ts # Интерфейс PrismaClientLike
│
├── utils/ # Утилиты
│ └── injection-keys.ts # DI токены
│
├── errors/ # Кастомные ошибки
│ └── prisma.error.ts # PrismaClientError
│
└── index.ts # Точка входа (экспорты)🔧 Быстрый старт
Шаг 1: Создайте Prisma Client
Сначала создайте свой Prisma Client согласно документации Prisma. Например:
// src/prisma/client.ts
import { PrismaClient as GeneratedPrismaClient } from '@prisma/client';
export const prismaClient = new GeneratedPrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
});Шаг 2: Создайте конфигурацию
// src/configs/database.config.ts
import { registerAs } from '@nestjs/config';
import type { PrismaClientModuleOptions } from '@packages/prisma-client';
import { prismaClient } from '../prisma/client';
export default registerAs(
'database',
(): PrismaClientModuleOptions => ({
clientFactory: () => prismaClient,
}),
);Шаг 3: Импортируйте модуль в AppModule
// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { PrismaModule } from '@packages/prisma-client';
import databaseConfig from './configs/database.config';
@Module({
imports: [
ConfigModule.forRoot({
load: [databaseConfig],
isGlobal: true,
}),
PrismaModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => configService.get('database')!,
inject: [ConfigService],
}),
],
})
export class AppModule {}Шаг 4: Используйте PrismaService в сервисах
// user.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from '@packages/prisma-client';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class UserService {
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
async findAll() {
return await this.prisma.client.user.findMany();
}
async findById(id: string) {
return await this.prisma.client.user.findUnique({
where: { id },
});
}
async create(data: { name: string; email: string }) {
return await this.prisma.client.user.create({ data });
}
}Готово! Модуль автоматически:
- Подключится к базе данных при старте
- Создаст единое подключение для всего приложения
- Отключится при shutdown
📚 Использование модулей и сервисов
PrismaModule
Назначение: Глобальный модуль для единого подключения к Prisma Client.
Метод инициализации:
forRootAsync(options)
PrismaModule.forRootAsync({
useFactory: (configService: ConfigService) => ({
clientFactory: () => prismaClient,
}),
inject: [ConfigService],
imports: [ConfigModule],
})Параметры:
useFactory: (deps) => PrismaClientModuleOptions- фабрика для создания опций модуляinject?: InjectionToken[]- зависимости для инжекции в useFactoryimports?: Module[]- дополнительные модули для DI
Экспортирует: PrismaService
PrismaService
Методы:
get client(): TClient
Получить экземпляр Prisma Client.
const users = await prismaService.client.user.findMany();Возвращает:
TClient- экземпляр Prisma Client- Выбрасывает
PrismaClientError, если клиент не инициализирован
disconnect(): Promise<void>
Отключиться от базы данных (вызывается автоматически при shutdown).
await prismaService.disconnect();PrismaClientError
Кастомная ошибка Prisma Client.
import { PrismaClientError } from '@packages/prisma-client';
try {
await prismaService.client.user.findMany();
} catch (error) {
if (error instanceof PrismaClientError) {
const originalError = error.getOriginalError();
}
}Особенности:
- Сохраняет stack trace оригинальной ошибки
- Предоставляет доступ к оригинальной ошибке через
getOriginalError()
🔧 Настройка переменных окружения
Добавьте в .env:
DATABASE_URL="postgresql://user:password@localhost:5432/database?schema=public"🎯 Типы и интерфейсы
PrismaClientModuleOptions
Опции конфигурации для Prisma модуля.
interface PrismaClientModuleOptions<TClient extends PrismaClientLike = PrismaClientLike> {
/** Фабрика для создания экземпляра PrismaClient */
clientFactory: () => TClient;
}PrismaClientModuleAsyncOptions
Асинхронные опции для динамической конфигурации модуля.
interface PrismaClientModuleAsyncOptions<
T extends unknown[] = [],
TClient extends PrismaClientLike = PrismaClientLike,
> {
/** Фабрика для создания опций */
useFactory: (...args: T) => Promise<PrismaClientModuleOptions<TClient>> | PrismaClientModuleOptions<TClient>;
/** Зависимости для инжекции в useFactory */
inject?: (InjectionToken | OptionalFactoryDependency)[];
/** Дополнительные модули для DI */
imports?: Module[];
}PrismaClientLike
Интерфейс для Prisma Client (используется для generic типов).
interface PrismaClientLike {
$disconnect: () => Promise<void>;
}📖 Примеры использования
Базовое использование
@Injectable()
export class UserService {
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
async findAll() {
return await this.prisma.client.user.findMany();
}
async findById(id: string) {
return await this.prisma.client.user.findUnique({
where: { id },
});
}
}Использование с кастомным Prisma Client
// Определите свой Prisma Client
class CustomPrismaClient extends PrismaClient {
// Кастомные методы
async customMethod() {
// ...
}
}
// Используйте в конфигурации
PrismaModule.forRootAsync({
useFactory: () => ({
clientFactory: () => new CustomPrismaClient(),
}),
})
// Используйте в сервисе
@Injectable()
export class CustomService {
constructor(private readonly prisma: PrismaService<CustomPrismaClient>) {}
async useCustomMethod() {
await this.prisma.client.customMethod();
}
}Работа с транзакциями
@Injectable()
export class TransactionService {
constructor(private readonly prisma: PrismaService<PrismaClient>) {}
async createWithTransaction() {
return await this.prisma.client.$transaction(async (tx) => {
const user = await tx.user.create({
data: { name: 'John', email: '[email protected]' },
});
await tx.post.create({
data: { title: 'Post', userId: user.id },
});
return user;
});
}
}🧪 Тестирование
Пакет имеет 100% покрытие тестами.
# Запустить тесты
npm test
# Запустить тесты с покрытием
npm run test:coverage
# Watch режим
npm run test:watch🚨 Troubleshooting
Prisma Client не инициализирован
Проблема: Prisma Client не инициализирован
Решение:
- Убедитесь, что
PrismaModuleимпортирован вAppModule - Проверьте, что
onModuleInit()был вызван (происходит автоматически) - Проверьте порядок импорта модулей
- Убедитесь, что
clientFactoryвозвращает валидный Prisma Client
Ошибка подключения к БД
Проблема: Не удается подключиться к базе данных
Решение:
- Проверьте переменные окружения (
DATABASE_URL) - Убедитесь, что PostgreSQL запущен
- Проверьте логи приложения
- Проверьте сетевые настройки (firewall, Docker network)
Проблемы с generic типами
Проблема: TypeScript ошибки при использовании generic типов
Решение:
- Убедитесь, что передаете правильный тип в
PrismaService<YourPrismaClient> - Проверьте, что ваш Prisma Client реализует интерфейс
PrismaClientLike - Используйте явное указание типа при инжекции
Проблемы с производительностью
Проблема: Медленные запросы к БД
Решение:
- Используйте connection pooling (реализовано автоматически через Prisma)
- Проверьте индексы в базе данных
- Используйте
selectдля получения только нужных полей - Оптимизируйте запросы (избегайте N+1 проблем)
🔑 Особенности реализации
Singleton Pattern
Prisma Service использует паттерн Singleton для создания единого экземпляра подключения:
private prismaClient: TClient | null = null;
async onModuleInit(): Promise<void> {
if (!this.prismaClient) {
this.prismaClient = this.options.clientFactory();
}
}Преимущества:
- Экономия ресурсов (одно подключение вместо множества)
- Автоматический connection pooling через Prisma
- Безопасное переиспользование в разных модулях
Graceful Shutdown
Модуль автоматически закрывает подключение при остановке приложения:
async onModuleDestroy(): Promise<void> {
await this.disconnect();
}Generic типы
Сервис поддерживает generic типы для работы с кастомными Prisma Client экземплярами:
PrismaService<CustomPrismaClient>Обработка ошибок
Кастомная ошибка PrismaClientError:
- Сохраняет stack trace оригинальной ошибки
- Предоставляет доступ к оригинальной ошибке через
getOriginalError()
📄 Лицензия
MIT License
👥 Автор
Skryabin Aleksey
