UNPKG

@eyjs/prisma

Version:

Prisma integration package for EyJS framework

592 lines (469 loc) 12.6 kB
# @eyjs/prisma [![npm version](https://badge.fury.io/js/%40eyjs%2Fprisma.svg)](https://badge.fury.io/js/%40eyjs%2Fprisma) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) Prisma integration package for the EyJS framework. Provides decorator-based model mapping, repository patterns, and seamless database integration with dependency injection. ## Features - 🗄️ **Prisma Integration** - Full support for Prisma ORM - 🎯 **Decorator-Based Models** - Simple `@PrismaModel()` decorator for entity mapping - 🏗️ **Repository Pattern** - Clean repository implementation with base classes - 🔧 **TypeScript Support** - Full TypeScript definitions and type safety - **EyJS Integration** - Works seamlessly with EyJS dependency injection - 🚀 **Auto-Generated Types** - Leverages Prisma's generated types ## Installation ```bash # Using Bun (recommended) bun add @eyjs/prisma # Using npm npm install @eyjs/prisma # Using yarn yarn add @eyjs/prisma # Using pnpm pnpm add @eyjs/prisma ``` ## Prerequisites This package requires: - `@eyjs/core@^1.0.4` - EyJS core framework - `@prisma/client@^6.8.1` - Prisma client ## Quick Start ### 1. Prisma Setup First, set up Prisma in your project: ```bash # Install Prisma CLI bun add -D prisma # Initialize Prisma bunx prisma init # Generate Prisma client bunx prisma generate ``` ### 2. Database Schema Define your schema in `prisma/schema.prisma`: ```prisma generator client { provider = "prisma-client-js" } datasource db { provider = "postgresql" url = env("DATABASE_URL") } model User { id String @id @default(cuid()) email String @unique name String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt posts Post[] } model Post { id String @id @default(cuid()) title String content String? published Boolean @default(false) authorId String author User @relation(fields: [authorId], references: [id]) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } ``` ### 3. Basic Usage ```typescript import { PrismaModel } from '@eyjs/prisma' import { Injectable } from '@eyjs/core' import { PrismaClient } from '@prisma/client' @Injectable() @PrismaModel('User') export class UserRepository { constructor(private readonly prisma: PrismaClient) {} async findById(id: string) { return await this.prisma.user.findUnique({ where: { id } }) } async create(data: { email: string; name?: string }) { return await this.prisma.user.create({ data }) } } ``` ## API Reference ### Decorators #### `@PrismaModel(modelName)` Maps a repository class to a Prisma model. ```typescript @PrismaModel('User') // Maps to Prisma User model export class UserRepository { // Repository implementation } ``` ### Base Repository #### `BaseRepository<T>` Provides common CRUD operations for Prisma models. ```typescript import { BaseRepository } from '@eyjs/prisma' export class UserRepository extends BaseRepository<User> { constructor(prisma: PrismaClient) { super(prisma.user) } // Custom methods async findByEmail(email: string) { return await this.model.findUnique({ where: { email } }) } } ``` ## Usage Examples ### Basic Repository Pattern ```typescript import { PrismaModel } from '@eyjs/prisma' import { Injectable } from '@eyjs/core' import { PrismaClient, User } from '@prisma/client' @Injectable() @PrismaModel('User') export class UserRepository { constructor(private readonly prisma: PrismaClient) {} async findById(id: string): Promise<User | null> { return await this.prisma.user.findUnique({ where: { id } }) } async findByEmail(email: string): Promise<User | null> { return await this.prisma.user.findUnique({ where: { email } }) } async create(data: { email: string name?: string }): Promise<User> { return await this.prisma.user.create({ data }) } async update(id: string, data: Partial<User>): Promise<User> { return await this.prisma.user.update({ where: { id }, data }) } async delete(id: string): Promise<User> { return await this.prisma.user.delete({ where: { id } }) } async findAll(): Promise<User[]> { return await this.prisma.user.findMany() } } ``` ### Using Base Repository ```typescript import { BaseRepository } from '@eyjs/prisma' import { Injectable } from '@eyjs/core' import { PrismaClient, User } from '@prisma/client' @Injectable() export class UserRepository extends BaseRepository<User> { constructor(private readonly prisma: PrismaClient) { super(prisma.user) } // Inherits: findById, create, update, delete, findAll // Custom methods async findByEmail(email: string): Promise<User | null> { return await this.model.findUnique({ where: { email } }) } async findActiveUsers(): Promise<User[]> { return await this.model.findMany({ where: { // Add your active user criteria } }) } } ``` ### Service Layer Integration ```typescript import { Injectable } from '@eyjs/core' import { UserRepository } from './user.repository' import { User } from '@prisma/client' @Injectable() export class UserService { constructor(private readonly userRepository: UserRepository) {} async getUserById(id: string): Promise<User | null> { return await this.userRepository.findById(id) } async createUser(data: { email: string name?: string }): Promise<User> { // Check if user already exists const existingUser = await this.userRepository.findByEmail(data.email) if (existingUser) { throw new Error('User already exists') } return await this.userRepository.create(data) } async updateUser(id: string, data: Partial<User>): Promise<User> { const user = await this.userRepository.findById(id) if (!user) { throw new Error('User not found') } return await this.userRepository.update(id, data) } } ``` ### Complex Queries ```typescript import { Injectable } from '@eyjs/core' import { PrismaClient } from '@prisma/client' @Injectable() export class PostRepository { constructor(private readonly prisma: PrismaClient) {} async findPostsWithAuthor() { return await this.prisma.post.findMany({ include: { author: true } }) } async findPublishedPostsByAuthor(authorId: string) { return await this.prisma.post.findMany({ where: { authorId, published: true }, include: { author: { select: { id: true, name: true, email: true } } }, orderBy: { createdAt: 'desc' } }) } async searchPosts(query: string) { return await this.prisma.post.findMany({ where: { OR: [ { title: { contains: query, mode: 'insensitive' } }, { content: { contains: query, mode: 'insensitive' } } ], published: true }, include: { author: { select: { name: true } } } }) } } ``` ### Transaction Support ```typescript import { Injectable } from '@eyjs/core' import { PrismaClient } from '@prisma/client' @Injectable() export class UserService { constructor(private readonly prisma: PrismaClient) {} async createUserWithPost(userData: { email: string name?: string }, postData: { title: string content?: string }) { return await this.prisma.$transaction(async (tx) => { // Create user const user = await tx.user.create({ data: userData }) // Create post const post = await tx.post.create({ data: { ...postData, authorId: user.id } }) return { user, post } }) } } ``` ## Configuration ### Environment Variables ```env # Database connection DATABASE_URL="postgresql://username:password@localhost:5432/mydb" # Prisma configuration PRISMA_GENERATE_DATAPROXY="false" ``` ### Prisma Client Configuration ```typescript import { PrismaClient } from '@prisma/client' const prisma = new PrismaClient({ log: ['query', 'info', 'warn', 'error'], errorFormat: 'pretty' }) export default prisma ``` ### EyJS Integration ```typescript import { EyJs } from '@eyjs/core' import { PrismaClient } from '@prisma/client' // Register Prisma client in EyJS container const container = await EyJs.start(import.meta.url) container.register(PrismaClient, 'singleton', false, [], 'infrastructure') ``` ## Advanced Usage ### Custom Repository with Relations ```typescript import { Injectable } from '@eyjs/core' import { PrismaClient } from '@prisma/client' @Injectable() export class PostRepository { constructor(private readonly prisma: PrismaClient) {} async findWithComments(postId: string) { return await this.prisma.post.findUnique({ where: { id: postId }, include: { author: true, comments: { include: { author: true }, orderBy: { createdAt: 'desc' } } } }) } async findPostsByCategory(categoryId: string) { return await this.prisma.post.findMany({ where: { categories: { some: { id: categoryId } } }, include: { author: { select: { name: true } }, categories: true } }) } } ``` ### Pagination Support ```typescript import { Injectable } from '@eyjs/core' import { PrismaClient } from '@prisma/client' @Injectable() export class PostRepository { constructor(private readonly prisma: PrismaClient) {} async findPaginated(page: number = 1, limit: number = 10) { const skip = (page - 1) * limit const [posts, total] = await Promise.all([ this.prisma.post.findMany({ skip, take: limit, include: { author: { select: { name: true } } }, orderBy: { createdAt: 'desc' } }), this.prisma.post.count() ]) return { posts, pagination: { page, limit, total, pages: Math.ceil(total / limit) } } } } ``` ## Testing ### Unit Tests ```typescript import { describe, it, expect, beforeEach } from 'bun:test' import { PrismaClient } from '@prisma/client' import { UserRepository } from './user.repository' describe('UserRepository', () => { let prisma: PrismaClient let userRepository: UserRepository beforeEach(async () => { prisma = new PrismaClient() userRepository = new UserRepository(prisma) }) it('should create a user', async () => { const userData = { email: 'test@example.com', name: 'Test User' } const user = await userRepository.create(userData) expect(user.email).toBe(userData.email) expect(user.name).toBe(userData.name) }) it('should find user by email', async () => { const userData = { email: 'test@example.com', name: 'Test User' } await userRepository.create(userData) const foundUser = await userRepository.findByEmail(userData.email) expect(foundUser).not.toBeNull() expect(foundUser?.email).toBe(userData.email) }) }) ``` ## Troubleshooting ### Common Issues **1. "PrismaClient is not defined" error** - Ensure Prisma client is generated: `bunx prisma generate` - Check that `@prisma/client` is installed **2. Database connection errors** - Verify `DATABASE_URL` environment variable - Check database server is running - Verify connection credentials **3. Type errors with Prisma models** - Run `bunx prisma generate` after schema changes - Restart TypeScript server in your IDE ### Debug Mode Enable Prisma query logging: ```typescript const prisma = new PrismaClient({ log: ['query', 'info', 'warn', 'error'] }) ``` ## Contributing Contributions are welcome! Please read our [Contributing Guide](https://github.com/elevenyellow/EyJS-core/blob/main/CONTRIBUTING.md) for details. ## License This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. ## Support - 📖 [Documentation](https://github.com/elevenyellow/EyJS-core#readme) - 🐛 [Issue Tracker](https://github.com/elevenyellow/EyJS-core/issues) - 💬 [Discussions](https://github.com/elevenyellow/EyJS-core/discussions)