Using TypeORM in a NestJS Project

Introduction

TypeORM is a powerful ORM (Object-Relational Mapper) library for the Node.js ecosystem, written in TypeScript. It allows developers to interact with databases using classes and objects instead of writing complex raw SQL queries.

Key Advantages

  • Excellent TypeScript support: Fully leverages decorators and type-checking, helping detect errors during development instead of at runtime.
  • Supports multiple databases: Compatible with popular database management systems such as MySQL, PostgreSQL, SQLite, etc.
  • Flexible patterns (Data Mapper & Active Record): Lets you choose the appropriate implementation style depending on project size (Data Mapper for large, complex projects; Active Record for smaller, quick projects).
  • Migration management: Provides powerful tools to manage database schema changes systematically, helping synchronize changes across development teams.
  • Query Builder: In addition to object-based operations, TypeORM provides a Query Builder for constructing complex queries while maintaining clean and readable syntax.


How to Use

In this article, I will guide you on how to use TypeORM in a NestJS project with a PostgreSQL database, along with data validation.

After creating your NestJS project, install the required packages:

yarn add typeorm @nestjs/typeorm class-transformer class-validator pg


Create test.entity.ts

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

@Entity({name: 'test'})
export class TestEntity {
  @PrimaryGeneratedColumn()
  id: number

  @Column({length: 100, unique: true})
  name: string

  @Column({nullable: true})
  description: string

  @Column({default: false})
  checked: boolean
}

Each field in this entity corresponds to a column that will be created in the database.


Create create-test.dto.ts

import {IsBoolean, IsNotEmpty, IsOptional, IsString} from 'class-validator'

export class CreateTest {
  @IsString()
  @IsNotEmpty()
  name: string

  @IsString()
  @IsOptional()
  description?: string

  @IsBoolean()
  @IsOptional()
  checked?: boolean
}

  • You can easily understand the purpose of each decorator from its name.
  • class-validator also provides many other useful decorators, such as email validation, string length validation, min/max number checks, etc.


Create test-entity.service.ts

import {Injectable} from '@nestjs/common'
import {InjectRepository} from '@nestjs/typeorm'
import {CreateTest} from 'src/dto/create-test.dto'
import {TestEntity} from 'src/entity/test.entity'
import {Repository} from 'typeorm'

@Injectable()
export class TestEntityService {
  constructor(
    @InjectRepository(TestEntity)
    private testRepository: Repository<TestEntity>
  ) {}

  getAll() {
    return this.testRepository.find()
  }

  create(testEntity: CreateTest) {
    return this.testRepository.save(testEntity)
  }
}

This service provides two simple functions:

  • Retrieve all records from the test table.
  • Create a new test entity.


Create test-entity.controller.ts

import {Body, Controller, Get, Post} from '@nestjs/common'
import {CreateTest} from 'src/dto/create-test.dto'
import {TestEntityService} from 'src/service/test-entity.service'

@Controller('test-entity')
export class TestEntityController {
  constructor(private testEntityService: TestEntityService) {}

  @Get('gets')
  getHealth() {
    return this.testEntityService.getAll()
  }

  @Post('create')
  create(@Body() payload: CreateTest) {
    return this.testEntityService.create(payload)
  }
}


To validate the request payload, update main.ts to use ValidationPipe:

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule)
  app.useGlobalPipes(
    new ValidationPipe({
      transform: true,
    })
  )
  await app.listen(process.env.PORT ?? 3000)
}
bootstrap()


Connect to PostgreSQL and add Service and Controller to app.module.ts

import {Module} from '@nestjs/common'
import {TypeOrmModule} from '@nestjs/typeorm'
import {TestEntity} from './entity/test.entity'
import {TestEntityController} from './controller/test-entity.controller'
import {TestEntityService} from './service/test-entity.service'

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'postgres',
      host: '127.0.0.1',
      port: 2345,
      username: '<username>',
      password: '<password>',
      database: 'test',
      autoLoadEntities: true,
      synchronize: true,
      logging: ['error', 'warn'],
    }),
    TypeOrmModule.forFeature([TestEntity]),
  ],
  controllers: [TestEntityController],
  providers: [TestEntityService],
})
export class AppModule {}

  • If you haven't installed Postgres yet, you can refer to this article which provides instructions on how to start Postgres using Docker.
  • Regarding the TypeOrmModule configuration, please keep the following points in mind:
    • schema: I haven't defined it here, so it will use the public schema by default. If you are using a different schema, you need to provide the correct schema name.
    • synchronize: Setting this to true will automatically sync your entities from the source code to the database. This should only be used in development environments and never in production.


When calling the create API with an incorrect payload data type, validation errors will be returned as expected.


Other APIs should function normally.


Happy coding!

See more articles here.

Comments

Popular posts from this blog

All practice series

Deploying a NodeJS Server on Google Kubernetes Engine

Setting up Kubernetes Dashboard with Kind

Using Kafka with Docker and NodeJS

Monitoring with cAdvisor, Prometheus and Grafana on Docker

Kubernetes Practice Series

Kubernetes Deployment for Zero Downtime

Practicing with Google Cloud Platform - Google Kubernetes Engine to deploy nginx

NodeJS Practice Series

Helm for beginer - Deploy nginx to Google Kubernetes Engine