HomeWho We AreContact

Setting Up NestJS on AWS Lightsail Steps

By Neil Ludlow
Published in Software
October 09, 2024
5 min read
Setting Up NestJS on AWS Lightsail Steps

Why NestJS?

NestJS is a server-side framework, that can be used to create an API.

NestJS is a javascript framework, I’d say it was more of a proper framework compared to something like Node and Express which would be a very simple way to create an API. You get a lot of functionality out of the box with NestJS, and the code is set up to be structured. It’s set up to use TDD and Typescript. However, NestJS is more difficult to use for the first time than a simple Node/Express API would be.

There are many different options for how to code an API. Alternatives would be Node/Express in the javascript universe, or something like Laravel in PHP, or a service such as Google’s Firebase. I’d used NestJS before and wanted to use it again.

Why put NextJS on Lightsail?

Whereas normally you might have an automated deployment process with Docker, for testing or as a dev server you might just want a cheap instance of Lightsail to save money. You may also be new to NestJS and appreciate the process of getting it set up manually on an instance. In the AWS ecosystem, Lightsail will be a lot cheaper than an EC2 instance.

What are we showing?

This is mainly about how to set up a NestJS project that you already have running locally, but I’ve also looked at the code for sending email and connecting to the database briefly for reference.

We’ll…

  • Get the NestJS up and running on a Lightsail “OS Only” instance with Ubuntu 24.04 using Nginx and PM2.
  • Make sure a ReactJS front end can communicate with it.
  • Take a quick look at sending email using Mailgun.
  • Use MongoDB as a database.
  • Have a look at some troubleshooting methods.

Setting up the Ubuntu instance

To get a quick and dirty API set up on Ubuntu 24.04 on AWS Lightsail, here’s a quick walkthough.

  • Create an “OS Only” Ubuntu 24.04 instance in the Lightsail control panel.
  • Log in to instance
sudo apt-get update
sudo apt upgrade -y
sudo apt autoremove
sudo reboot

When you log back in you should see something like this…

