TypeOrmCrudService
The TypeOrmCrudService is a concrete implementation of the CrudService abstract class that provides full CRUD functionality using TypeORM. This is the primary service class you’ll use with TypeORM repositories.
Overview
This service provides:
- Complete CRUD operations implementation
- Advanced query building with TypeORM
- Automatic relation handling
- Search and filter support
- Pagination and sorting
- SQL injection protection
- Soft delete support
Constructor
constructor(protected repo: Repository<T>)
TypeORM repository instance for the entity
Example:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService extends TypeOrmCrudService<User> {
constructor(@InjectRepository(User) repo) {
super(repo);
}
}
CRUD Methods
getMany()
Retrieves multiple entities with advanced query support.
async getMany(req: CrudRequest): Promise<GetManyDefaultResponse<T> | T[]>
The CRUD request containing:
parsed: Parsed query parameters (filter, sort, pagination, joins)
options: CRUD configuration options
return
Promise<GetManyDefaultResponse<T> | T[]>
Returns:
- Paginated response with metadata if pagination is applied
- Simple array of entities otherwise
Features:
- Automatic query builder creation
- Field selection
- Filtering and search
- Relation joins
- Sorting
- Pagination
- Caching support
Example:
// Controller endpoint
@Get()
async getUsers(@ParsedRequest() req: CrudRequest) {
return this.service.getMany(req);
}
// Request: GET /users?filter=age||$gt||18&sort=name,ASC&limit=10&page=1
// Returns:
// {
// data: [...users...],
// count: 10,
// total: 156,
// page: 1,
// pageCount: 16
// }
getOne()
Retrieves a single entity by ID or search criteria.
async getOne(req: CrudRequest): Promise<T>
Request object with search criteria in parsed.search
The found entity or throws NotFoundException
Example:
@Get(':id')
async getUser(@ParsedRequest() req: CrudRequest) {
return this.service.getOne(req);
}
// Request: GET /users/123?join=profile&join=posts
// Returns the user with profile and posts relations loaded
createOne()
Creates a single entity.
async createOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>
Request object with configuration options
The created entity (shallow or fully loaded based on returnShallow option)
Example:
@Post()
async createUser(
@ParsedRequest() req: CrudRequest,
@ParsedBody() dto: CreateUserDto,
) {
return this.service.createOne(req, dto);
}
// Request: POST /users
// Body: { "name": "John", "email": "[email protected]" }
The service automatically applies parameter filters and auth persist data from the request.
createMany()
Creates multiple entities in bulk.
async createMany(req: CrudRequest, dto: CreateManyDto<T | Partial<T>>): Promise<T[]>
dto
CreateManyDto<T | Partial<T>>
required
Object with bulk array containing entities to create
Array of created entities
Example:
@Post('bulk')
async createUsers(
@ParsedRequest() req: CrudRequest,
@ParsedBody() dto: CreateManyDto<User>,
) {
return this.service.createMany(req, dto);
}
// Request: POST /users/bulk
// Body: {
// "bulk": [
// { "name": "John", "email": "[email protected]" },
// { "name": "Jane", "email": "[email protected]" }
// ]
// }
Entities are saved in chunks of 50 for optimal performance.
updateOne()
Partially updates an existing entity.
async updateOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>
Request object with entity identifier
Partial entity data to update
Example:
@Patch(':id')
async updateUser(
@ParsedRequest() req: CrudRequest,
@ParsedBody() dto: UpdateUserDto,
) {
return this.service.updateOne(req, dto);
}
// Request: PATCH /users/123
// Body: { "name": "John Updated" }
// Only updates the name field, other fields remain unchanged
Behavior:
- Fetches the existing entity
- Merges with new data
- Applies parameter filters if
allowParamsOverride is false
- Returns shallow or fully loaded entity based on
returnShallow option
replaceOne()
Fully replaces an entity.
async replaceOne(req: CrudRequest, dto: T | Partial<T>): Promise<T>
Request object with entity identifier
Example:
@Put(':id')
async replaceUser(
@ParsedRequest() req: CrudRequest,
@ParsedBody() dto: User,
) {
return this.service.replaceOne(req, dto);
}
// Request: PUT /users/123
// Body: { "name": "John", "email": "[email protected]", "age": 30 }
// Replaces the entire entity with new data
If the entity doesn’t exist, it will be created with the provided data.
deleteOne()
Deletes a single entity (soft or hard delete).
async deleteOne(req: CrudRequest): Promise<void | T>
Request object with entity identifier
Void or the deleted entity if returnDeleted option is enabled
Example:
@Delete(':id')
async deleteUser(@ParsedRequest() req: CrudRequest) {
return this.service.deleteOne(req);
}
// Request: DELETE /users/123
Behavior:
- Uses soft delete if
options.query.softDelete is true
- Uses hard delete (permanent) otherwise
- Can return deleted entity based on configuration
recoverOne()
Recovers a soft-deleted entity.
async recoverOne(req: CrudRequest): Promise<T>
Request object with entity identifier
Example:
@Patch(':id/recover')
async recoverUser(@ParsedRequest() req: CrudRequest) {
return this.service.recoverOne(req);
}
// Request: PATCH /users/123/recover
// Restores a soft-deleted user
Only works with entities that have a delete date column (e.g., @DeleteDateColumn()).
Query Builder Methods
createBuilder()
Creates a TypeORM SelectQueryBuilder with all query parameters applied.
async createBuilder(
parsed: ParsedRequestParams,
options: CrudRequestOptions,
many = true,
withDeleted = false,
): Promise<SelectQueryBuilder<T>>
parsed
ParsedRequestParams
required
Parsed request parameters (filters, joins, sort, pagination)
options
CrudRequestOptions
required
CRUD configuration options
Whether to apply pagination and sorting (true for getMany, false for getOne)
Include soft-deleted entities
return
Promise<SelectQueryBuilder<T>>
Configured TypeORM query builder
Example:
class CustomUserService extends TypeOrmCrudService<User> {
async getActiveUsers(req: CrudRequest): Promise<User[]> {
const builder = await this.createBuilder(req.parsed, req.options);
builder.andWhere('user.isActive = :active', { active: true });
return builder.getMany();
}
}
doGetMany()
Executes the query builder and returns results with or without pagination.
protected async doGetMany(
builder: SelectQueryBuilder<T>,
query: ParsedRequestParams,
options: CrudRequestOptions,
): Promise<GetManyDefaultResponse<T> | T[]>
builder
SelectQueryBuilder<T>
required
Configured query builder
query
ParsedRequestParams
required
Parsed query parameters
options
CrudRequestOptions
required
CRUD options
return
Promise<GetManyDefaultResponse<T> | T[]>
Paginated response or array of entities
Example:
class CustomUserService extends TypeOrmCrudService<User> {
async getMany(req: CrudRequest): Promise<GetManyDefaultResponse<User> | User[]> {
const { parsed, options } = req;
const builder = await this.createBuilder(parsed, options);
// Add custom filtering
builder.andWhere('user.verified = :verified', { verified: true });
return this.doGetMany(builder, parsed, options);
}
}
Helper Methods
getParamFilters()
Extracts parameter filters from parsed request.
getParamFilters(parsed: CrudRequest['parsed']): ObjectLiteral
parsed
CrudRequest['parsed']
required
Parsed request data
Object with filter field-value pairs
Example:
const filters = this.getParamFilters(req.parsed);
// Returns: { organizationId: 123 } for route /organizations/:organizationId/users
Repository Accessors
The service provides direct access to TypeORM repository methods:
findOne
public get findOne(): Repository<T>['findOne']
Access TypeORM’s findOne method directly.
Example:
find
public get find(): Repository<T>['find']
Access TypeORM’s find method directly.
Example:
const users = await this.service.find({ where: { isActive: true } });
count
public get count(): Repository<T>['count']
Access TypeORM’s count method directly.
Example:
const totalUsers = await this.service.count({ where: { role: 'admin' } });
Advanced Features
SQL Injection Protection
The service includes built-in SQL injection protection:
protected sqlInjectionRegEx: RegExp[] = [
/(%27)|(\')|(--)|((%23)|(#)/gi,
/((%3D)|(=))[^\n]*((%27)|(\')|(--)|((%3B)|(;))/gi,
/w*((%27)|(\''))((%6F)|o|(%4F))((%72)|r|(%52))/gi,
/((%27)|(\''))union/gi,
]
All field names in queries are automatically checked against these patterns.
Soft Delete Support
If your entity has a @DeleteDateColumn(), the service automatically:
- Filters out soft-deleted records by default
- Supports
includeDeleted query parameter
- Provides
deleteOne() for soft deletion
- Provides
recoverOne() for recovery
Example Entity:
import { Entity, Column, DeleteDateColumn } from 'typeorm';
@Entity()
export class User {
@Column()
name: string;
@DeleteDateColumn()
deletedAt: Date;
}
Usage:
// Soft delete
await this.service.deleteOne(req); // Sets deletedAt
// Recover
await this.service.recoverOne(req); // Clears deletedAt
// Include deleted in query
// GET /users?includeDeleted=1
Relation Handling
The service automatically handles entity relations:
// Request: GET /users?join=profile&join=posts.comments
// Automatically loads nested relations
Relation Configuration:
@Crud({
model: { type: User },
query: {
join: {
profile: { eager: true },
posts: { allow: ['title', 'content'] },
'posts.comments': {},
},
},
})
export class UserController {
constructor(public service: UserService) {}
}
Caching
Enable query result caching:
@Crud({
model: { type: User },
query: {
cache: 2000, // Cache for 2 seconds
},
})
export class UserController {
constructor(public service: UserService) {}
}
// Disable cache per request
// GET /users?cache=0
Complete Usage Example
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column()
email: string;
@Column()
age: number;
@DeleteDateColumn()
deletedAt: Date;
}
// user.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { User } from './user.entity';
@Injectable()
export class UserService extends TypeOrmCrudService<User> {
constructor(@InjectRepository(User) repo) {
super(repo);
}
// Optional: Add custom methods
async findByEmail(email: string): Promise<User> {
const user = await this.findOne({ where: { email } });
if (!user) {
this.throwNotFoundException('User');
}
return user;
}
}
// user.controller.ts
import { Controller } from '@nestjs/common';
import { Crud, CrudController } from '@nestjsx/crud';
import { User } from './user.entity';
import { UserService } from './user.service';
@Crud({
model: { type: User },
query: {
softDelete: true,
limit: 10,
maxLimit: 100,
cache: 2000,
},
routes: {
exclude: [],
},
})
@Controller('users')
export class UserController implements CrudController<User> {
constructor(public service: UserService) {}
}
Query Examples
# Get all users with pagination
GET /users?limit=10&page=1
# Filter users by age
GET /users?filter=age||$gt||18&filter=age||$lt||65
# Search with multiple conditions
GET /users?s={"$or":[{"name":"John"},{"email":"[email protected]"}]}
# Sort by multiple fields
GET /users?sort=age,DESC&sort=name,ASC
# Select specific fields
GET /users?fields=name,email
# Join relations
GET /users?join=profile&join=posts
# Include soft-deleted records
GET /users?includeDeleted=1
# Complex query
GET /users?filter=age||$gte||18&sort=name,ASC&limit=20&join=profile&fields=name,email