NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production

This is a post to share my experience on building a client/server solution in NX Workspace with NestJS/Angular. Most tutorials don’t explains how to deal with development & production environments and using TypeORM brings some complexity.

What I w…


This content originally appeared on DEV Community and was authored by Clément

This is a post to share my experience on building a client/server solution in NX Workspace with NestJS/Angular. Most tutorials don't explains how to deal with development & production environments and using TypeORM brings some complexity.

What I want to build ?
An Angular web application
A NestJS API, using TypeORM to link a PostgreSQL database
I develop on my local environment, then deploy on production environment via SSH

Setup local environment

What are the steps ?
First we will bring up our local (development) environment by creating an NX Workspace.

npx create-nx-workspace@latest
  ? Workspace name(e.g., orgname): banana
  ? What to create in the new workspace: angular-nest [a workspace with a full stack application (Angular + Nest)]
  ? Application name: kiwi
  ? Default stylesheet format: SASS(.scss) [http://sass-lang.com]
  ? Use Nx Cloud?: No

Now prepare our local database, I will use PostgreSQL through Docker.
You can install Docker for your OS by reading docker documentation https://docs.docker.com/engine/install/

Create a docker-compose.yml file at root of workspace (near package.json)

version: "3"

services:
  db:
    image: postgres
    restart: always
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: kiwi
      POSTGRES_USER: _username_
      POSTGRES_PASSWORD: _password_

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

Launch our service

sudo docker-compose up -d

You can visit http://localhost:8080 and login to view your empty database, empty but up and running !

image

We can setup NestJS to connect our database, we need to install required package

npm install --save @nestjs/typeorm typeorm pg

Create a ormconfig.local.json at root of workspace (near package.json)
This file is read by TypeORM to connect to the database

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "_username_",
  "password": "_password_",
  "database": "kiwi",
  "entities": ["apps/api/**/*.entity.js"],
  "migrations": ["apps/api/src/migrations/*"],
  "cli": {
    "migrationsDir": "apps/api/src/migrations"
  }
}

Update the apps/api/src/app/app.module.ts file

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';

import { AppController } from './app.controller';
import { AppService } from './app.service';
import { pg } from 'pg'; // keep this, it force generatePackageJson to add `pg` in dependencies
import { getConnectionOptions } from 'typeorm';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      useFactory: async () =>
        Object.assign(await getConnectionOptions(), {
          autoLoadEntities: true,
        }),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

You may be asking what is this import { pg } from 'pg'; for ? The answer will come as soon as we will start to build our project for production environment.

In order to create TypeORM migrations we will add some script helpers in the root package.json

{
  ...,
  scripts: {
    ...,
    "migration:create": "npx typeorm migration:create -f ormconfig.local",
    "migration:run": "ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run -f ormconfig.local"
  },
  }
}

We these scripts we can create a new migration

npm run migration:create -- -n CreateUserTable

This will create a new file in apps/api/src/migrations

import {MigrationInterface, QueryRunner} from "typeorm";

