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
Then create a NestJS project
$ 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
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
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
Set TypeOrmModule in AppModule
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.
{
"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:
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
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.
$ npm i @nestjs/mapped-types
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) {}
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();
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
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> {}
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 {}
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);
}
}
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);
}
}
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);
}
}
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 😉
Discussion (2)
thanks so much, can we use TypeOrm with mongoDb?
yes, you can use typeorm with mongodb here is an example base, without custom repositoy: github.com/nestjs/nest/tree/master...