Simple example custom repository with NestJS 7.x/8.x and TypeORM 😻
Tags: nestjs, typescript, backend, database
Published: 2020-12-29 11:06:08

In this post I will give you a simple example of a custom repository in [NestJS](https://nestjs.com/)😻 with [TypeORM](https://typeorm.io/#/).
For those unfamiliar with or unfamiliar with [NestJS](https://nestjs.com/), it's a Node.js TypeScript framework that helps you build efficient and scalable enterprise-grade Node.js applications.

So let's get started by creating the NestJS app
Open Terminal and install CLI for NestJS, if you already have it installed, skip this step.

```sh
$ npm i -g @nestjs/cli
```
### Then create a NestJS project

```sh
$ nest new app
$ cd app
// start the application
$ npm run start: dev
```

Open the browser on `localhost:3000` to verify that hello world is displayed.

then we create a docker-compose.yml file to create the service
MySQL

```yml
version: "3"

services:
   mysql:
     image: mysql:5
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: root
       MYSQL_DATABASE: nest
     ports:
       - "3306:3306"
```

for those who do not know what docker is I leave the link here for more information [Docker](https://www.docker.com/get-started).

As I mentioned in the previous post, there are many different ways to integrate Nest with databases and they all depend on personal preferences or the needs of the project.

### Install TypeORM and MySQL dependencies

```sh
$ npm install --save @nestjs/typeorm typeorm@0.2 mysql
```

Set TypeOrmModule in AppModule

```typescript
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

@Module ({
   imports: [
     TypeOrmModule.forRoot(),
   ],
})
export class AppModule {}
```

create an ormconfig.json file in the project root directory.

```json
{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "root",
  "database": "nest",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true,
  "autoLoadEntities": true
}
```

If you have trouble setting up TypeOrmModule here, make sure Docker is running with `docker compose up`.

Also make sure the database name inside your `TypeOrmModule.forRoot` matches the one you have in your docker-compose file.

Well now let's create our entity we call it `products.entity.ts`:

```typescript
import { PrimaryGeneratedColumn, Column, Entity } from 'typeorm';

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

     @Column()
     name: string;

     @Column()
     description: string;

     @Column()
     price: string;
}
```

Create Data Transfer Objects (Dto) class to create the product:

```typescript
import { MaxLength, IsNotEmpty, IsString } from 'class-validator';

export class CreateProductDto {
    @IsString()
    @IsNotEmpty()
    @MaxLength(20)
    name: string;

    @IsString()
    @IsNotEmpty()
    @MaxLength(100)
    description: string;

    @IsString()
    @IsNotEmpty()
    price: string;
}
```
Remember to install this package before creating the dto class for the upgrade.
```sh
$ npm i @nestjs/mapped-types
```
Well, now to update the customer data we extend the CreateProductDto class:

```typescript
import { PartialType } from '@nestjs/mapped-types';
import { CreateProductDto } from './create-product.dto';

export class UpdateProductDto extends PartialType(CreateProductDto) {}
```

we call validation pipe in the main.ts file as follows:

```typescript
import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe({
      whitelist: true,
      transform: true,
      forbidNonWhitelisted: true,
      transformOptions: {
        enableImplicitConversion: true,
      },
    }),
  );
  await app.listen(3000);
}
bootstrap();
```

Well, now we're going to create a simple service, controller, and product module

```sh
$ nest g module products
$ nest g service products
$ nest g controller products
```

You should now have a customer folder with ProductsModule, ProductsService, and ProductsController inside.

Now let's create a products.repository.ts file to create a custom repository that extends the TypeORM base repository, like this:

```typescript
import { Repository, EntityRepository } from 'typeorm';
import { Products } from './products.entity';

@EntityRepository(Products)
export class ProductsRepository extends Repository<Products> {}

```

Our ProductsModule file should look like this:

```typescript
import { Module } from '@nestjs/common';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProductsRepository } from './products.repository';

@Module({
  imports: [TypeOrmModule.forFeature([ProductsRepository])],
  controllers: [ProductsController],
  providers: [ProductsService],
})

export class ProductsModule {}

```

ProductsService:

```typescript
import { HttpException, HttpStatus, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Products } from './entities/products.entity';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { ProductsRepository } from './repositories/products.repository';

@Injectable()
export class ProductsService {
    constructor(
        @InjectRepository(ProductsRepository)
        private productsRepository: ProductsRepository,
    ) {}

    public async findAll(): Promise<Products[]> {
        return await this.productsRepository.findAll();
    }

    public async findOne(productId: number): Promise<Products> {
        const product = await this.productsRepository.findById(productId);
        if (!product) {
            throw new NotFoundException(`Product #${productId} not found`);
        }
        return product;
    }

    public async create(
        createProductDto: CreateProductDto,
    ): Promise<Products> {
        try {
          return await this.productsRepository.createProduct(createProductDto);
        } catch (err) {
          throw new HttpException(err, HttpStatus.BAD_REQUEST);
        }
    }

    public async update(
        productId: number,
        updateProductDto: UpdateProductDto,
    ): Promise<Products> {
        const product = await this.productsRepository.findOne(productId);
        if (!product) {
            throw new NotFoundException(`Product #${productId} not found`);
        }
        return this.productsRepository.editProduct(productId, updateProductDto);
    }

    public async remove(productId: number): Promise<void> {
        await this.productsRepository.delete(productId);
    }
}
```

Now let's edit our custom repository like this:

```typescript
import { Repository, EntityRepository } from 'typeorm';
import { Products } from '../entities/products.entity';
import { CreateProductDto } from '../dto/create-product.dto';
import { UpdateProductDto } from '../dto/update-product.dto';
import { Injectable } from '@nestjs/common';

