@eyjs/prisma
Version:
Prisma integration package for EyJS framework
592 lines (469 loc) • 12.6 kB
Markdown
# @eyjs/prisma
[](https://badge.fury.io/js/%40eyjs%2Fprisma)
[](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)