image
author

William Dawson

Full Stack Developer

In this article, we will be using TypeORM with Nest Js to integrate database with our application. But before starting with TypeORM, let’s have a brief look over the concept of Object-relational mapping(ORM).

Object-relational mapping as a technique for converting data between incompatible type systems using object-oriented programming languages. In other words, ORM is a programming technique in which a metadata descriptor is used to connect object code to a relational database.

Source Wikipedia

Object code is written in object-oriented programming (OOP) languages such as C++, JAVA, etc. We will be using TypeScript for creations of our object-oriented programming.

In addition to the data access technique, ORM also provide
simplified development because it automates object-to-table and table-to-object conversion, resulting in lower development and maintenance costs.

Now, when we have a good idea about what is the notion of ORM is, let’s understand what TypeORM is :-

TypeORM: TypeORM is an ORM that can run in NodeJS, Browser, Cordova, PhoneGap, Ionic, React Native, NativeScript, Expo, and Electron platforms and can be used with TypeScript and JavaScript (ES5, ES6, ES7, ES8).

Topics:

  1. Creating a model( or Table ).
  2. Primary / Auto-generation column.
  3. Relationship between two or more models.
  4. Our Project.

Creating a model/ Table

The first step in the database is to create a table. With TypeORM, we create database tables through models. So models in our app will be our database tables.

Now create a sample model “Cat” for a better understanding.


export class Cat {
    id: number;
    name: string;
    breed: string;
    age: string;
}

Note: The database table is not created for each model but only for those models which are declared as entities. To declare a model as an entity, we just need to add @Entity() decorator before the declaration of the Class defining our model.

In addition to this, we should ideally have columns in our model now because the table which will be generated (because of the model being declared as an entity now) makes no sense without any column in it. To add a data member of a model as a column, we need to decorate a data member with a @Column() decorator.

Let us modify our above model of ‘Cats’ by adding ‘@Entity()’ and ‘@Column()’ decorator.


@Entity()
export class Cat {

    @Column()
    id: number;

    @Column()
    name: string;

    @Column()
    breed: string;
    
    @Column()
    age: string;

}

Primary / auto-generated primary column

For creating a column as a primary key of the database table, we need to use @PrimaryColumn() decorator instead of @Column() decorator. And for the primary column to be self-generated, we need to use @PrimaryGeneratedColumn() instead of @PrimaryColumn().

By making ‘id’ in ‘Cat’ as auto-generated primary key, our Cat model will look like this:


@Entity()
export class Cat {

    @PrimaryGeneratedColumn()
    id: number;

    @Column()
    name: string;

    @Column()
    breed: string;
    
    @Column()
    age: string;

}

Relationship between two or more models

A relationship, in the context of databases, is a situation that exists between two relational database tables when one table has a foreign key that references the primary key of the other table. Relationships allow relational databases to split and store data in different tables while linking disparate data items.

There are 3 types of relationships in relational database design :-

  • One-to-One (implemented by @OneToOne() decorator)
  • One-to-Many / Many-to-One (implemented by @OneToMany() decorator )
  • Many-to-Many (implemented by @ManyToMany() decorator)
One-to-One
One-to-Many
Many-to-Many

Our Project

In this section, we will create a NestJS project in which we will have three tables/entities as follows:

  • UserEntity
  • BookEntity
  • GenreEntity

Relationships between the entities:

  • UserEntity and BookEntity: One-To-Many
  • BookEntity and GenreEntity: Many-To-Many

In simple words, a user can have many books and each book can belong to more than one Genre.

For now, we will create the above-mentioned entities as follows without any relationship between them as follows:

MyProject/db/user.entity.ts


import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, OneToMany } from 'typeorm';
import BookEntity from './book.entity';
@Entity()
export default class UserEntity extends BaseEntity {

  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 500 })
  name: string;
}

MyProject/db/book.entity.ts


import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, ManyToOne, ManyToMany, JoinTable } from 'typeorm';
import UserEntity from './user.entity';
import GenreEntity from './genre.entity';

@Entity()
export default class BookEntity extends BaseEntity 
{
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ length: 500 })
  name: string;
}

MyProject/db/genre.entity.ts


@Entity()
export default class GenreEntity extends BaseEntity {

  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  type: string;

}

For setting up the relation between UserEntity and BookEntity, we have to add the following code in UserEntity and BookEntity Class as follows:

MyProject/db/user.entity.ts


// 1:n relation with bookEntity 
  @OneToMany( type => BookEntity , book => book.user)
  books: BookEntity[];

type => BookEntity is a function that returns the class of the entity with which we want to make our relationship.

book => book.user states which column to be used by ‘BookEntity’ to get the associated user.

Now, we have set a One-to-Many relationship from the ‘UserEntity’ side. As One-to-Many is complimentary with Many-to-One, we should state the ‘Many-to-One’ relationship from BookEntity to UserEntity.

MyProject/db/book.entity.ts


// n:1 relation with books
  @ManyToOne(type => UserEntity, user => user.books)
  user: UserEntity;

Similarly, to make a many-to-many relationship between BookEntity and GenreEntity, we have to add the following code:


