DEV Community

Cover image for An overview of TypeORM
Mohammad Abdul Alim
Mohammad Abdul Alim

Posted on

An overview of TypeORM

One of the most popular Object Relational Mapping or ORM is TypeORM. At first lets talk a bit about ORM. As the name suggests, ORM is a programming technique that relates or maps object oriented model to relational database model. In a nutshell, you can craete or query data from a relational database using object oriented way with ORM. Almost all of the object oriented programming languages support ORM and their frameworks use ORM e.g. django in python, laravel in php, nestjs in node etc. ORM helps us in many ways. It helps us by revoking the direct use of writing sql queries.

Some of the main benefits of using ORM includes:

  • It speeds up the development by eliminating repetitive usage of SQL code
  • It reduces development time a lot
  • It overcomes all the vendor spicific SQL differences because it knows how to convert to vendor SQL code
  • It can be used with both relational databases like mysql, oracle, postgresql, maria db and nosql like mongodb
  • It abstracts things like caching and indexing
  • It can catch general issues like input validations

Now lets go to our main topic TypeORM. The official documentation says:

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).

Features

Lets find out some basic features of typeORM:

  • entities and columns
  • database-specific column types
  • entity manager
  • repositories and custom repositories
  • clean object relational model
  • associations (relations)
  • eager and lazy relations
  • uni-directional, bi-directional and self-referenced relations
  • supports multiple inheritance patterns
  • cascades
  • indices
  • transactions
  • migrations and automatic migrations generation
  • connection pooling
  • replication
  • using multiple database connections

More features can be found in the official documentation

Installation

We can install TypeORM from npm registry very easily with the following command

npm i -g typeorm
Enter fullscreen mode Exit fullscreen mode

Here i is the alias for install and -g means we are installing it globally in our machine. In order to use it in a project we need to omit -g. In addition we can install two optional modules named reflect-metadata and @types/node as follows

npm i reflect-metadata @types/node 
Enter fullscreen mode Exit fullscreen mode

Now according to the database you would like connect, just install its module. Suppose I would like to connect to mysql db so I will do the following

npm i mysql
Enter fullscreen mode Exit fullscreen mode

Connection

In order to interact to a database we need to create connection of TypeORM with that particular database. This connection can be created in several ways. One way to achieve this is using createConnection method. Let us suppose that we would like to connect to a mysql db which can be achieved in the following way

import { createConnection } from "typeorm";

const connection = await createConnection({
    type: "mysql",
    host: "localhost",
    port: 3306,
    username: "admin",
    password: "12345",
    database: "blog-world"
});
Enter fullscreen mode Exit fullscreen mode

We can connect with multiple databases using createConnections method which takes an array of object as input where we can use our different databases.

Another way of connecting to a database is to use ormconfig.json file where all those object key value prperties will be stored as json format and exported when needed. I personally prefer this way.

Entity

Entity is a class which will represent a table of our database or document in case of mongodb. This means all the properties of an entity get converted to columns of a table. Entity supports all basic types like number, string, boolean, array and even an object of other entity. Lets build a writer entity for our blog-world database.

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

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

    @Column()  
    name: string;

    @Column() 
    phone: string;

    @Column() 
    isFeatured: boolean;
}
Enter fullscreen mode Exit fullscreen mode

Here each property tagged with @Column represents a column in db table. There can be different types of columns based on its nature. A model must have a Primary Column which will represent that row. If it is auto incremented then @PrimaryGeneratedColumn needs to be used. We can also use @PrimaryGeneratedColumn("uuid") to generate unique id everytime. In that case type of id will be string instead of number. There can be other columns like @CreateDateColumn which automatically sets entity's insertion date. Based on different databases Spatial Columns can be used too. We can use enum as type of a column. Let us add a column named gender to our above entity.

export enum Gender {
    MALE = "male",
    FEMALE = "female",
    OTHER = "other" 
}

@Entity 
export class Writer {
    // other properties will remain same

    @Column({
        type: "enum",
        enum: Gender
    })
    gender: Gender;
}
Enter fullscreen mode Exit fullscreen mode

Relations

