Skip to main content

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>)
repo
Repository<T>
required
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[]>
req
CrudRequest
required
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>
req
CrudRequest
required
Request object with search criteria in parsed.search
return
Promise<T>
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>
req
CrudRequest
required
Request object with configuration options
dto
T | Partial<T>
required
Entity data to create
return
Promise<T>
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": "john@example.com" }
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[]>
req
CrudRequest
required
Request object
dto
CreateManyDto<T | Partial<T>>
required
Object with bulk array containing entities to create
return
Promise<T[]>
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": "john@example.com" },
//     { "name": "Jane", "email": "jane@example.com" }
//   ]
// }
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>
req
CrudRequest
required
Request object with entity identifier
dto
T | Partial<T>
required
Partial entity data to update
return
Promise<T>
The updated entity
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>
req
CrudRequest
required
Request object with entity identifier
dto
T | Partial<T>
required
Complete entity data
return
Promise<T>
The replaced entity
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": "new@example.com", "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>
req
CrudRequest
required
Request object with entity identifier
return
Promise<void | T>
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>
req
CrudRequest
required
Request object with entity identifier
return
Promise<T>
The recovered entity
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
many
boolean
default:"true"
Whether to apply pagination and sorting (true for getMany, false for getOne)
withDeleted
boolean
default:"false"
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
return
ObjectLiteral
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:
const user = await this.service.findOne({ where: { email: 'test@example.com' } });

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":"john@example.com"}]}

# 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