Database

https://github.com/typeorm/typeorm

Installation

npm install typeorm --save
# or
yarn add typeorm

Directory structure

Create a new folder in the src directory with the following files:

src
├──  databases
│   ├── config
│   │   └── const.ts
│   ├── helpers
│   ├── models       // place where your entities (database models) are stored
│   │   └── CategoryDAO.ts
│   ├── samples       // sample data
│   ├── services
│   │   └── DAO-to-entity.ts  // Object.assign<DAO, Entity>()
│   │   └── entity-to-DAO.ts  // Object.assign<Entity, DAO>()
│   │   └── data-service.ts     // create seed data
│   │   └── database-service.ts  // connect db
│   ├── index.ts
├── entities       // place where your entities (entity models) are stored
│   │   └── Category  // sample model
│   │   │   └── Category.ts
│   │   │   └── CategoryFilter.ts
│   │   │   └── index.ts
├── repositories       // CRUD methods
└── ...
  • Entity: is a business object (BO) within a multitiered software application that works in conjunction with the data access and business logic layers to transport data.
  • DAO: Data Access Objects is an abstraction of data persistence. It would be considered closer to the database, often table-centric.

Usage

Connection

  1. Create the connection
// create the connection instance
const connection = createConnection({
  name: "default",
  type: "react-native",
  database: "default",
  location: "default",
  synchronize: true,
  logging: ["error"],
  entities: [
    Todo, //... whatever entities you have
  ],
})

Common connection options: https://typeorm.io/#connection-options/common-connection-options

  1. Connects the connection.
await connection.connect()
  1. Closes the connection.
await connection.close()

Entity

All active-record entities must extend the BaseEntity class, which provides methods to work with the entity.

Example:

import { BaseEntity, Column } from "typeorm/browser"

@Entity("Category")
export class CategoryDAO extends BaseEntity {
  @Column("integer", { primary: true, name: "id" })
  id: number

  @Column("text", { name: "code" })
  code: string

  @Column("text", { name: "name" })
  name: string

  @Column("boolean", { name: "isMaster" })
  isMaster: boolean

  @Column("datetime", { name: "startAt", nullable: true })
  startAt: Date

  @Column("integer", { name: "iconId", nullable: true })
  iconId: number
}
Primary columns

Each entity must have at least one primary column. There are several types of primary columns besides the above example:

  • @PrimaryColumn() creates a primary column which takes any value of any type. You can specify the column type. If you don't specify a column type it will be inferred from the property type. The example below will create id with int as type which you must manually assign before save.
  • @PrimaryGeneratedColumn() creates a primary column which value will be automatically generated with an auto-increment value. It will create int column with auto-increment/serial/sequence (depend on the database). You don't have to manually assign its value before save - value will be automatically generated.
  • @PrimaryGeneratedColumn("uuid") creates a primary column which value will be automatically generated with uuid. Uuid is a unique string id. You don't have to manually assign its value before save - value will be automatically generated.
Special columns

There are several special column types with additional functionality available:

  • @CreateDateColumn is a special column that is automatically set to the entity's insertion date. You don't need to set this column - it will be automatically set.
  • @UpdateDateColumn is a special column that is automatically set to the entity's update time each time you call save of entity manager or repository. You don't need to set this column - it will be automatically set.
  • @DeleteDateColumn is a special column that is automatically set to the entity's delete time each time you call soft-delete of entity manager or repository. You don't need to set this column - it will be automatically set. If the @DeleteDateColumn is set, the default scope will be "non-deleted".
  • @VersionColumn is a special column that is automatically set to the version of the entity (incremental number) each time you call save of entity manager or repository. You don't need to set this column - it will be automatically set.

Relations

Don't use relation in my project! You can read more about the Relations on docs.

Active Record, Data Mapper and Query Builder

Active Record

The Active Record pattern is an approach to access your database within your models. Read more.

// example how to save AR entity
const categoryDAO = new CategoryDAO()
user.code = "ABC"
user.name = "Saw"
user.isMaster = true
await categoryDAO.save() // or await CategoryDAO.save(categoryDAO);

// example how to remove AR entity
await user.remove()

// example how to load AR entities
const categories: CategoryDAO[] = await CategoryDAO.find({
  where: { code: Equal("ABC") },
})

Data Mapper

Data Mapper is an approach to access your database within repositories instead of models. Read more.

const categoryDAORepository = connection.getRepository(CategoryDAO)

// example how to save AR entity
const categoryDAO = new CategoryDAO()
user.code = "ABC"
user.name = "Saw"
user.isMaster = true
await categoryDAORepository.save()

// example how to remove AR entity
await categoryDAORepository.remove()

// example how to load AR entities
const categories: CategoryDAO[] = await categoryDAORepository.find({
  where: { code: Equal("ABC") },
})

Query Builder

Query builder is used build complex SQL queries in an easy way. It is initialized from Connection method and QueryRunner objects.

We can create QueryBuilder in three ways.

Connection

Consider a simple example of how to use QueryBuilder using connection method.

import { getConnection } from "typeorm/browser"

const category = await getConnection()
  .createQueryBuilder()
  .select("Category")
  .from(CategoryDAO, "Category")
  .where("Category.id = :id", { id: 1 })
  .getOne()

Entity manager

Let's create a query builder using entity manager as follows −

import { getManager } from "typeorm/browser"

const category = await getManager()
  .createQueryBuilder(CategoryDAO, "Category")
  .where("Category.id = :id", { id: 1 })
  .getOne()

Repository

We can use repository to create query builder. It is described below,

import { getRepository } from "typeorm/browser"

const category = await getRepository(CategoryDAO)
  .createQueryBuilder("Category")
  .where("Category.id = :id", { id: 1 })
  .getOne()

Which one should I choose?

One thing we should always keep in mind with software development is how we are going to maintain our applications. | Active Record | Data Mapper | Query Builder | |—|—|—| | The Active record approach helps keep things simple which works well in smaller apps. And simplicity is always a key to better maintainability. | The Data Mapper approach helps with maintainability, which is more effective in bigger apps. | A better approach would be to build up the join table yourself by adding a custom join table entity. Then use Query Builder to replace the old foreign ky to the old channel with the is of the new one |