In order to add reference of another entity to an entity we need to use relations. Relations can be one-to-one, one-to-many, many-to-one and many-to-many. TypeORM has support for all of these relations using @OneToOne, @OneToMany, @ManyToOne and @ManyToMany respectively. Lets see its implementation with code. At first we create an Avatar entity. Here each writer can have a single avatar and we would like to assign one avatar to a single writer which represents a one-to-one relation.

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

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

    @Column()
    url: string;

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

Now in our Writer entity we need to add this Avatar property along with necessary relation decorator.

import {Entity, OneToOne, JoinColumn} from "typeorm";
import {Avatar} from "./Avatar";

@Entity 
export class Writer {
    // other properties will remain same

    @OneToOne(() => Avatar)
    @JoinColumn()  
    avatar: Avatar;
}
Enter fullscreen mode Exit fullscreen mode

In order to establish one-to-many and many-to-one relations we will create a new entity named Blog. A writer can have many blogs but one blog will be written by one writer only which satisfies our requirement.

import {Entity, PrimaryGeneratedColumn, Column, ManyToOne} from "typeorm";
import {Writer} from "./Writer";

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

    @Column()
    title: string;

    @Column()
    cover: string;

    @Column() 
    content: string;

    @ManyToOne(() => Writer, writer => writer.blogs)
    writer: Writer;
}
Enter fullscreen mode Exit fullscreen mode

Now we update Writer entity as follows to establish one-to-many relation.

import {Entity, OneToMany} from "typeorm";
import {Blog} from "./Blog";

@Entity 
export class Writer {
    // other properties will remain same

    @OneToMany(() => Blog, blog => blog.writer)  
    blogs: Blog[];
}
Enter fullscreen mode Exit fullscreen mode

Now we are left with many-to-many relation which can be established by adding another entity named Category. A blog can have multiple categories and a category can be assigned to multiple blogs too.

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

@Entity()
export class Category {

    @PrimaryGeneratedColumn()
    id: number;

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

Now we will update Writer entity as follows

import {Entity, ManyToMany, JoinTable} from "typeorm";
import {Category} from "./Category";

@Entity 
export class Writer {
    // other properties will remain same

    @ManyToMany(() => Category)  
    @JoinTable()
    categories: Category[];
}
Enter fullscreen mode Exit fullscreen mode

@JoinTable() is required for @ManyToMany relations. We need to put @JoinTable on one (owning) side of relation.

CRUD Operations

In TypeORM we can perform CRUD operations using EntityManager, Repository and Query Builder. EntityManager and Repository are quite similar in syntax while performing necessary operations. The difference is that an Entity Manager handles all entities, while Repository handles a single entity which means that when using an Entity Manager we need to specify the Entity we are working with for each method call. Let us explain it with an example. Suppose we would like to fetch a Writer with a id given.

import {getManager} from "typeorm";
import {Writer} from "./entity/Writer";

// using entity manager
const entityManager = getManager();
const writer = await entityManager.findOne(Writer, 1);

// using repository
const writerRepository = getRepository(Writer); 
const writer = await writerRepository.findOne(1);

// now lets update writer's name
writer.name = "Zlatan Ibrahimovic";

await entityManager.save(writer);
await writerRepository.save(writer);
Enter fullscreen mode Exit fullscreen mode

This is a simple task performed but other tasks like joining and complex operations can also be performed with the help of both entity manager and repository. Now lets talk a bit about query builder. According to the documentation:

QueryBuilder is one of the most powerful features of TypeORM - it allows you to build SQL queries using elegant and convenient syntax, execute them and get automatically transformed entities.

So we can have an idea that how powerful query builder is. Its time for some coding examples again. We perform the above query i.e. find out the first writer using query builder.

const firstWriter = await connection
    .getRepository(Writer)
    .createQueryBuilder("writer")
    .where("writer.id = :id", { id: 1 })
    .getOne();
Enter fullscreen mode Exit fullscreen mode

We can also perform join queries using query builder very easily. Almost all sql queries we perform can be done in TypeORM but in many cases like very complex queries, it can be difficult.

In this post I have tried to discuss the basic topics of TypeORM briefly. Advanced topics like Migration, Indices, Transactions, Listeners, Subscribers etc have not been discussed here. The official document is very elaborative and nicely explained with lots of examples. You are highly encouraged to have a look at it.

Discussion (0)