export class CreateUserTable1626968757496 implements MigrationInterface {

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE users(firstname varchar(128))`)
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
    }

}

Then we can run the migration

npm run migration:run

The result is to get a database with 2 tables, the well-known migrations table used TypeORM and our users table.
image

Setup production environment

The production environment will run a Ubuntu-like distro and connect the server via SSH, let's start to install required packages on the remote server

sudo apt install pg nginx
sudo -u postgres psql

postgres=# CREATE USER _prod_username_ WITH PASSWORD '_prod_password_';
CREATE ROLE

postgres=# CREATE DATABASE kiwi;
CREATE DATABASE

postgres=# GRANT ALL PRIVILEGES ON DATABASE kiwi to _prod_username_;
GRANT

Our database is up and running on the production environment. Now we will configure Nginx, start to create a folder architecture to host our build code

mkdir -p workspace/public_html
mkdir -p workspace/api
echo "Hello world" >> workspace/public_html/index.html

Create a new Nginx config file

cd /etc/nginx
sudo touch sites-available/kiwi.example.com

Put this content in kiwi.example.com

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    root /home/john/workspace/public_html;
    index index.html index.htm index.php;
    server_name kiwi.example.com;
    gzip on;

    if ($scheme = http) {
        return 301 https://$host$request_uri;
    }

    location /api {
        proxy_pass http://localhost:3333;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_cache_bypass $http_upgrade;
        # try_files $uri $uri/ =404;
    }

    location / {
        try_files $uri $uri/ /index.html;
    }
    ssl_certificate /etc/letsencrypt/live/kiwi.example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/kiwi.example.com/privkey.pem; # managed by Certbot

}

server {
    if ($host = kiwi.example.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot

    listen 80;
    listen [::]:80;
    server_name kiwi.example.com;
    return 404; # managed by Certbot
}

LetsEncrypt configuration is out-of-scope of this article, just be aware that all # managed by Certbot blocks have been wrote by installing and execute certbot tool which generate self-signed certificate

Then enable this new Nginx configuration

sudo ln -s sites-available/kiwi.example.com sites-enabled/kiwi.example.com
sudo systemctl reload nginx.service

Now you can check your public website is up and running by visition https://kiwi.example.com and read the greating Hello world

Because our API is a NestJS app, we will need NodeJS to run our server. Install it with NVM (https://github.com/nvm-sh/nvm#install--update-script)

nvm install node

Add a line at the end of you $HOME/.profile

PATH="$PATH:/home/john/.nvm/versions/node/v16.5.0/bin"

Now we have NodeJS we can continue install and setup our API dependencies. Install the tool to run and monitor our API service

npm install -g pm2

That's all, our production environment is ready to receive our build

Build & Deploy applications

Leave the production environment and go back to the local environment.

Starting with our API application, we need to build the NestJS code, add migration scripts the build, upload and run the build on the production environnment

Edit angular.json to add migration scripts to the build

{
  ...
  "projects": {
    "api": {
      ...
      "architect": {
        "build": {
          ...
          "options": {
            ...
            "assets": [
              "apps/api/src/assets",
              "apps/api/src/migrations"
            ]
          },
        }
      }
    }
  }
}

Create deploy.sh file in tools/

touch tools/deploy.sh
chmod +x tools/deploy.sh

The content of deploy.sh

#!/bin/bash

SSH_HOST=john@kiwi.example.com
SSH_WORKDIR=workspace
SSH_BASEURL="${SSH_HOST}:${SSH_WORKDIR}"
SCRIPT_DIR=`dirname $(readlink -f $0)`
DIST_DIR="${SCRIPT_DIR}/../dist/apps"

project=$1

function buildApi {
  nx build api --generatePackageJson
}

function deployApi {
  sshUrl="${SSH_BASEURL}/api"
  scp -r ${DIST_DIR}/api/* ${SCRIPT_DIR}/../ormconfig.json $sshUrl
  ssh john@kiwi.example.com "
    . ~/.profile && \
    cd ${SSH_WORKDIR}/api && \
    npm install && \
    ts-node --transpile-only ./node_modules/typeorm/cli.js migration:run && \
    pm2 reload kiwi-api"
}

function buildKiwi {
  nx build kiwi
}

function deployKiwi {
  scp -r ${DIST_DIR}/kiwi/* "${SSH_BASEURL}/public_html"
}

case $project in
  api)
    buildApi
    deployApi
  ;;

  kiwi)
    buildKiwi
    deployKiwi
  ;;

  all)
    buildApi
    deployApi
    buildKiwi
    deployKiwi
  ;;
esac

You can see the --generatePackageJson argument on the API build process. This argument asks NX to generate a package.json file in the dist directory. This package.json will contains all project dependencies that will be required on the production environment. Do you remember the import { pg } from 'pg'; we added in app.module.ts, this line is here to force NX to add PostgreSQL has a dependency in this generated package.json because TypeORM does not expose this dependency.

Add some script helpers to package.json

{
  ...,
  scripts: {
    ...,
    "deploy:api": "./tools/deploy.sh api",
    "deploy:immo": "./tools/deploy.sh immo",
    "deploy:all": "./tools/deploy.sh all",
    "migration:create": "npx typeorm migration:create -f ormconfig.local",
    "migration:run": "ts-node --project tsconfig.base.json -O '{\"module\": \"commonjs\", \"experimentalDecorators\": true}' -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -f ormconfig.local"
  },
  }
}

Copy/paste ormconfig.local.json to ormconfig.json edit ormconfig.json to this content

{
  "type": "postgres",
  "host": "localhost",
  "port": 5432,
  "username": "_prod_username_",
  "password": "_prod_password_",
  "database": "kiwi",
  "entities": ["./**/*.entity.js"],
  "migrations": ["./migrations/*"],
  "cli": {
    "migrationsDir": "apps/api/src/migrations"
  }
}

We are now ready to deploy our apps !

npm run deploy:all

This command will build the NestJS app, add migrations files to the build, upload the build on the production environment, run the migration on the production environment, reload the API application. Then it will build the Angular app, upload the build on the production environment.


This content originally appeared on DEV Community and was authored by Clément


Print Share Comment Cite Upload Translate Updates
APA

Clément | Sciencx (2021-07-30T07:48:39+00:00) NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production. Retrieved from https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/

MLA
" » NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production." Clément | Sciencx - Friday July 30, 2021, https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/
HARVARD
Clément | Sciencx Friday July 30, 2021 » NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production., viewed ,<https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/>
VANCOUVER
Clément | Sciencx - » NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/
CHICAGO
" » NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production." Clément | Sciencx - Accessed . https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/
IEEE
" » NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production." Clément | Sciencx [Online]. Available: https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/. [Accessed: ]
rf:citation
» NestJS/PostgreSQL & Angular within NX Workspace – From scratch to production | Clément | Sciencx | https://www.scien.cx/2021/07/30/nestjs-postgresql-angular-within-nx-workspace-from-scratch-to-production/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.