DEV Community

loading...

Simple example custom repository with NestJS 7.x and TypeORM 😻

tony133 profile image Tony Updated on ・5 min read

In this post I will give you a simple example of a custom repository in NestJS😻 with TypeORM.
For those unfamiliar with or unfamiliar with NestJS, 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.

$ npm i -g @nestjs/cli
Enter fullscreen mode Exit fullscreen mode

Then create a NestJS project

$ nest new app
$ cd app
// start the application
$ npm run start: dev
Enter fullscreen mode Exit fullscreen mode

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

version: "3"

services:
   mysql:
     image: mysql:5
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: root
       MYSQL_DATABASE: nest
     ports:
       - "3306:3306"
Enter fullscreen mode Exit fullscreen mode

for those who do not know what docker is I leave the link here for more information Docker

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

$ npm install --save @nestjs/typeorm typeorm mysql
Enter fullscreen mode Exit fullscreen mode

Set TypeOrmModule in AppModule

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

@Module ({
   imports: [
     TypeOrmModule.forRoot(),
   ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

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

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

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:

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

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

     @Column()
     name: string;

     @Column()
     description: string;

     @Column()
     price: string;
}
Enter fullscreen mode Exit fullscreen mode

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

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;
}
Enter fullscreen mode Exit fullscreen mode

Remember to install this package before creating the dto class for the upgrade.

$ npm i @nestjs/mapped-types
Enter fullscreen mode Exit fullscreen mode

Well, now to update the customer data we extend the CreateProductDto class:

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

export class UpdateProductDto extends PartialType(CreateProductDto) {}
Enter fullscreen mode Exit fullscreen mode

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

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();
Enter fullscreen mode Exit fullscreen mode

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

$ nest g module products
$ nest g service products
$ nest g controller products
Enter fullscreen mode Exit fullscreen mode

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:

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

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

Enter fullscreen mode Exit fullscreen mode

Our ProductsModule file should look like this:

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 {}

Enter fullscreen mode Exit fullscreen mode

ProductsService:

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);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now let's edit our custom repository like this:

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';

@Injectable()
@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);
    } 
}


Enter fullscreen mode Exit fullscreen mode

ProductsController:

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);
    }
}

Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

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

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

Discussion (2)

pic
Editor guide
Collapse
mahdipishguy profile image
Mahdi Pishguy

thanks so much, can we use TypeOrm with mongoDb?

Collapse
tony133 profile image
Tony Author

yes, you can use typeorm with mongodb here is an example base, without custom repositoy: github.com/nestjs/nest/tree/master...