Welcome to Ubuntu 24.04.1 LTS (GNU/Linux 6.8.0-1016-aws x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Thu Sep 26 17:53:22 UTC 2024
System load: 0.01
Usage of /: 15.1% of 18.33GB
Memory usage: 48%
Swap usage: 0%
Temperature: -273.1 C
Processes: 109
Users logged in: 0
IPv4 address for ens5: 172.26.13.208
IPv6 address for ens5: 2a05:d01c:78d:5900:f364:2234:b4bc:3deb
* Ubuntu Pro delivers the most comprehensive open source security and
compliance features.
https://ubuntu.com/aws/pro
Expanded Security Maintenance for Applications is not enabled.
0 updates can be applied immediately.
Enable ESM Apps to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
Last login: Thu Sep 26 17:41:53 2024 from 52.94.39.60

Making the NestJs API

Now, make the directory you want to store the API code in. If you have a NestJS project on your computer you can just FTP the files into the new directory.

mkdir api
cd api
  • FTP files into “api” directory

NPM 1

Ubuntu is up-to-date, the code is in place. Now, we need to run the npm install command, but first we’ll need to install NPM…

$ sudo apt install npm -y
$ npm -v
9.2.0
$ sudo npm install -g npm@latest```
$ npm -v
9.2.0
$ sudo reboot
  • Log in again
$ npm -v
10.8.3
$ cd api
$ npm install

This resulted in the process gradually getting slower and slower and eventually stopping…

$ npm install
⠦Killed

Add Swap Space to Ubuntu

To fix the error we had with NPM, a quick search and it might be that we don’t have any swap space, so let’s set that up.

How To Add Swap Space on Ubuntu 22.04

sudo swapon --show

No output shown.

$ free -h
total used free shared buff/cache available
Mem: 416Mi 188Mi 174Mi 2.7Mi 84Mi 228Mi
Swap: 0B 0B 0B
$ df -h
Filesystem Size Used Avail Use% Mounted on
/dev/root 19G 3.1G 16G 17% /
tmpfs 209M 0 209M 0% /dev/shm
tmpfs 84M 880K 83M 2% /run
tmpfs 5.0M 0 5.0M 0% /run/lock
efivarfs 128K 3.8K 120K 4% /sys/firmware/efi/efivars
/dev/nvme0n1p16 881M 133M 687M 17% /boot
/dev/nvme0n1p15 105M 6.1M 99M 6% /boot/efi
tmpfs 42M 12K 42M 1% /run/user/1000
$ sudo fallocate -l 1G /swapfile
$ ls -lh /swapfile
-rw-r--r-- 1 root root 1.0G Sep 26 18:22 /swapfile
$ sudo chmod 600 /swapfile
$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 1024 MiB (1073737728 bytes)
no label, UUID=98ad9322-669f-4821-b3d1-38445da1a258
$ sudo swapon /swapfile
$ sudo swapon --show
NAME TYPE SIZE USED PRIO
/swapfile file 1024M 0B -2
$ free -h
total used free shared buff/cache available
Mem: 416Mi 189Mi 159Mi 2.7Mi 99Mi 227Mi
Swap: 1.0Gi 0B 1.0Gi
$ sudo cp /etc/fstab /etc/fstab.bak # Backup fstab incase anything goes wrong
$ echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab # Make the swap file permanent
$ cat /proc/sys/vm/swappiness
60 # 60 is ok for a desktop
$ sudo sysctl vm.swappiness=10 # Set to 10 for a server
vm.swappiness=10

Add this to the bottom of /etc/sysctl.conf…

vm.swappiness=10
$ cat /proc/sys/vm/vfs_cache_pressure
100
$ sudo sysctl vm.vfs_cache_pressure=50
vm.vfs_cache_pressure = 50

Add this to the bottom of /etc/sysctl.conf…

vm.vfs_cache_pressure = 50

NPM 2

Now that we have some swap space, let’s try the npm install again. If the “node_modules” directory exists, delete it and start again…

rm -rf node_modules/
npm install
npm warn deprecated @humanwhocodes/[email protected]: Use @eslint/object-schema instead
npm warn deprecated @humanwhocodes/[email protected]: Use @eslint/config-array instead
npm warn deprecated [email protected]: This module is not supported, and leaks memory. Do not
use it. Check out lru-cache if you want a good and tested way to coalesce async requests
by a key value, which is much more comprehensive and powerful.
npm warn deprecated [email protected]: This package is no longer supported.
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: Rimraf versions prior to v4 are no longer supported
npm warn deprecated [email protected]: This package is no longer supported.
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: Glob versions prior to v9 are no longer supported
npm warn deprecated [email protected]: This package is no longer supported.
added 980 packages, and audited 981 packages in 11m
153 packages are looking for funding
run `npm fund` for details
32 high severity vulnerabilities
To address issues that do not require attention, run:
npm audit fix
To address all issues (including breaking changes), run:
npm audit fix --force
Run `npm audit` for details.

That worked, let’s try building the application…

npm run build
> nest build

All working so far, let’s see if we can get the app connected to the internet…

Nginx

Nginx has to be installed, then configured…

sudo apt install nginx
sudo nano /etc/nginx/sites-available/api.conf
server {
listen 80;
server_name api.website.com;
client_max_body_size 50M;
location / {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Access-Control-Allow-Origin *;
}
}
$ sudo unlink /etc/nginx/sites-enabled/default
$ sudo ln -s /etc/nginx/sites-available/api.conf /etc/nginx/sites-enabled/
$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
$ sudo systemctl enable nginx
$ sudo service nginx restart

PM2

To get the API to be always running on port 3000 we can use PM2…

PM2 installation

ecosystem.config.js…

module.exports = {
apps: [
{
name: 'api',
script: './dist/src/main.js',
},
],
};
$ sudo npm install pm2@latest -g
$ pm2 completion install
$ pm2 -v
5.4.2

Make ecosystem.config.js file in the root

pm2 start
pm2 logs
pm2 plus #Sign up to view the web dashboard

If there are any problems pm2 logs should tell you what they are.

CORS

main.ts…

const app = await NestFactory.create<NestExpressApplication>(AppModule, {
cors: true,
});

Also, in the Nginx config file we had…

proxy_set_header Access-Control-Allow-Origin *;

Connecting to a Database

In this example I’m connecting to MongoDB, a service that I don’t need to do much setup to so I can get this example working pretty quickly.

I won’t go too much into how NestJS and TypeORM work with MongoDB, but the official docs are pretty good…

Mongo and NestJS

One thing to bear in mind is to whitelist the IP of the Lightsail instance for your MongoDB database. Trying to connect to a database but not being allowed to because the IP is not whitelisted will generally be a bad thing.

NestJS and communicating with a database

If you haven’t used NestJS before it can be quite daunting. I’m including some sample code to give an idea of how it works as even the official docs can be confusing.

To begin with, to test the connection and get some database calls happening pretty quickly I created a user.schema.ts like this, below. I am validating the email field in the Schema to make sure it is a valid email…

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { HydratedDocument } from 'mongoose';
export type UserDocument = HydratedDocument<User>;
const validateEmail = function (email: string): boolean {
const re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
return re.test(email);
};
@Schema()
export class User {
@Prop({
type: String,
required: [true, 'Username required'],
validate: [validateEmail, 'username must be a valid email address'],
unique: [true, 'email address already used, try again'],
})
username: string;
@Prop({
type: String,
required: [true, 'Password required'],
hide: true,
})
password: string;
@Prop({ type: Date })
created_at: Date;
@Prop({ type: Date })
last_logged_in_at: Date;
}
export const UserSchema = SchemaFactory.createForClass(User);

Then, my user.service.ts could be something like this…

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { User } from '../common/schemas/user.schema';
@Injectable()
export class UserService {
constructor(
@InjectModel(User.name) private readonly userModel: Model<User>,
) {}
async getAllUsers(): Promise<User[]> {
return await this.userModel.find().exec();
}
async getUser(username: string): Promise<User> {
return await this.userModel.findOne({ username: username });
}
}

I have the password getting checked in the Validation, for example. If all emails and passwords must be in a certain format, I will not even ask the database if they are in an incorrect format…

import {
IsEmail,
IsOptional,
IsString,
Matches,
MinLength,
} from 'class-validator';
export class CreateUserFormDto {
@IsEmail()
username: string;
@IsString()
@MinLength(8)
@Matches(/\d/, {
message: 'password must contain a number',
})
@Matches(/[a-z]/, {
message: 'password must contain a lowercase letter',
})
@Matches(/[A-Z]/, {
message: 'password must contain an uppercase letter',
})
@Matches(/[`!@#$£%^&*()_+\-=\[\]{};':"\\|,\.<>\/?~]/, {
message: 'password must contain a special character',
})
password: string;
@IsOptional()
created_at: Date;
@IsOptional()
last_logged_in_at: Date;
}

