@rs-tech-hub/nestjs-prisma
Prisma ORM integration for NestJS applications with PostgreSQL adapter, providing seamless database management, type-safe queries, and repository pattern support.
๐ Table of Contents
- โจ Features
- ๐ Prerequisites
- ๐ฆ Installation
- Schema
- ๐ Module Registration
- ๐ป Service Usage
- ๐๏ธ Repository Pattern
- ๐ Repository API Reference
- ๐ Advanced Usage
- ๐ก Best Practices
- ๐ License
- ๐ Support
๐ Related Documentation
- Service Operation - Repository base functionality
- User Module - User data persistence
- Account Starter - Account data management
- Profile Module - Profile data storage
- Activation Token - Token storage
- Refresh Token - Refresh token persistence
- Getting Started - Database setup
โจ Features
- ๐ PostgreSQL Adapter - Built-in PrismaPg adapter with connection pooling
- ๐ฏ Type-Safe Queries - Full TypeScript support with Prisma Client
- ๐๏ธ Repository Factory - Generic repository pattern with automatic type inference
- ๐ Lifecycle Hooks - Automatic connection management (connect/disconnect)
- ๐งน Data Cleanup - Helper methods to clean nested Prisma results
- ๐ Global Module - Available throughout your application
- ๐ Comprehensive CRUD - Find, create, update, upsert, delete operations
๐ Prerequisites
- Node.js โฅ 20.11.1
- NestJS โฅ 11.1.6
- TypeScript โฅ 5.1.0
- Prisma โฅ 7.0.0
- PostgreSQL database
๐ฆ Installation
npm install @rs-tech-hub/nestjs-prisma \
@rs-tech-hub/nestjs-service-operation \
@prisma/client \
@prisma/adapter-pg \
@nestjs/common \
@nestjs/config \
pg
yarn add @rs-tech-hub/nestjs-prisma \
@rs-tech-hub/nestjs-service-operation \
@prisma/client \
@prisma/adapter-pg \
@nestjs/common \
@nestjs/config \
pg
pnpm add @rs-tech-hub/nestjs-prisma \
@rs-tech-hub/nestjs-service-operation \
@prisma/client \
@prisma/adapter-pg \
@nestjs/common \
@nestjs/config \
pg
Schema
generator client {
provider = "prisma-client-js"
output = "../generated/.prisma/client"
}
// Copy the generated client folder into the @rs-tech-hub/nestjs-prisma module
// -> src/lib/generated/client
datasource db {
provider = "postgresql"
}
๐ Module Registration
Basic Registration
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { PrismaModule } from "@rs-tech-hub/nestjs-prisma";
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
PrismaModule, // Global module - available everywhere
],
})
export class AppModule {}
Environment Configuration
Set your database connection string in .env:
DATABASE_URL="postgresql://user:password@localhost:port/mydb?schema=public"
๐ป Service Usage
Direct PrismaService Usage
import { Injectable } from "@nestjs/common";
import { PrismaService } from "@rs-tech-hub/nestjs-prisma";
@Injectable()
export class UserService {
constructor(private prisma: PrismaService) {}
async findAll() {
return this.prisma.user.findMany();
}
async findOne(id: string) {
return this.prisma.user.findUnique({
where: { id },
});
}
async create(data: { name: string; email: string }) {
return this.prisma.user.create({ data });
}
async update(id: string, data: { name?: string; email?: string }) {
return this.prisma.user.update({
where: { id },
data,
});
}
async delete(id: string) {
return this.prisma.user.delete({
where: { id },
});
}
}
Data Cleanup Helper
Remove Prisma internal fields (fields starting with _):
import { Injectable } from "@nestjs/common";
import { PrismaService } from "@rs-tech-hub/nestjs-prisma";
@Injectable()
export class PostService {
constructor(private prisma: PrismaService) {}
async getPostsWithCommentCount() {
const posts = await this.prisma.post.findMany({
include: {
_count: {
select: { comments: true },
},
},
});
// Clean up _count and other internal fields
return this.prisma.cleanupData(posts);
}
}
๐๏ธ Repository Pattern
Creating a Repository
Use the factory to create type-safe repositories:
import { Injectable } from "@nestjs/common";
import { PrismaService, createRepository } from "@rs-tech-hub/nestjs-prisma";
import { ServiceFacilitatorService } from "@rs-tech-hub/nestjs-service-operation";
import type { User } from "@prisma/client";
const UserRepositoryBase = createRepository<User, PrismaService["user"]>(
(prisma) => prisma.user,
"user"
);
@Injectable()
export class UserRepository extends UserRepositoryBase {
constructor(
prisma: PrismaService,
serviceFacilitator: ServiceFacilitatorService
) {
super(prisma, serviceFacilitator);
}
// Add custom methods here
async findByEmail(email: string) {
return this.findUnique({ email });
}
async findActiveUsers() {
return this.findMany({ isActive: true });
}
}
Repository Usage in Service
import { Injectable } from "@nestjs/common";
@Injectable()
export class UserService {
constructor(private userRepository: UserRepository) {}
async getUser(id: string) {
return this.userRepository.findUnique({ id });
}
async getUserByEmail(email: string) {
return this.userRepository.findByEmail(email);
}
async createUser(data: CreateUserDto) {
return this.userRepository.create(data);
}
async updateUser(id: string, data: UpdateUserDto) {
return this.userRepository.update({ id }, data);
}
async deleteUser(id: string) {
return this.userRepository.delete({ id });
}
}
๐ Repository API Reference
Find Methods
findMany(where?, options?)
Find multiple records with optional filtering and pagination.
const users = await repository.findMany(
{ isActive: true },
{
orderBy: { createdAt: "desc" },
take: 10,
skip: 0,
select: { id: true, name: true, email: true },
}
);
findUnique(where, options?)
Find a unique record by unique identifier.
const user = await repository.findUnique(
{ id: "user-123" },
{ include: { posts: true } }
);
findWithSelect(where, select)
Find with specific field selection (type-safe).
const user = await repository.findWithSelect(
{ email: "john@example.com" },
{ id: true, name: true, email: true }
);
// Returns: { id: string, name: string, email: string } | null
findManyWithSelect(where?, select?, options?)
Find multiple records with specific field selection.
const users = await repository.findManyWithSelect(
{ isActive: true },
{ id: true, name: true },
{ orderBy: { name: "asc" }, take: 5 }
);
findOneWithSelect(where?, select?)
Find first record matching criteria with field selection.
const user = await repository.findOneWithSelect(
{ email: "john@example.com" },
{ id: true, name: true }
);
findWithInclude(where?, include?)
Find with included relations.
const user = await repository.findWithInclude(
{ id: "user-123" },
{ posts: true, profile: true }
);
findManyWithInclude(where?, include?, options?)
Find multiple records with included relations.
const users = await repository.findManyWithInclude(
{ isActive: true },
{ posts: true },
{ orderBy: { createdAt: "desc" } }
);
findWithFields(where?, fields?)
Find with specific fields (simplified API).
const user = await repository.findWithFields({ id: "user-123" }, [
"id",
"name",
"email",
]);
findManyWithFields(where?, fields?)
Find multiple records with specific fields.
const users = await repository.findManyWithFields({ isActive: true }, [
"id",
"name",
]);
findUniqueWithSelect(where, select)
Find unique record with field selection.
const user = await repository.findUniqueWithSelect(
{ id: "user-123" },
{ id: true, name: true, email: true }
);
Create/Update Methods
create(data, select?)
Create a new record.
const user = await repository.create({
name: "John Doe",
email: "john@example.com",
});
update(where, data, select?)
Update an existing record.
const user = await repository.update({ id: "user-123" }, { name: "Jane Doe" });
upsert(where, create, update, select?)
Create if not exists, update if exists.
const user = await repository.upsert(
{ email: "john@example.com" },
{ name: "John Doe", email: "john@example.com" },
{ name: "John Updated" }
);
Delete Methods
delete(where)
Delete a single record.
const deleted = await repository.delete({ id: "user-123" });
deleteMany(where?)
Delete multiple records.
const result = await repository.deleteMany({ isActive: false });
// Returns: { count: number }
Utility Methods
count(where?)
Count records matching criteria.
const total = await repository.count({ isActive: true });
exists(where?)
Check if records exist.
const hasUsers = await repository.exists({ isActive: true });
Data Cleanup
cleanupData(data)
Remove internal Prisma fields (starting with _).
const cleanData = this.prisma.cleanupData(result);
๐ Advanced Usage
Transactions
import { Injectable } from "@nestjs/common";
import { PrismaService } from "@rs-tech-hub/nestjs-prisma";
@Injectable()
export class OrderService {
constructor(private prisma: PrismaService) {}
async createOrderWithItems(orderData: any, items: any[]) {
return this.prisma.$transaction(async (tx) => {
const order = await tx.order.create({ data: orderData });
const orderItems = await Promise.all(
items.map((item) =>
tx.orderItem.create({
data: { ...item, orderId: order.id },
})
)
);
return { order, orderItems };
});
}
}
Raw Queries
const users = await this.prisma.$queryRaw`
SELECT * FROM "User" WHERE "email" = ${email}
`;
Middleware
constructor(configService: ConfigService) {
super(/* ... */);
this.$use(async (params, next) => {
const before = Date.now();
const result = await next(params);
const after = Date.now();
console.log(`Query ${params.model}.${params.action} took ${after - before}ms`);
return result;
});
}
๐ก Best Practices
- Use Repository Pattern: Encapsulate data access logic in repositories
- Type Safety: Leverage TypeScript for compile-time type checking
- Connection Pooling: The service uses pg Pool for efficient connection management
- Transactions: Use
$transactionfor operations that must succeed or fail together - Select Fields: Use
selectto fetch only needed fields for better performance - Error Handling: Repository methods use ServiceFacilitator for consistent error handling
- Cleanup Data: Use
cleanupData()to remove Prisma internal fields from API responses
๐ License
This package is free and open source under the MIT License.
๐ Support
For issues, questions, or contributions, please visit the RS-Tech-Hub repository.