An overview of TypeORM
Tags: programming, database, beginners, orm
Published: 2021-10-04 20:13:24

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](https://typeorm.io/)

###Installation
We can install TypeORM from `npm` registry very easily with the following command
```
npm i -g typeorm
```
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 
```
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
```

###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"
});
```
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;
}
``` 
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;
}
```   
###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;
}
```
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;
}
```
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;
}
```
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[];
}
```
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;
}
```
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[];
}
```
`@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);
```
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();
```
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](https://typeorm.io/) is very elaborative and nicely explained with lots of examples. You are highly encouraged to have a look at it.