Sending email

Check the correct email connection info using swaks…

sudo apt-get install swaks

What we’re doing here is checking that the server, username (—au) and password (—ap) are correct before we try to connect in the app. In this case I am using my Mailgun info, but you should be able to use any email provider, although some may have a more complex log in system.

./swaks --auth \
--server smtp.mailgun.org \
--body 'This is a swaks test' \
--h-Subject 'Swaks Test!' \
--ap 'TheAPIKeyOrPassword'

Once you have the SMTP info that works, choose an email package to use with NestJs… As I am using Mailgun, I tried a couple of different packages before just plumping for a general email sending package…

  • Do no use mailgun-js, deprecated.
  • Do not use mailgun.js, doesn’t seem to work.
  • Use nodemailer.
npm install --save @nestjs-modules/mailer nodemailer
npm install --save-dev @types/nodemailer

Then sending an email can be something like this, for example, email.service.ts…

import { Injectable } from '@nestjs/common';
import { MailerService } from '@nestjs-modules/mailer';
@Injectable()
export class MailgunService {
constructor(private readonly mailService: MailerService) {}
async sendNewUserMail(username: string, subject: string, message: string) {
await this.mailService.sendMail({
to: username,
subject: subject,
text: message,
});
}
}

Example app.module.ts

  • config module allows .env to be used
  • mongoose module to connect to the mongodb instance
  • jwt for the auth methods
  • mailer to connect to mailgun
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { MailerModule } from '@nestjs-modules/mailer';
import { JwtService } from '@nestjs/jwt';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
@Module({
imports: [
ConfigModule.forRoot({ isGlobal: true }),
MongooseModule.forRoot(process.env.MONGOOSE_CONNECTION),
MailerModule.forRoot({
transport: {
host: process.env.MAILGUN_DOMAIN,
auth: {
user: process.env.MAILGUN_USER,
pass: process.env.MAILGUN_API_KEY,
},
},
}),
UserModule,
],
controllers: [AppController],
providers: [AppService, JwtService],
})
export class AppModule {}