// n:n relation with genre
  @ManyToMany(type => GenreEntity)
  @JoinTable()
  genres: GenreEntity[];

Here, ‘@JoinTable()’ decorator states that in a many-to-many relationship in BookEntity and GenreEntity, the ownership lies in the BookEntity side.

Now we are done with almost everything related to database and TypeORM. The only thing that remains is to establish a connection with the database. For this purpose, we have to create an ‘ormconfig.json’ file and add the following JSON code in it.

MyProject/ormconfig.json


{
  "type": "sqlite",
  "database": "./database.sqlite",
  "synchronize": "true",
  "entities": [
    "dist/db/entity/**/*.js"
  ],
  "cli": {
    "entitiesDir": "src/db/entity"
  }
}

The first line in the above JSON object specifies that the database we are using is ‘SQLite’.

Now we have to create the NEST controllers and services to handle the requests.

Here are the three DataTransferObjects we will be using in the further code:

MyProject/User/dto/create-user.dto.ts


export default class CreateUserDto {
  readonly name: string;
  readonly books: number[] ;
}

MyProject/User/dto/create-book.dto.ts


export default class CreateBookDto {
  readonly name: string;
  readonly userID: number;
  readonly genreIDs: number[];
}

MyProject/User/dto/create-genre.dto.ts


export default class CreateGenreDto {
  readonly type: string;
}

Below are the Controllers and Services for Users, Books, and Genres which will be handling the requests.

Users

MyProject/User/user.controller.ts


import { Body, Controller, Get, ParseIntPipe, Post, Put } from '@nestjs/common';
import { UserServices } from './user.services';
import CreateUserDto from './dto/create-user.dto';

@Controller('users')
export class UserController {
  constructor(private readonly usersServices: UserServices) {}

//'postUser()' will handle the creating of new User
  @Post('post')
  postUser( @Body() user: CreateUserDto) {
    return this.usersServices.insert(user);
  }
// 'getAll()' returns the list of all the existing users in the database
  @Get()
  getAll() {
    return this.usersServices.getAllUsers();
  }

//'getBooks()' return all the books which are associated with the user 
// provided through 'userID' by the request  
  @Get('books')
  getBooks( @Body('userID', ParseIntPipe) userID: number ) {
    return this.usersServices.getBooksOfUser(userID);
  }
}

Myproject/User/user.service.ts


import { Injectable } from '@nestjs/common';
import UserEntity from '../db/entity/user.entity';
import CreateUserDto from './dto/create-user.dto';
import BookEntity from '../db/entity/book.entity';
import {getConnection} from "typeorm";

@Injectable()
export class UserServices {

  async insert(userDetails: CreateUserDto): Promise<UserEntity> {
    const userEntity: UserEntity = UserEntity.create();
    const {name } = userDetails;
    userEntity.name = name;
    await UserEntity.save(userEntity);
    return userEntity;
  }
  async getAllUsers(): Promise<UserEntity[]> {
    return await UserEntity.find();
  }
  async getBooksOfUser(userID: number): Promise<BookEntity[]> {
    console.log(typeof(userID));
    const user: UserEntity = await UserEntity.findOne({where: {id: userID}, relations: ['books']});
    return user.books;
  }
}

Myproject/User/user.module.ts


import { Module } from '@nestjs/common';
import { UserServices } from './user.services';
import { UserController } from './user.controller';
@Module({
  imports: [],
  controllers: [UserController],
  providers: [UserServices],
})
export class UserModule {}

Genre

MyProject/Genre/genre.controller.ts


import { Body, Controller, Get, Post } from '@nestjs/common';
import GenreServices from './genre.services';
import CreateGenreDto from './dto/create-genre.dto';

@Controller('genre')
export default class GenreController {
  constructor(private readonly genreServices: GenreServices) {}
  @Post('post')
  postGenre( @Body() genre: CreateGenreDto) {
    return this.genreServices.insert(genre);
  }
  @Get()
  getAll() {
    return this.genreServices.getAllGenre();
  }
}

MyProject/Genre/genre.services.ts


import { Injectable } from '@nestjs/common';
import CreateGenreDto from './dto/create-genre.dto';
import GenreEntity from '../db/entity/genre.entity';

@Injectable()
export default class GenreServices {
    async insert(genreDetails: CreateGenreDto): Promise<GenreEntity> {

    const genreEntity: GenreEntity = GenreEntity.create();
    const {type} = genreDetails;

    genreEntity.type = type;
    await GenreEntity.save(genreEntity);
    return genreEntity;
  }
  async getAllGenre(): Promise<GenreEntity[]> {
        return await GenreEntity.find();
  }
}

MyProject/Genre/genre.module.ts


import { Module } from '@nestjs/common';
import GenreServices from './genre.services';
import GenreController from './genre.controller';
@Module({
  imports: [],
  controllers: [GenreController],
  providers: [GenreServices],
})
export default class GenreModule {}

Books


MyProject/Books/books.controller.ts

import BookEntity from '../db/entity/book.entity';
import CreateBookDto from './dto/create-book.dto';
import UserEntity from '../db/entity/user.entity';
import { createQueryBuilder, getConnection } from 'typeorm';
import GenreEntity from '../db/entity/genre.entity';

