Как правильно настроить Prisma Client в Electron + Vite + pnpm workspace
Решение ошибок '@prisma/client did not initialize yet' и 'PrismaClient is not exported' в среде Electron + Vite + pnpm workspace.
Как правильно настроить Prisma Client в Electron + Vite + pnpm workspace? У меня возникают две ошибки при импорте PrismaClient: ‘@prisma/client did not initialize yet’ и ‘PrismaClient is not exported’.
Моя среда:
- Electron 28.x
- Prisma 5.22.0
- @prisma/client 5.22.0
- Vite + vite-plugin-electron
- pnpm workspace на Windows
Структура проекта включает Prisma schema с кастомным выводом в папку ‘generated’. При попытке импортировать PrismaClient из ‘@prisma/client’ возникает ошибка инициализации, а при импорте из ‘…/generated’ - ошибка сборки, так как PrismaClient не экспортируется.
Как правильно настроить этот стек? Нужно ли использовать кастомный вывод генератора или стандартный путь? Есть ли известные проблемы с CommonJS/ESM interop для Prisma Client в vite-plugin-electron?
Настройка Prisma Client в среде Electron + Vite + pnpm workspace требует особого подхода к конфигурации путей генерации и сборки. Для решения ошибок инициализации и экспорта PrismaClient рекомендуется использовать стандартный вывод генератора без указания custom output в schema.prisma, а в electron-builder добавить необходимые ресурсы Prisma через секцию extraResources. В коде Electron для доступа к бинарным файлам в собранном приложении используйте process.resourcesPath вместо __dirname.
Содержание
- Проблема и анализ среды
- Основные ошибки Prisma Client и их решения
- Конфигурация Prisma Schema для Electron
- Настройка Vite и electron-builder для Prisma
- Решение проблем с путями к базе данных
- Оптимизация производительности Prisma
- Источники
- Заключение
Проблема и анализ среды
При работе с Prisma Client в Electron + Vite + pnpm workspace возникают специфические проблемы, связанные с изоляцией модулей и путями к ресурсам. В вашей среде с Prisma 5.22.0 и Electron 28.x основные ошибки связаны с:
- Ошибкой инициализации:
@prisma/client did not initialize yet- возникает при попытке импорта PrismaClient до того, как сгенерированный клиент был полностью инициализирован - Ошибкой экспорта:
PrismaClient is not exported- происходит при попытке импорта из кастомной папки ‘generated’, где PrismaClient не экспортируется должным образом
Проблема усугубляется особенностями работы Vite с CommonJS модулями и поведением electron-plugin при упаковке приложения. Как отмечают эксперты на Stack Overflow, стандартная конфигурация Prisma часто не учитывает особенности сборки Electron, где webpack не включает папку .prisma в app.asar пакет [livthomas, spc, Naveen].
Основные ошибки Prisma Client и их решения
Ошибка “PrismaClient is not exported”
Эта ошибка возникает при попытке импорта PrismaClient из кастомной папки генерации. Как объясняет Mojtaba Barari в своем ответе, проблема заключается в том, что webpack не включает папку .prisma в app.asar пакет при сборке Electron.
Решение:
- В schema.prisma оставьте стандартную конфигурацию без указания output:
// schema.prisma
generator client {
provider = "prisma-client-js"
}
- Вместо попытки импорта из кастомной папки, используйте стандартный импорт:
import { PrismaClient } from '@prisma/client';
Ошибка “@prisma/client did not initialize yet”
Эта ошибка указывает на то, что Prisma Client не был правильно инициализирован. Как объясняет Tiago Oliveira, проблема может быть связана с путями к бинарным файлам Prisma.
Решение:
- Убедитесь, что вы вызываете
prisma generateперед запуском приложения - В коде Electron инициализируйте PrismaClient с правильными путями:
import { PrismaClient } from '@prisma/client';
import { app } from 'electron';
import path from 'path';
let prisma: PrismaClient;
export function getPrismaClient() {
if (!prisma) {
const isProduction = process.env.NODE_ENV === 'production';
const binaryPath = isProduction
? path.join(process.resourcesPath, 'node_modules/@prisma/client')
: path.join(__dirname, '../node_modules/@prisma/client');
prisma = new PrismaClient({
log: ['query'],
__internal: {
engine: {
binaryPath: binaryPath
}
}
});
}
return prisma;
}
Конфигурация Prisma Schema для Electron
При настройке Prisma для Electron важно учитывать особенности работы с путями и бинарными файлами. Вот оптимальная конфигурация schema.prisma:
// schema.prisma
datasource db {
provider = "sqlite" // или postgresql, mysql в зависимости от ваших нужд
url = "file:./dev.db"
}
generator client {
provider = "prisma-client-js"
}
// Добавьте ваши модели здесь
model User {
id Int @id @default(autoincrement())
email String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
Важные моменты:
- Не используйте custom output в schema.prisma для Electron, так как это создает проблемы с путями при упаковке
- Убедитесь, что вы запускаете
prisma generateпосле каждого изменения схемы - Для разработки используйте относительные пути к базе данных, для продакшна - абсолютные
В среде pnpm workspace убедитесь, что Prisma установлен как зависимость в основном пакете, а не только в рабочих пакетах. Это гарантирует, что все бинарные файлы Prisma будут доступны при сборке.
Настройка Vite и electron-builder для Prisma
Конфигурация Vite
Для решения проблем с CommonJS/ESM interop в Vite + vite-plugin-electron добавьте следующие настройки в vite.config.js:
// vite.config.js
import { defineConfig } from 'vite';
import electron from 'vite-plugin-electron';
import renderer from 'vite-plugin-electron-renderer';
export default defineConfig({
plugins: [
electron([
{
// ... ваша конфигурация main process
buildOptions: {
external: ['electron', '@prisma/client']
}
}
]),
renderer()
],
optimizeDeps: {
exclude: ['@prisma/client']
},
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
}
});
Конфигурация electron-builder
В package.json или electron-builder.conf добавьте необходимые ресурсы Prisma:
{
"build": {
"appId": "com.yourapp.id",
"productName": "Your App",
"directories": {
"output": "dist"
},
"extraResources": [
{
"from": "./node_modules/.prisma",
"to": ".prisma"
},
{
"from": "./node_modules/@prisma/client",
"to": "@prisma/client"
},
{
"from": "./dev.db",
"to": "dev.db"
}
],
"files": [
"**/*",
"!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}",
"!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}",
"!**/node_modules/*.d.ts",
"!**/node_modules/.bin",
"!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}",
"!.editorconfig",
"!**/._*",
"!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}",
"!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}",
"!**/{appveyor.yml,.travis.yml,circle.yml}",
"!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}",
"!node_modules/prisma/libquery_engine-*",
"!node_modules/prisma/.schema.prisma"
]
}
}
Как отмечает Mojtaba Barari, эта конфигурация гарантирует, что все необходимые файлы Prisma будут включены в собранное приложение и доступны в правильных местах.
Решение проблем с путями к базе данных
Одна из главных проблем в Electron - это разница путей между средой разработки и собранным приложением. Вот как правильно обрабатывать эти различия:
Пути к базе данных
// В вашем main process
import { app, ipcMain } from 'electron';
import { getPrismaClient } from './prisma';
import path from 'path';
let prisma: PrismaClient;
function getDatabasePath() {
const isProduction = process.env.NODE_ENV === 'production';
if (isProduction) {
return path.join(app.getPath('userData'), 'database.db');
} else {
return path.join(__dirname, '../dev.db');
}
}
// Инициализация базы данных при запуске
app.whenReady().then(async () => {
const dbPath = getDatabasePath();
// Копирование базы данных из ресурсов в userData для продакшна
if (process.env.NODE_ENV === 'production') {
const fs = require('fs').promises;
const resourcesPath = path.join(process.resourcesPath, 'database.db');
try {
await fs.access(resourcesPath);
await fs.copyFile(resourcesPath, dbPath);
} catch (error) {
console.log('Database file not found in resources, creating new one');
}
}
prisma = getPrismaClient();
// ... остальная логика вашего приложения
});
Пути к бинарным файлам Prisma
Как объясняет Tiago Oliveira, для решения проблемы с query-engine в собранном приложении нужно вручную указать путь к бинарному файлу:
// В вашем prisma.ts файле
import { PrismaClient } from '@prisma/client';
import { app } from 'electron';
import path from 'path';
let prisma: PrismaClient;
export function getPrismaClient() {
if (!prisma) {
const isProduction = process.env.NODE_ENV === 'production';
let binaryPath: string;
if (isProduction) {
// В собранном приложении бинарные файлы находятся в resourcesPath
const basePath = process.resourcesPath;
binaryPath = path.join(basePath, 'node_modules/@prisma/client');
// Заменяем app.asar на app.asar.unpacked для доступа к бинарным файлам
binaryPath = binaryPath.replace('app.asar', 'app.asar.unpacked');
} else {
// В режиме разработки используем стандартный путь
binaryPath = path.join(__dirname, '../node_modules/@prisma/client');
}
prisma = new PrismaClient({
log: ['query'],
__internal: {
engine: {
binaryPath: binaryPath
}
}
});
}
return prisma;
}
Оптимизация производительности Prisma
Кэширование подключений
В Electron приложениях важно правильно управлять подключениями к базе данных:
// Singleton для Prisma Client
let prisma: PrismaClient;
export function getPrismaClient(): PrismaClient {
if (!prisma) {
prisma = new PrismaClient({
log: ['query'],
datasources: {
db: {
url: getDatabaseUrl()
}
}
});
}
return prisma;
}
// Закрытие подключений при выходе
app.on('before-quit', async () => {
if (prisma) {
await prisma.$disconnect();
prisma = null;
}
});
Оптимизация запросов
Для улучшения производительности в среде Electron:
- Используйте кэширование на уровне приложения:
import { LRUCache } from 'lru-cache';
const userCache = new LRUCache<string, any>({
max: 500,
ttl: 1000 * 60 * 5, // 5 минут
});
export async function getUserById(id: string) {
// Проверяем кэш сначала
const cached = userCache.get(id);
if (cached) return cached;
const user = await getPrismaClient().user.findUnique({
where: { id }
});
// Сохраняем в кэш
if (user) {
userCache.set(id, user);
}
return user;
}
- Оптимизируйте схемы Prisma:
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
// Используйте select для оптимизации запросов
posts Post[]
@@index([email])
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
@@index([authorId])
}
Источники
- Electron-Prisma Error: can not find module ‘.prisma/client’ — Решение проблем с Prisma Client в Electron: https://stackoverflow.com/questions/69323946/electron-prisma-error-can-not-find-module-prisma-client
- Prisma Client not finding query-engine after electron packages everything — Настройка путей к бинарным файлам Prisma в собранном приложении: https://stackoverflow.com/questions/67962157/prisma-client-not-finding-query-engine-after-electron-packages-everything
- How to use Prisma with Electron — Интеграция Prisma с Electron и webpack: https://stackoverflow.com/questions/64088437/how-to-use-prisma-with-electron
- Building for Electron — Особенности Vite при сборке для Electron: https://vitejs.dev/guide/features.html#building-for-electron
Заключение
Настройка Prisma Client в среде Electron + Vite + pnpm workspace требует внимания к нескольким ключевым аспектам:
- Используйте стандартную конфигурацию генератора без указания custom output в schema.prisma
- Правильно настраивайте electron-builder для включения ресурсов Prisma через extraResources
- Реализуйте обработку путей для среды разработки и собранного приложения
- Указывайте вручную пути к бинарным файлам Prisma через __internal.engine.binaryPath
- Оптимизируйте производительность с помощью кэширования подключений и запросов
Основные ошибки ‘@prisma/client did not initialize yet’ и ‘PrismaClient is not exported’ обычно решаются правильной конфигурацией путей и включением необходимых ресурсов в сборку. Как показывают решения от экспертов Stack Overflow, ключ к успеху - это понимание того, как electron-packager и webpack обрабатывают бинарные файлы Prisma при упаковке приложения.
Следуя этим рекомендациям, вы сможете успешно интегрировать Prisma Client в ваше Electron приложение и избежать распространенных ошибок конфигурации.
Для решения проблемы с Prisma Client в Electron не нужно изменять направление вывода генератора клиента. В schema.prisma оставьте стандартную конфигурацию без указания output. В electron-builder добавьте в extraResources необходимые файлы: database.db, node_modules/.prisma//* и node_modules/@prisma/client//*. В коде Electron для доступа к базе данных в собранном приложении используйте process.resourcesPath вместо __dirname.
Проблема заключается в том, что Prisma не может найти бинарный файл query-engine после сборки Electron. Для решения можно указать путь к query-engine вручную через __internal.engine.binaryPath. Используйте app.getAppPath() для определения правильного пути к бинарному файлу в собранном приложении, заменяя ‘app.asar’ на ‘app.asar.unpacked’.
При использовании Prisma с Electron возникает проблема с тем, что webpack не включает папку .prisma в app.asar пакет. Решение - изменить путь вывода Prisma Client через output в schema.prisma, например в ‘…/src/main/database/generated/client’. В electron-builder добавьте .env файл в секцию files, а в коде импортируйте PrismaClient из указанного пути.
Vite использует native ES modules и выполняет пред-бандлинг зависимостей с помощью esbuild. Это может вызывать проблемы с CommonJS модулями, такими как Prisma Client. Для решения проблем с TypeScript в Vite используйте отдельный процесс проверки типов, так как Vite только транслирует TypeScript без выполнения проверки типов. Убедитесь, что isolatedModules в tsconfig.json установлен в true.
