Opinionated backend development standards for Node.js + Express + TypeScript microservices. Covers layered architecture, BaseController pattern, dependency injection, Prisma repositories, Zod validation, unifiedConfig, Sentry error tracking, async safety, and testing discipline.
71
71%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Complete guide to database access patterns using Prisma in backend microservices.
import { PrismaService } from '@project-lifecycle-portal/database';
// Always use PrismaService.main
const users = await PrismaService.main.user.findMany();if (!PrismaService.isAvailable) {
throw new Error('Prisma client not initialized');
}
const user = await PrismaService.main.user.findUnique({ where: { id } });✅ Use repositories when:
❌ Skip repositories for:
export class UserRepository {
async findById(id: string): Promise<User | null> {
return PrismaService.main.user.findUnique({
where: { id },
include: { profile: true },
});
}
async findActive(): Promise<User[]> {
return PrismaService.main.user.findMany({
where: { isActive: true },
orderBy: { createdAt: 'desc' },
});
}
async create(data: Prisma.UserCreateInput): Promise<User> {
return PrismaService.main.user.create({ data });
}
}const result = await PrismaService.main.$transaction(async (tx) => {
const user = await tx.user.create({ data: userData });
const profile = await tx.userProfile.create({ data: { userId: user.id } });
return { user, profile };
});const result = await PrismaService.main.$transaction(
async (tx) => {
const user = await tx.user.findUnique({ where: { id } });
if (!user) throw new Error('User not found');
return await tx.user.update({
where: { id },
data: { lastLogin: new Date() },
});
},
{
maxWait: 5000,
timeout: 10000,
}
);// ❌ Fetches all fields
const users = await PrismaService.main.user.findMany();
// ✅ Only fetch needed fields
const users = await PrismaService.main.user.findMany({
select: {
id: true,
email: true,
profile: { select: { firstName: true, lastName: true } },
},
});// ❌ Excessive includes
const user = await PrismaService.main.user.findUnique({
where: { id },
include: {
profile: true,
posts: { include: { comments: true } },
workflows: { include: { steps: { include: { actions: true } } } },
},
});
// ✅ Only include what you need
const user = await PrismaService.main.user.findUnique({
where: { id },
include: { profile: true },
});// ❌ N+1 Query Problem
const users = await PrismaService.main.user.findMany(); // 1 query
for (const user of users) {
// N queries (one per user)
const profile = await PrismaService.main.userProfile.findUnique({
where: { userId: user.id },
});
}// ✅ Single query with include
const users = await PrismaService.main.user.findMany({
include: { profile: true },
});
// ✅ Or batch query
const userIds = users.map(u => u.id);
const profiles = await PrismaService.main.userProfile.findMany({
where: { userId: { in: userIds } },
});import { Prisma } from '@prisma/client';
try {
await PrismaService.main.user.create({ data });
} catch (error) {
if (error instanceof Prisma.PrismaClientKnownRequestError) {
// Unique constraint violation
if (error.code === 'P2002') {
throw new ConflictError('Email already exists');
}
// Foreign key constraint
if (error.code === 'P2003') {
throw new ValidationError('Invalid reference');
}
// Record not found
if (error.code === 'P2025') {
throw new NotFoundError('Record not found');
}
}
// Unknown error
Sentry.captureException(error);
throw error;
}Related Files: