Back to list
allenlin90

service-pattern-nestjs

by allenlin90

1🍴 0📅 Jan 24, 2026

SKILL.md


name: service-pattern-nestjs description: Provides NestJS-specific service implementation patterns. This skill should be used when implementing Model Services, Orchestration Services, or business logic with NestJS decorators.

Service Pattern - NestJS Implementation

Implementation guide for NestJS Services in Eridu.

For core database concepts (Transactions, Bulk Ops), see Database Patterns. For general service architecture, see Service Pattern.

Model Service Structure

Extend BaseModelService<T> for standard CRUD.

import { Injectable } from '@nestjs/common';
import { BaseModelService } from '@/lib/services/base-model.service';
import { UtilityService } from '@/utility/utility.service';

@Injectable()
export class UserService extends BaseModelService {
  // UID_PREFIX has NO trailing underscore (e.g., 'user', not 'user_')
  static readonly UID_PREFIX = 'user';
  protected readonly uidPrefix = UserService.UID_PREFIX;

  constructor(
    private readonly userRepository: UserRepository,
    utilityService: UtilityService,
  ) {
    super(utilityService);
  }
}

CRUD Operations

Implement business logic here.

// Create with ID generation
async createUser(data: CreateUserDto): Promise<User> {
  return this.userRepository.create({
    uid: this.generateUid(), // Helper from BaseModelService
    email: data.email,
    name: data.name,
  });
}

// Read with verification
async getUserById(uid: string): Promise<User> {
  const user = await this.userRepository.findByUid(uid);
  if (!user) throw HttpError.notFound('User', uid);
  return user;
}

// Update (See "Verify Before Modify" pattern)
async updateUser(uid: string, data: UpdateUserDto): Promise<User> {
  await this.getUserById(uid); // Ensure exists
  return this.userRepository.update({ uid }, data);
}

Error Handling

Use HttpError utility, NEVER NestJS exceptions directly. This ensures consistent error responses and logging.

import { HttpError } from '@/common/errors/http-error.util';

// 404 Not Found
if (!user) throw HttpError.notFound('User', uid);

// 400 Bad Request
if (invalid) throw HttpError.badRequest('Invalid state');

// 409 Conflict
if (exists) throw HttpError.conflict('User already exists');

// 403 Forbidden
if (!allowed) throw HttpError.forbidden('Access denied');

Orchestration Services

Coordinate multiple services/repositories using Transactions. See Database Patterns for transaction rules.

// Example: Creating a Show implies creating Assignments
async createShowWithAssignments(data: CreateShowDto) {
  return this.prismaService.$transaction(async (tx) => {
    // 1. Create Parent
    const show = await this.showService.createShow({ ...data, tx });
    
    // 2. Create Children
    await this.assignmentService.createAssignments(show.id, data.assignments, tx);
    
    return show;
  });
}

Verify Before Modify Pattern

Always check existence before mutating.

async deleteUser(uid: string): Promise<void> {
  // 1. Verify existence (throws 404 if missing)
  await this.getUserById(uid);

  // 2. Perform operation
  await this.userRepository.softDelete({ uid });
}

Pagination

Execute Count and Data queries in parallel.

async listUsers(params: PaginationParams) {
  const [data, total] = await Promise.all([
    this.userRepository.findMany({ ...params }),
    this.userRepository.count({ ...params }),
  ]);
  return { data, total };
}

Advanced Search and Filtering

For services that require complex search logic (e.g. partial matches, date ranges, soft-delete filtering), see the Admin List Pattern.

Bulk Operations

Use Repository bulk methods, DO NOT loop in Service.

async createManyUsers(users: CreateUserDto[]) {
  // Map DTOs to internal structure (e.g. add UIDs)
  const data = users.map(u => ({
    ...u,
    uid: this.generateUid()
  }));
  
  // Single DB Call
  return this.userRepository.createMany(data);
}

Best Practices Checklist

  • Extend BaseModelService
  • Define UID_PREFIX static constant
  • Inject UtilityService
  • Use this.generateUid()
  • Verify resource exists before Update/Delete
  • Use HttpError for all exceptions
  • Use Promise.all for independent async tasks
  • Use PrismaService.$transaction for multi-step workflows
  • Never throw NotFoundException directly (use HttpError.notFound)

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon