npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@thephilosoph/nestjs-crud-base

v0.1.1

Published

A powerful and flexible CRUD library for NestJS with built-in pagination, filtering, serialization, soft delete, and file upload support

Readme

@thephilosoph/nestjs-crud-base

A powerful and flexible CRUD library for NestJS that provides base classes for rapid REST API development with TypeORM. Built-in support for pagination, filtering, serialization, soft delete, and file uploads.

npm version License: MIT

Features

Base Controller & Service - Extend pre-built classes for instant CRUD operations
📄 Pagination - Built-in pagination with customizable page size and sorting
🔍 Filtering - Query parameter-based filtering
🎯 Serialization - Response DTOs with class-transformer groups
🗑️ Soft Delete - Optional soft delete support
📁 File Uploads - Built-in file upload handling
🔄 Transactions - Transaction support out of the box
📚 Swagger Integration - Automatic API documentation

Installation

npm install @thephilosoph/nestjs-crud-base

Peer Dependencies

This library requires the following peer dependencies:

npm install @nestjs/common @nestjs/core @nestjs/typeorm @nestjs/swagger \
  typeorm class-transformer class-validator reflect-metadata rxjs

Quick Start

1. Create Your Entity

import { Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;

  @DeleteDateColumn()
  deletedAt: Date;
}

2. Create DTOs

import { IsString, IsEmail } from 'class-validator';
import { Expose } from 'class-transformer';

export class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

export class UpdateUserDto {
  @IsString()
  name?: string;
}

export class UserResponseDto {
  @Expose()
  id: number;

  @Expose()
  name: string;

  @Expose()
  email: string;
}

3. Create Service

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { BaseService } from '@nestjs-crud/base';
import { User } from './user.entity';

@Injectable()
export class UserService extends BaseService<User> {
  constructor(
    @InjectRepository(User)
    public repository: Repository<User>
  ) {
    super();
  }
}

4. Create Controller

import { Controller } from '@nestjs/common';
import { BaseController } from '@thephilosoph/nestjs-crud-base';
import { User } from './user.entity';
import { UserService } from './user.service';
import { CreateUserDto, UpdateUserDto, UserResponseDto } from './user.dto';

const { Controller: CrudController, Base } = BaseController<User, UserResponseDto>('User', {
  CreateDto: CreateUserDto,
  UpdateDto: UpdateUserDto,
  ResponseDto: UserResponseDto,
  softDelete: true,
});

@Controller('users')
export class UserController extends Base {
  constructor(service: UserService) {
    super(service);
  }
}

5. Register in Module

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

API Endpoints

The BaseController automatically provides the following endpoints:

| Method | Endpoint | Description | |--------|----------|-------------| | POST | /users | Create a new user | | GET | /users | Get all users (paginated) | | GET | /users/:id | Get user by ID | | PATCH | /users/:id | Update user | | DELETE | /users/:id | Delete user (soft delete if enabled) |

Configuration Options

CrudOptions Interface

The CrudOptions interface provides comprehensive configuration for your CRUD controllers. All options are optional unless specified.

interface CrudOptions<T, TResponseDto> {
  CreateDto?: Type<any>;
  UpdateDto?: Type<any>;
  ResponseDto?: ClassConstructor<TResponseDto>;
  allowedRelationsFindOne?: (keyof T & string)[];
  allowedRelationsFindAll?: (keyof T & string)[];
  guards?: CrudGuards;
  createInterceptor?: any;
  createFileField?: string;
  serializationGroups?: string[];
  softDelete?: boolean;
}

Option Details

CreateDto

  • Type: Type<any>
  • Required: Recommended
  • Description: DTO class for validating create requests
  • Example:
class CreateUserDto {
  @IsString()
  name: string;

  @IsEmail()
  email: string;
}

// Usage in controller
const { Base } = BaseController<User, UserResponseDto>('User', {
  CreateDto: CreateUserDto,
  // ...
});

UpdateDto

  • Type: Type<any>
  • Required: Recommended
  • Description: DTO class for validating update requests. Typically has optional fields.
  • Example:
class UpdateUserDto {
  @IsString()
  @IsOptional()
  name?: string;

  @IsEmail()
  @IsOptional()
  email?: string;
}

ResponseDto

  • Type: ClassConstructor<TResponseDto>
  • Required: Recommended
  • Description: DTO class for serializing responses. Uses class-transformer's @Expose() decorator.
  • Example:
class UserResponseDto {
  @Expose()
  id: number;

  @Expose()
  name: string;

  @Expose()
  email: string;

  // This field won't be exposed in responses
  password: string;
}

serializationGroups

  • Type: string[]
  • Default: undefined
  • Description: class-transformer groups for selective field exposure. Allows different fields for different endpoints.
  • Example:
class UserResponseDto {
  @Expose()
  id: number;

  @Expose()
  name: string;

  @Expose({ groups: ['detail'] })
  email: string;  // Only shown when 'detail' group is active

  @Expose({ groups: ['admin'] })
  role: string;   // Only shown when 'admin' group is active
}

// Controller configuration
const { Base } = BaseController<User, UserResponseDto>('User', {
  ResponseDto: UserResponseDto,
  serializationGroups: ['detail'], // Default groups for all endpoints
});

softDelete

  • Type: boolean
  • Default: false
  • Description: Enables soft delete (sets deletedAt timestamp instead of removing from database). Requires @DeleteDateColumn() in your entity.
  • Example:
// Entity with soft delete support
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @DeleteDateColumn()
  deletedAt: Date;  // Required for soft delete
}

// Controller configuration
const { Base } = BaseController<User, UserResponseDto>('User', {
  softDelete: true,  // DELETE requests will soft delete
});