export class BooksService {

  async insert(bookDetails: CreateBookDto): Promise<BookEntity> {
    const { name , userID , genreIDs } = bookDetails;
    const book = new BookEntity();
    book.name = name;
    book.user = await UserEntity.findOne(userID) ;
    book.genres=[];
    for ( let i = 0; i < genreIDs.length ; i++)
    {
             const genre = await GenreEntity.findOne(genreIDs[i]);
             book.genres.push(genre);
    }
    await book.save();
    return book;
  }
  async getAllBooks(): Promise<BookEntity[] > {
    // const user: UserEntity = await UserEntity.findOne({where: {id: 2}, relations: ['books']});
    return BookEntity.find();
  }

MyProject/Books/books.module.ts


import { Module } from '@nestjs/common';
import { BooksService } from './books.service';
import BooksController from './books.controller';
@Module({
  imports: [],
  controllers: [BooksController],
  providers: [BooksService],
})
export default class BooksModule {}

Now, finally, this is the time to integrate everything with ‘app.module.ts’


import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './User/user.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import UserEntity from './db/entity/user.entity';
import BooksModule from './Books/books.module';
import GenreModule from './Genre/genre.module';
import BookEntity from './db/entity/book.entity';
import GenreEntity from './db/entity/genre.entity';

@Module({
  imports: [UserModule ,
            BooksModule,
            GenreModule,
    TypeOrmModule.forFeature(
      [UserEntity, BookEntity , GenreEntity],
    ),

    TypeOrmModule.forRoot(),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

What is ORM and TypeORM?

TypeORM has orignated from ORM. ORM is a programming technique for creating a ‘virtual object database’ that can be used within the programming languages. TypeORM is an ORM which can be used with the environments like nodeJS and the programs can be implemented using JavaScript or TypeScript.

How do I set up One-To-Many / Many-To-One relationship in TypeORM?

To set a relation in which each record of ‘User’ can have many records of ‘Posts’ associated with it on a social networking platform, add a column ‘posts’ in ‘User’ entity as:
‘@OneToMany(type => Post, post => post.user) posts: Post[];’
and a corresponding column in ‘Post’ entity as:
‘@OneToMany(type => User, user => user.posts) user: User;’

How to implement Eager and Lazy Relationship with TypeORM?

For two entites, ‘Question’ and ‘Category’ with Many-To-Many relationship from Question to Category, Eager Relationship between the two entites allows to load question with its categories to be loaded automatically with the find* method.
To add Eager relationship in the above entities, add the the following code in Question entity:

@ManyToMany(type => Category, category => category.questions, { eager: true }) @JoinTable()
categories: Category[];

Note: Here, ‘questions’ is the column in ‘Category’ entity. Also, {eager:true} can only be used at one side of the relationship and not on both sides.

How to implement Lazy Relationship with TypeORM?

For two entites, ‘Question’ and ‘Category’ with Many-To-Many relationship from Question to Category, Lazy Relationship between the two entites allows to load question only without fetching the associated categories as per the many-to-many relation between Question and Category
To add Lazy relationship in the above entities, add the the following code:

In Question entity:
@ManyToMany(type => Category, category => category.questions)
@JoinTable()
categories: Promise;

In Category entity:
@ManyToMany(type => Question, question => question.categories)
questions: Promise;

Note: ‘categories’ is a Promise. It means it is lazy and it can store only a promise with a value inside.

How useful was this post?

How useful was this post?

Click on a star to rate it!

Average rating 4.8 / 5. Vote count: 12

No votes so far! Be the first to rate this post.

Please do Rate Us and Share!

Related Blogs

  • author
    Adam Davidson

    Best Android Emulator for PC

    Android emulator for PC or MACs is one of the best for gamers to focus and improve their gaming skills.There are many reasons for emulating Android on your Windows PC, because with the help of emulation it is much easier to test apps on-screen or desktop than a mobile device. Android developers can debug...

  • author
    Adam Davidson

    Companies that Use Node JS in Production

    Despite of being arrived late on the scene, NodeJS is dominating the entire application development scenario with its great optimal features. This is a well-kept secret for the seamless distribution of their services that some of the top companies that use NodeJS for its web-based applications today. From concurrence to being a lightweight runtime,...

  • author
    Lucas White

    How to Use callBack With setState in React

    Today we are going to explore the callback function in setState and get to know about how we can use it.  First of all, I’d like to explain the ‘callBack’ and ‘setState’. callBack functions is a function that is passed as an argument to another function, to be “called back” at a later time....

image

About The Author

William is a CTO and a full-stack engineer with 10 years of experience. He has spent the past seven years doing web and mobile apps. He’s good at designing architecture and implementing agile development process. The technologies he’s worked with include: Node.js, Elixir, Rails, AngularJS, React, React Native, Objective-C, iOS, Java, Android. He’s also familiar with C++, Haskell, C#/.NET. He is an enthusiastic programmer and a great guy to know

Try our One-Week Risk Free Trial for Hiring a Coder

Know more Hire a Coder