@EntityRepository(Products)
export class ProductsRepository extends Repository<Products> {

    public async findAll(): Promise<Products[]> {
        return await this.find({});
    } 

    public async findById(productId: number): Promise<Products> {
        return await this.findOne(productId);
    }

    public async createProduct(
        createProductDto: CreateProductDto,
    ): Promise<Products> {
        const { name, description, price } = createProductDto;
        const product = new Products();
        product.name = name;
        product.description = description;
        product.price = price;

        await this.save(product);
        return product;
    }

    public async editProduct(
        productId: number,
        updateProductDto: UpdateProductDto,
    ): Promise<Products> {
        const { name, description, price } = updateProductDto;
        const product = await this.findOne(productId);
        product.name = name;
        product.description = description;
        product.price = price;
        await this.save(product);

        return product;
    }

    public async destroy(productId: number): Promise<void> {
        const product = await this.findOne(productId);
        await this.remove(product);
    } 
}


```

ProductsController:

```typescript
import {
    Controller,
    Post,
    Body,
    Get,
    Patch,
    Param,
    Delete,
} from '@nestjs/common';
import { ProductsService } from './products.service';
import { CreateProductDto } from './dto/create-product.dto';
import { UpdateProductDto } from './dto/update-product.dto';
import { Products } from './entities/products.entity';

@Controller('/api/products')
export class ProductsController {
    constructor(private productsService: ProductsService) { }

    @Get()
    public async findAll(): Promise<Products[]> {
        return await this.productsService.findAll();
    }

    @Get('/:productId')
    public async findOne(@Param('productId') productId: number): Promise<Products> {
        return await this.productsService.findOne(productId);
    }

    @Post()
    public async create(
        @Body() createProductsDto: CreateProductDto,
    ): Promise<Products> {
        return await this.productsService.create(createProductsDto);
    }

    @Patch('/:productId')
    public async update(
        @Body() updateProductDto: UpdateProductDto,
        @Param('productId') productId: number,
    ): Promise<Products> {
        const product = await this.productsService.update(
            productId,
            updateProductDto,
        );
        return product;
    }

    @Delete('/:productId')
    public async delete(@Param('productId') productId: number): Promise<void> {
        const product = await this.findOne(productId);
        if (!product) {
            throw new NotFoundException(`Product #${product} not found`);
        }

        return await this.productsService.remove(productId);
    }
}

```

Getting with Curl Products:

```
    $ curl -H 'content-type: application/json' -v -X GET http://127.0.0.1:3000/api/products  
    $ curl -H 'content-type: application/json' -v -X GET http://127.0.0.1:3000/api/products/:id 
    $ curl -H 'content-type: application/json' -v -X POST -d '{"name": "Product #1","description": "Lorem ipsum", "price": "19.99"}' http://127.0.0.1:3000/api/products 
    $ curl -H 'content-type: application/json' -v -X PUT -d '{"name": "Product #1","description": "Lorem ipsum", "price": "19.99"}' http://127.0.0.1:3000/api/products/:id 
    $ curl -H 'content-type: application/json' -v -X DELETE http://127.0.0.1:3000/api/products/:id 
```


I hope it will be useful in building your applications with NestJS 😻

That's it 😀 
For anything write me in the comments 😉