Setup express with Typescript - typeorm setup
Tags: javascript, typescript, node
Published: 2022-07-12 07:02:28

### Introduction

In this series we will setup an express server using `Typescript`, we will be using `TypeOrm` as our ORM for querying a PostgresSql Database, we will also use `Jest` and `SuperTest` for testing. The goal of this series is not to create a full-fledged node backend but to setup an express starter project using typescript which can be used as a starting point if you want to develop a node backend using express and typescript.

### Overview

This series is not recommended for beginners some familiarity and experience working with `nodejs`, `express`, `typescript` and `typeorm` is expected. In this post which is part three of our series we will set up typeorm : - 
 - Setup `typeorm` and connect to a Postgres Database.
 - Setup migrations and create a todo table.
 - Setup Todo Enitity.
 - Handle Graceful shutdowns.

### Step One: Setup Typeorm

Lets start by installing the necessary dependencies : - 
```bash
 npm install pg reflect-metadata typeorm
```

In our `app.ts` we will import `reflect-metadata` just below our dotenv
```ts
import 'reflect-metadata'
```

Lets update our `.env` and `.env.sample` with the following environment variables - 
```
NODE_ENV=
SERVER_PORT=
RDS_POSTGRES_USERNAME=
RDS_POSTGRES_PASSWORD=
RDS_POSTGRES_HOST=
RDS_DATABASE_DEV=
RDS_DATABASE_TEST=
RDS_DATABASE_PROD=
```
I am using AWS RDS Postgres Database so I have named my env variables as such. Copy the above env variables in your `.env` file and fill them up.

Now create a new folder under `src` called `config` and under `src/config` create a new file called `dbConfig.ts` and paste the following - 
```ts
import 'dotenv/config'

export const dbConfig = {
  development: {
    host: process.env.RDS_POSTGRES_HOST,
    username: process.env.RDS_POSTGRES_USERNAME,
    password: process.env.RDS_POSTGRES_PASSWORD,
    database: process.env.RDS_DATABASE_DEV,
  },
  test: { 
    host: process.env.RDS_POSTGRES_HOST,
    username: process.env.RDS_POSTGRES_USERNAME,
    password: process.env.RDS_POSTGRES_PASSWORD,
    database: process.env.RDS_DATABASE_TEST,
   },
   production: {
    host: process.env.RDS_POSTGRES_HOST,
    username: process.env.RDS_POSTGRES_USERNAME,
    password: process.env.RDS_POSTGRES_PASSWORD,
    database: process.env.RDS_DATABASE_PROD,
   },
}
```
We created an object where its keys are equivalent to our NODE_ENV values namely `test, production and development`. Take note we are using different database for each environment. Now under `src` folder create a new file `datasource.ts` and paste the following code : - 
```ts
import { DataSource } from 'typeorm'

import { dbConfig } from './config/dbConfig'

const environment = (process.env.NODE_ENV || 'development')  as keyof typeof dbConfig

const credentials = dbConfig[environment]

export const AppDataSource = new DataSource({
  type: 'postgres',
  host: credentials.host,
  port: 5432,
  username: credentials.username,
  password: credentials.password,
  database: credentials.database,
  logging: true,
  synchronize: false,
  entities: [__dirname + '/**/*.entity.{js,ts}'],
  migrations: [__dirname + '/migrations/*.{js,ts}'],
})
```
In the above snippet we create our AppDataSource we passed our credentials, we have set synchronize to false because we will be using migrations to create and delete databases. The `entities` key will search for files ending with `.entity.ts` or `.entity.js`. The `migrations` will be located inside the `src/migrations` folder.

Now lets import our Datasource in our main `app.ts` file and connect to the database. Inside the `startServer` method paste - 
```ts
private startServer() {
 this.server.listen(this.port, async () => {
   console.log('Server started on port', this.port)
   try {
    await AppDataSource.initialize()
    console.log('Database Connected')
   } catch(error) {
    console.log('Error connecting to Database', error)
   }
 })
}
``` 
Now run `npm run dev` from the terminal it should print `Database Connected` in the console. With that our Database connection is completed.

### Step Two: Setup migrations and create a Todos table

