@hazeljs/prisma
Prisma + HazelJS. Type-safe, no boilerplate.
Repository pattern, @Repository decorator, DI integration. Full CRUD from your schema. Transactions, relations, pagination — the way you'd expect it to work.

Features
- 🎯 Type-Safe Queries - Full TypeScript support with Prisma
- 🏗️ Repository Pattern - Clean data access layer
- 🎨 Decorator Support -
@Repository implies @Injectable() — one decorator does the job
- 🔄 Transaction Support - Built-in transaction management
- 📊 Query Builder - Fluent query interface
- 🔌 Dependency Injection - Seamless DI integration
- 🧪 Testing Utilities - Mock Prisma for testing
- 📈 Connection Pooling - Automatic connection management
Decorator Convention
Repository (extends BaseRepository) | @Repository({ model: '...' }) — implies @Injectable() |
| Service (business logic) | @Service() |
| Controller | @Controller(...) |
Installation
npm install @hazeljs/prisma @prisma/client
npm install -D prisma
Quick Start
1. Initialize Prisma
npx prisma init
2. Define Schema
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id String @id @default(uuid())
email String @unique
name String
posts Post[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post {
id String @id @default(uuid())
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. Generate Prisma Client
npx prisma generate
npx prisma migrate dev --name init
4. Configure Module
import { HazelModule } from '@hazeljs/core';
import { PrismaModule } from '@hazeljs/prisma';
@HazelModule({
imports: [
PrismaModule.forRoot({
connectionString: process.env.DATABASE_URL,
}),
],
})
export class AppModule {}
5. Create Repository
@Repository implies @Injectable() — no need to add both decorators.
import { PrismaService, BaseRepository } from '@hazeljs/prisma';
import { Repository } from '@hazeljs/prisma';
@Repository({ model: 'user' })
export class UserRepository extends BaseRepository<User> {
constructor(prisma: PrismaService) {
super(prisma, 'user');
}
async findByEmail(email: string) {
return this.prisma.user.findUnique({ where: { email } });
}
async findWithPosts(id: string) {
return this.prisma.user.findUnique({
where: { id },
include: { posts: true },
});
}
}
6. Use in a Service
Use @Service for service classes — not @Injectable.
import { Service } from '@hazeljs/core';
import { InjectRepository } from '@hazeljs/prisma';
@Service()
export class UserService {
constructor(@InjectRepository() private readonly userRepository: UserRepository) {}
async create(data: { email: string; name: string }) {
return this.userRepository.create(data);
}
async findAll() {
return this.userRepository.findMany();
}
async findOne(id: string) {
return this.userRepository.findOne({ id });
}
async update(id: string, data: Partial<User>) {
return this.userRepository.update({ id }, data);
}
async delete(id: string) {
return this.userRepository.delete({ id });
}
}
Base Repository
The BaseRepository provides common CRUD operations:
class BaseRepository<T> {
create(data: Omit<T, 'id'>): Promise<T>;
findMany(): Promise<T[]>;
findOne(where: WhereUniqueInput): Promise<T | null>;
count(args?: unknown): Promise<number>;
update(where: WhereUniqueInput, data: UpdateInput): Promise<T>;
delete(where: WhereUniqueInput): Promise<T>;
}
Transactions
Using PrismaService directly
import { Service } from '@hazeljs/core';
import { PrismaService } from '@hazeljs/prisma';
@Service()
export class TransferService {
constructor(private readonly prisma: PrismaService) {}
async transfer(fromId: string, toId: string, amount: number) {
return this.prisma.$transaction(async (tx) => {
await tx.account.update({
where: { id: fromId },
data: { balance: { decrement: amount } },
});
await tx.account.update({
where: { id: toId },
data: { balance: { increment: amount } },
});
return tx.transaction.create({
data: { fromId, toId, amount },
});
});
}
}
Using Repositories inside a transaction
import { Service } from '@hazeljs/core';
import { PrismaService } from '@hazeljs/prisma';
@Service()
export class OrderService {
constructor(
private readonly orderRepository: OrderRepository,
private readonly inventoryRepository: InventoryRepository,
private readonly prisma: PrismaService
) {}
async createOrder(userId: string, items: OrderItem[]) {
return this.prisma.$transaction(async (tx) => {
const order = await tx.order.create({
data: { userId, items: { create: items } },
});
for (const item of items) {
await tx.inventory.update({
where: { productId: item.productId },
data: { quantity: { decrement: item.quantity } },
});
}
return order;
});
}
}
Advanced Queries
Relations
@Repository({ model: 'user' })
export class UserRepository extends BaseRepository<User> {
constructor(prisma: PrismaService) {
super(prisma, 'user');
}
async findWithRelations(id: string) {
return this.prisma.user.findUnique({
where: { id },
include: {
posts: {
where: { published: true },
orderBy: { createdAt: 'desc' },
},
profile: true,
},
});
}
}
@Repository({ model: 'post' })
export class PostRepository extends BaseRepository<Post> {
constructor(prisma: PrismaService) {
super(prisma, 'post');
}
async findPaginated(page: number, limit: number) {
const skip = (page - 1) * limit;
const [posts, total] = await Promise.all([
this.prisma.post.findMany({ skip, take: limit, orderBy: { createdAt: 'desc' } }),
this.prisma.post.count(),
]);
return {
data: posts,
meta: { page, limit, total, totalPages: Math.ceil(total / limit) },
};
}
}
Filtering
@Repository({ model: 'product' })
export class ProductRepository extends BaseRepository<Product> {
constructor(prisma: PrismaService) {
super(prisma, 'product');
}
async search(
query: string,
filters: {
category?: string;
minPrice?: number;
maxPrice?: number;
inStock?: boolean;
}
) {
return this.prisma.product.findMany({
where: {
AND: [
{
OR: [
{ name: { contains: query, mode: 'insensitive' } },
{ description: { contains: query, mode: 'insensitive' } },
],
},
filters.category ? { category: filters.category } : {},
filters.minPrice ? { price: { gte: filters.minPrice } } : {},
filters.maxPrice ? { price: { lte: filters.maxPrice } } : {},
filters.inStock ? { stock: { gt: 0 } } : {},
],
},
});
}
}
Aggregations
import { Service } from '@hazeljs/core';
import { PrismaService } from '@hazeljs/prisma';
@Service()
export class AnalyticsService {
constructor(private readonly prisma: PrismaService) {}
async getOrderStats() {
return this.prisma.order.aggregate({
_sum: { total: true },
_avg: { total: true },
_count: true,
_max: { total: true },
_min: { total: true },
});
}
async getRevenueByMonth() {
return this.prisma.$queryRaw`
SELECT
DATE_TRUNC('month', "createdAt") as month,
SUM(total) as revenue,
COUNT(*) as orders
FROM "Order"
GROUP BY month
ORDER BY month DESC
`;
}
}
Middleware
Add Prisma middleware for logging, soft deletes, etc:
import { Injectable } from '@hazeljs/core';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient {
constructor() {
super();
this.$use(async (params, next) => {
const before = Date.now();
const result = await next(params);
console.log(`Query ${params.model}.${params.action} took ${Date.now() - before}ms`);
return result;
});
this.$use(async (params, next) => {
if (params.action === 'delete') {
params.action = 'update';
params.args['data'] = { deletedAt: new Date() };
}
return next(params);
});
}
}
Seeding
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
await prisma.user.create({
data: {
email: 'alice@example.com',
name: 'Alice',
posts: { create: [{ title: 'First Post', content: 'Hello World!', published: true }] },
},
});
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(() => prisma.$disconnect());
Add to package.json:
{
"prisma": {
"seed": "ts-node prisma/seed.ts"
}
}
Run seed:
npx prisma db seed
Migration Commands
npx prisma migrate dev --name add_user_role
npx prisma migrate deploy
npx prisma migrate reset
npx prisma generate
npx prisma studio
Links
License
Apache 2.0 © HazelJS