Troubleshooting a NestJS API

Update pm2 to the latest version

sudo npm install pm2@latest -g

Re-install node modules and clean cache

rm -rf node_modules
rm package-lock.json
npm cache clean --force
npm install

Upgrade NVM

Upgrade NVM to the latest version…

$ nvm -v
10
$ nvm install 14

Debugging

View PM2 logs to see any error messages or console.logs…

pm2 logs
cat /home/ubuntu/.pm2/logs/api-error.log

Or, if you’re just using node, you can cat or tail the error logs where you specified.

Is the database connected?

When first setting up NestJs for the first time, something that may not give helpful error messages might be if the database is not connected properly.

For example, I had to whitelist the API instance IP address with the database service for the database to work. Because the database was not connecting successfully I was unable to get the application working at all until the IP was whitelisted.

Node

It’s also possible to run the API on port 3000 using node. To run the application and write to the access and error logs, instead of displaying the info on screen, like this…

node dist/main.js > api.log 2> api_error.log

Try/Catch

All the usual methods for troubleshooting can apply. Try/catch can be particularly useful.

Summary

This has been an overview of some of the basic things to look at when initially setting up NestJS on an Ubuntu Lightsail instance. I’ve included a little bit of code the aid in understanding, but this is mainly looking at the general concepts. While we get the CORS working, connect to the database, and send email, there are plenty of other things to consider and that a fully functioning website will need. The code is to give anyone who is brand new to NestJS and wants to learn some of the concepts an idea to work from.

I have tried to link to other guides I’ve used here, but if I haven’t posted a link I probably have used another guide as a template and made several stackoverflow queries in each section.


Tags

#ubuntu#nestjs#nginx#pm2#aws#mongodb#mailgun#javascript

Share

Previous Article
Choosing a Tech Stack in 2024
Next Article
Upgrading Software
Neil Ludlow

Neil Ludlow

Freelance Software Developer

Table Of Contents

1
Why NestJS?
2
Why put NextJS on Lightsail?
3
What are we showing?
4
Setting up the Ubuntu instance
5
Making the NestJs API
6
NPM 1
7
Add Swap Space to Ubuntu
8
NPM 2
9
Nginx
10
PM2
11
CORS
12
Connecting to a Database
13
Sending email
14
Example app.module.ts
15
Troubleshooting a NestJS API
16
Summary

Related Posts

Using Linux Find
August 25, 2024
3 min
© 2024, All Rights Reserved.
Powered By

Quick Links

About ShortdarkContact

Legal Stuff

Social Media