BaseService
The BaseService is an abstract class that provides a standardized service layer implementation with business logic validation and error handling. It acts as an intermediary between controllers and repositories.
Class Definition
abstract class BaseService<T, TCreateInput, TUpdateInput> {
constructor(
protected readonly repository: IRepository<T, TCreateInput, TUpdateInput>
)
async findAll(options?: any): Promise<T[]>
async findById(id: string): Promise<T>
async create(data: TCreateInput): Promise<T>
async update(id: string, data: TUpdateInput): Promise<T>
async delete(id: string): Promise<boolean>
async count(options?: any): Promise<number>
async findByIds(ids: string[]): Promise<T[]>
async findFirst(where: any, include?: any): Promise<T>
async findMany(options: any): Promise<T[]>
async findWithPagination(options: any): Promise<PaginatedResult<T>>
async search(searchTerm: string, searchFields: string[], include?: any): Promise<T[]>
async exists(query: any): Promise<boolean>
}
Constructor
constructor(repository: IRepository<T, TCreateInput, TUpdateInput>)
Creates a new service instance with the provided repository.
Parameters:
repository: IRepository<T, TCreateInput, TUpdateInput>- Repository instance for data access
Example:
import { BaseService } from '@mifty/core/base';
import { UserRepository } from './user.repository';
export class UserService extends BaseService<User, CreateUserDto, UpdateUserDto> {
constructor(userRepository: UserRepository) {
super(userRepository);
}
}
Methods
findAll(options?: any): Promise<T[]>
Retrieves all records with optional filtering and inclusion options.
Parameters:
options?: any- Query options (where, include, orderBy, etc.)
Returns: Promise<T[]>
Example:
const users = await userService.findAll({
where: { active: true },
include: { profile: true },
orderBy: { createdAt: 'desc' }
});
findById(id: string): Promise<T>
Retrieves a single record by ID. Throws NotFoundException if not found.
Parameters:
id: string- Record identifier
Returns: Promise<T>
Throws: NotFoundException if record doesn't exist
Example:
try {
const user = await userService.findById('123');
console.log(user);
} catch (error) {
// Handle NotFoundException
}
create(data: TCreateInput): Promise<T>
Creates a new record with the provided data.
Parameters:
data: TCreateInput- Data for creating the record
Returns: Promise<T>
Example:
const newUser = await userService.create({
name: 'John Doe',
email: 'john@example.com',
profile: {
firstName: 'John',
lastName: 'Doe'
}
});
update(id: string, data: TUpdateInput): Promise<T>
Updates an existing record. Throws NotFoundException if record doesn't exist.
Parameters:
id: string- Record identifierdata: TUpdateInput- Update data
Returns: Promise<T>
Throws: NotFoundException if record doesn't exist
Example:
const updatedUser = await userService.update('123', {
name: 'Jane Doe',
email: 'jane@example.com'
});
delete(id: string): Promise<boolean>
Deletes a record by ID. Throws NotFoundException if record doesn't exist.
Parameters:
id: string- Record identifier
Returns: Promise<boolean> - Always returns true on success
Throws: NotFoundException if record doesn't exist
Example:
const deleted = await userService.delete('123');
console.log(deleted); // true
count(options?: any): Promise<number>
Returns the count of records matching the provided criteria.
Parameters:
options?: any- Query options (where clause, etc.)
Returns: Promise<number>
Example:
const activeUserCount = await userService.count({
where: { active: true }
});
findByIds(ids: string[]): Promise<T[]>
Retrieves multiple records by their IDs.
Parameters:
ids: string[]- Array of record identifiers
Returns: Promise<T[]>
Example:
const users = await userService.findByIds(['123', '456', '789']);
findFirst(where: any, include?: any): Promise<T>
Finds the first record matching the criteria. Throws NotFoundException if no match found.
Parameters:
where: any- Query criteria (required)include?: any- Related data to include
Returns: Promise<T>
Throws:
ValidationExceptionif where clause is missingNotFoundExceptionif no matching record found
Example:
const user = await userService.findFirst(
{ email: 'john@example.com' },
{ profile: true }
);
findMany(options: any): Promise<T[]>
Retrieves multiple records with query options.
Parameters:
options: any- Query options (where, include, orderBy, pagination, etc.)
Returns: Promise<T[]>
Example:
const users = await userService.findMany({
where: { active: true },
include: { profile: true },
orderBy: { createdAt: 'desc' },
take: 10
});
findWithPagination(options: any): Promise<PaginatedResult<T>>
Retrieves paginated results with metadata.
Parameters:
options: any- Query options including pagination parameters
Returns: Promise<PaginatedResult<T>>
Example:
const result = await userService.findWithPagination({
page: 1,
pageSize: 10,
where: { active: true },
include: { profile: true },
orderBy: { createdAt: 'desc' }
});
console.log(result.data); // Array of users
console.log(result.total); // Total count
console.log(result.totalPages); // Total pages
search(searchTerm: string, searchFields: string[], include?: any): Promise<T[]>
Performs text search across specified fields.
Parameters:
searchTerm: string- Text to search forsearchFields: string[]- Fields to search ininclude?: any- Related data to include
Returns: Promise<T[]>
Throws: ValidationException if parameters are invalid
Example:
const users = await userService.search(
'john',
['name', 'email', 'profile.firstName'],
{ profile: true }
);
exists(query: any): Promise<boolean>
Checks if a record exists matching the query criteria.
Parameters:
query: any- Query criteria
Returns: Promise<boolean>
Throws: ValidationException if query is invalid
Example:
const emailExists = await userService.exists({
email: 'john@example.com'
});
Implementation Example
import { BaseService } from '@mifty/core/base';
import { UserRepository } from './user.repository';
import { User, CreateUserDto, UpdateUserDto } from './user.types';
import { ConflictException } from '@mifty/core/exceptions';
export class UserService extends BaseService<User, CreateUserDto, UpdateUserDto> {
constructor(private userRepository: UserRepository) {
super(userRepository);
}
// Override create to add business logic
async create(data: CreateUserDto): Promise<User> {
// Check if email already exists
const emailExists = await this.exists({ email: data.email });
if (emailExists) {
throw new ConflictException('Email already exists');
}
// Add additional business logic
const userData = {
...data,
active: true,
createdAt: new Date()
};
return super.create(userData);
}
// Add custom business methods
async activateUser(id: string): Promise<User> {
return this.update(id, { active: true });
}
async deactivateUser(id: string): Promise<User> {
return this.update(id, { active: false });
}
async findActiveUsers(): Promise<User[]> {
return this.findMany({
where: { active: true },
include: { profile: true },
orderBy: { createdAt: 'desc' }
});
}
async searchActiveUsers(searchTerm: string): Promise<User[]> {
const users = await this.search(
searchTerm,
['name', 'email', 'profile.firstName', 'profile.lastName'],
{ profile: true }
);
// Filter to only active users
return users.filter(user => user.active);
}
}
Error Handling
The BaseService automatically handles and throws appropriate exceptions:
- NotFoundException - When records are not found
- ValidationException - When input validation fails
- ConflictException - When business rules are violated (custom implementation)
Best Practices
- Override methods to add business logic validation
- Add custom methods for specific business operations
- Use transactions for complex operations involving multiple entities
- Validate business rules before calling repository methods
- Handle errors gracefully and provide meaningful error messages