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 : -
npm install pg reflect-metadata typeorm
In our app.ts
we will import reflect-metadata
just below our dotenv
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 -
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 : -
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 -
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 :
"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 :
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 :
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 :
public async down(queryRunner: QueryRunner) {
await queryRunner.query('drop table todos')
}
Now run the migration running the following command:
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 :
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 : -
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
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. In the next tutorial we will use our TodoEntity in our controllers to fetch data from the database until next time PEACE.
Top comments (0)