allowedRelationsFindOne

  • Type: (keyof T & string)[]
  • Default: []
  • Description: Array of relation names that can be included when fetching a single item via query parameter.
  • Example:
// Entity with relations
@Entity()
class User {
  @PrimaryGeneratedColumn()
  id: number;

  @OneToOne(() => Profile)
  profile: Profile;

  @OneToMany(() => Post, post => post.user)
  posts: Post[];
}

// Controller configuration
const { Base } = BaseController<User, UserResponseDto>('User', {
  allowedRelationsFindOne: ['profile', 'posts'],
});

// Usage: GET /users/1?include=profile,posts

allowedRelationsFindAll

  • Type: (keyof T & string)[]
  • Default: []
  • Description: Array of relation names that can be included when fetching multiple items.
  • Example:
const { Base } = BaseController<User, UserResponseDto>('User', {
  allowedRelationsFindAll: ['profile'],  // Only allow profile, not posts
});

// Usage: GET /users?include=profile

guards

  • Type: CrudGuards
  • Default: undefined
  • Description: Apply guards to specific CRUD endpoints.
  • Example:
interface CrudGuards {
  create?: MethodDecorator[];
  update?: MethodDecorator[];
  delete?: MethodDecorator[];
  findOne?: MethodDecorator[];
  findAll?: MethodDecorator[];
}

// Usage
const { Base } = BaseController<User, UserResponseDto>('User', {
  guards: {
    create: [UseGuards(AuthGuard)],
    update: [UseGuards(AuthGuard, OwnershipGuard)],
    delete: [UseGuards(AuthGuard, AdminGuard)],
  },
});

createInterceptor

  • Type: any
  • Default: undefined
  • Description: Custom interceptor for the create endpoint.
  • Example:
const { Base } = BaseController<User, UserResponseDto>('User', {
  createInterceptor: UseInterceptors(TransformInterceptor),
});

createFileField

  • Type: string
  • Default: undefined
  • Description: Field name for file uploads in create requests. Enables file upload handling.
  • Example:
const { Base } = BaseController<User, UserResponseDto>('User', {
  createFileField: 'avatar',  // Expects multipart/form-data with 'avatar' field
});

// Override handleFiles in service to process uploads
class UserService extends BaseService<User> {
  protected async handleFiles(entity: User, files: Express.Multer.File[]): Promise<void> {
    if (files.length > 0) {
      entity.avatarPath = files[0].path;
      await this.repository.save(entity);
    }
  }
}

Complete Example with All Options

const { Controller: CrudController, Base } = BaseController<User, UserResponseDto>('User', {
  // DTOs
  CreateDto: CreateUserDto,
  UpdateDto: UpdateUserDto,
  ResponseDto: UserResponseDto,
  
  // Serialization
  serializationGroups: ['detail'],
  
  // Relations
  allowedRelationsFindOne: ['profile', 'posts', 'comments'],
  allowedRelationsFindAll: ['profile'],
  
  // Soft Delete
  softDelete: true,
  
  // Guards
  guards: {
    create: [UseGuards(AuthGuard)],
    update: [UseGuards(AuthGuard, OwnershipGuard)],
    delete: [UseGuards(AuthGuard, AdminGuard)],
  },
  
  // File Upload
  createFileField: 'avatar',
  createInterceptor: UseInterceptors(FileInterceptor('avatar')),
});

@Controller('users')
export class UserController extends Base {
  constructor(service: UserService) {
    super(service);
  }
}

Advanced Usage

Pagination

GET /users?page=1&limit=10&sort=name:ASC

Query parameters:

  • page - Page number (default: 1)
  • limit - Items per page (default: 10)
  • sort - Sort format: field:ASC or field:DESC

Filtering

GET /users?name=John&[email protected]

All query parameters (except page, limit, sort) are treated as filters.

Serialization Groups

Use class-transformer groups to control which fields are exposed:

export class UserResponseDto {
  @Expose()
  id: number;

  @Expose()
  name: string;

  @Expose({ groups: ['detail'] })
  email: string;  // Only shown when 'detail' group is active
}

File Uploads

Override the handleFiles method in your service:

@Injectable()
export class UserService extends BaseService<User> {
  constructor(
    @InjectRepository(User)
    public repository: Repository<User>
  ) {
    super();
  }

  protected async handleFiles(entity: User, files: Express.Multer.File[]): Promise<void> {
    // Handle file upload logic here
    if (files.length > 0) {
      entity.avatarPath = files[0].path;
      await this.repository.save(entity);
    }
  }
}

Transactions

Use the built-in transaction support:

await this.userService.transactional(async (manager) => {
  const user = await this.userService.create(createUserDto, manager);
  // Other operations within the same transaction
  return user;
});

Custom Methods

Extend the base controller with custom endpoints:

@Controller('users')
export class UserController extends Base {
  constructor(private readonly userService: UserService) {
    super(userService);
  }

  @Get('search')
  async search(@Query('q') query: string) {
    // Custom search logic
    return this.userService.findAll({
      where: { name: Like(`%${query}%`) }
    });
  }
}

API Response Format

All responses follow a consistent format:

Success Response (Single Item)

{
  "success": true,
  "message": "User retrieved successfully",
  "data": {
    "id": 1,
    "name": "John Doe",
    "email": "[email protected]"
  }
}

Success Response (Paginated)

{
  "success": true,
  "message": "Users retrieved successfully",
  "data": {
    "data": [...],
    "meta": {
      "total": 100,
      "page": 1,
      "limit": 10,
      "totalPages": 10
    }
  }
}

Requirements

  • NestJS 11.x
  • TypeORM 0.3.x
  • Node.js 18+

License

MIT © 2025

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

If you encounter any issues or have questions, please file an issue on the GitHub repository.