Setting up migrations with typeorm is kind of tricky if you are using typescript in your node project. In your `package.json` paste the following under the scripts section : 
```json
  "typeorm": "typeorm-ts-node-commonjs -d ./src/datasource.ts",
  "migration:generate": "npm run typeorm migration:generate",
  "migration:show": "npm run typeorm migration:show",
  "migration:run": "npm run typeorm migration:run",
  "migration:revert": "npm run typeorm migration:revert",
  "migration:create": "typeorm-ts-node-commonjs migration:create"
```

First lets create a `migration` folder, inside the `src` folder. Now to create a migration we run the following in the terminal : 
```bash
 npm run migration:create src/migrations/todos
```
This will create a new migration file inside of the migrations folder. Open the migration file, for the `up` function paste : 
```ts
public async up(queryRunner: QueryRunner) {
 await queryRunner.query('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"')
  await queryRunner.query(`
    create table todos(
      id uuid primary key not null default uuid_generate_v1(),
      text varchar(255) not null,
      status varchar(125) not null check (status in ('pending', 'done')) default 'pending')
`)
}
``` 
We have 2 queries first to create an extension for uuid so that we can use `uuid_generate_v1()` this is postgres specific. Second query is a simple create table query. Now in the `down` function paste the following code : 
```ts
public async down(queryRunner: QueryRunner) {
  await queryRunner.query('drop table todos')
}
```
Now run the migration running the following command: 
```bash
npm run migration:run
```
And verify whether the todos table is created inside the database, also make sure you have the database before running the migration.

### Step 3: Setup a Todo Entity

Lets now setup a typeorm todo entitiy. Inside the `src/api/todos` folder create a new file called todos.entity.ts and paste the following code : 
```ts
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'

export enum TodoStatus {
  PENDING = 'pending',
  DONE = 'done'
}

@Entity()
export class Todos {
  @PrimaryGeneratedColumn()
   id: string

  @Column()
   text: string

  @Column({
   type: 'enum',
   enum: TodoStatus,
   default: TodoStatus.PENDING
  })
   status: TodoStatus
}
```

### Step Four: Handling Graceful shutdowns

It is very important to shutdown our node server and disconnect from our database gracefully. But what do we mean by graceful shutdowns ? When we deploy our node servers say on EBS, EC2 your server may go down. When our node server is killed, **it should stop accepting new requests, complete all the current requests, we should then close our database connection and finally gracefully shutdown our node server / process**. This video on youtube is a great explanation of this overall process, I recommend you to please watch this - https://www.youtube.com/watch?v=Z82mZV2Ye38. 

In your `app.ts` lets add 2 new private functions like so : - 
```ts
private handleServerShutDown() {
// Ctrl + C
 process.on('SIGINT', () => {
   console.log('SIGINT RECEIVED, SHUTTING DOWN')
   this.stopServer()
 })

// kill command
 process.on('SIGTERM', () => {
   console.log('SIGTERM RECEIVED, SHUTTING DOWN')
   this.stopServer()
 })
}

private stopServer() {
  this.server.close(() => {
   console.log('EXPRESS SERVER IS CLOSED')
    AppDataSource.destroy().then(() => {
     console.log('DATABASE DISCONNECTED')
     process.exit(0)
    })
  })
}
```
`SIGINT` is triggered on CTRL + C and `SIGTERM` is triggered when your process gets killed. In the stopServer() method we are closing our express server, destroying our database connection and finally exiting our node process using `process.exit(0)`. 

Now inside the constructor we will call our `handleServerShutDown` method
```ts
constructor() {
  this.server = http.createServer(expressServer)
  this.startServer()
  this.handleServerShutDown()
}
```

Now we can start our node server by running `npm run dev` and then stop the server using CTRL+C you will see the console.logs from the shutdown functions.

Lets now build our project, by running `npm run build` and then run `npm run start`, everything should work as expected, check the console.logs and also the `/api/todos` routes.

### Summary
Well then we have setup typeorm and todos entity, along with a migration. All the code for this tutorial can be found under the `feat/setup-typeorm` branch [here](https://github.com/yaldram/express-typescript-starter/tree/feat/setup-typeorm). In the next tutorial we will use our TodoEntity in our controllers to fetch data from the database until next time PEACE.