How to write a base service for your NestJS REST API using Typeorm Version 0.3+

andymwamengo

Andongwisye Mwamengo

Posted on May 10, 2023

How to write a base service for your NestJS REST API using Typeorm Version 0.3+

I have been looking for different ways of writing a base service in NestJS application for all shared database CRUD and others operations using Typeorm v0.3. I din't find any article relating to this case. Therefore I decided to write this blogpost.

Migration from Typeorm v0.2 to v0.3 has brought alot of changes when writting some of the database queries. Feel free to check the migrations guide.

We had a simple way of writing a base service for all CRUD operations in NestJS using typeorm in v0.2. Since typeorm introduced typeorm v0.3 there are couple of deprecations that happened such that you wont be able to use the approach from v0.2 of typeorm.

Today I'll be showing you and writing code on how to implement base service for all crud operations and other shared methods that you want to share across your application.

Let me start with the old way with typeorm v0.2

import { DeleteResult, Repository } from 'typeorm';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';
import { HttpException, HttpStatus } from '@nestjs/common';

export class BaseService<T> {
  constructor(private readonly repository: Repository<T>) {}

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

  async findOneById(id: FindOneOptions<T>): Promise<T> {
    return await this.repository.findOne({ where: { id } });
  }

  async create(data: DeepPartial<T> ) {
   return await this.repository.save(data);
  }

  async update(id: FindConditions<T>, partialEntity: QueryDeepPartialEntity<T>) {
   return await this.repository.update(id, partialEntity);
  }

  async delete(id: FindConditions<T>): Promise<DeleteResult> {
    return await this.repository.delete(id);
  }
}

Enter fullscreen mode Exit fullscreen mode

The below code show how to implement it with typeorm v0.3 using dataSource since the main changes we encounter is due to the fact that typeorm deprecated connection in v0.2 and added a dataSource to typeorm v0.3

This is the first way of writing a base service

import { DataSource, FindOneOptions, Repository } from 'typeorm';
import { EntityTarget } from 'typeorm/common/EntityTarget';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

export class GenericRepository<T> extends Repository<T> {
  constructor(target: EntityTarget<T>, dataSource: DataSource) {
    super(target, dataSource.createEntityManager());
  }

  async findActive(options: FindOneOptions<T>): Promise<T[]> {
    return await this.find(options);
  }

  async findByEmail(options: FindOneOptions<T>): Promise<T> {
    return this.findOne(options);
  }
}

Enter fullscreen mode Exit fullscreen mode

To use the above base service write the following code

import { Injectable } from '@nestjs/common';
import { DataSource, EntityTarget } from 'typeorm';
import { User } from './user.entity';
import { BaseService } from './base.service';

@Injectable()
export class UserService extends BaseService<User> {
  constructor(
    @InjectRepository(DataSource)
    private readonly dataSource: DataSource,
  ) {
    super(dataSource, User);
  }
}

Enter fullscreen mode Exit fullscreen mode

You can also decide to write the code in the following style also.

import { Repository } from 'typeorm';
import { EntityTarget } from 'typeorm/common/EntityTarget';
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity';

export abstract class BaseService<T> {
  protected constructor(protected readonly repository: Repository<T>) {}

  async create(entity: T): Promise<T> {
    return await this.repository.save(entity);
  }

  async findOne(options: FindOneOptions<T>): Promise<T> {
    return this.repository.findOne(options);
  }

  async findAll(): Promise<T[]> {
    return this.repository.find();
  }

  async update(id: number, options: FindOneOptions<T>, entity: T): Promise<T> {
    await this.repository.update(id, entity as QueryDeepPartialEntity<T>);
    return this.findOne(options);
  }

  async delete(id: number): Promise<void> {
    await this.repository.delete(id);
  }
}

Enter fullscreen mode Exit fullscreen mode

To use the above base service write the following code

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

@Injectable()
export class UserService extends BaseService<User> {
  constructor(
    @InjectRepository(User)
    private readonly userRepository: Repository<User>,
  ) {
    super(userRepository);
  }
}

Enter fullscreen mode Exit fullscreen mode

Feel free to checkout migrations from Typeorm version 0.2 to 0.3 via Migration guide from v0.2.x to v0.3.x · Issue.

If you have any issue or comment feel free to leave it in this post.

💖 💪 🙅 🚩
andymwamengo
Andongwisye Mwamengo

Posted on May 10, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related