This is a super fast course on Nest.js. In this tutorial, we will build a blog from the first line of code to deployment in just a few simple steps, taking less than 10 minutes.
The reason it’s so fast is that this tutorial won’t delve into detailed explanations of the process. Instead, it will guide you directly to build a finished product. I believe that learning a framework is faster by modifying an existing project to fit your own needs.
This blog consists of 3 functional modules, representing a common technology stack for a pure Node.js backend project:
- Nest.js
- PostgreSQL, as the database
- ejs, for rendering pages
Without further ado, let’s get started:
1. Initialize the Project
Nest.js projects rely heavily on the CLI tool for project management. First, let’s install the Nest.js CLI.
npm i -g @nestjs/cli
Use the CLI to create a new project.
nest new personal-blog
This will create a new folder named personal-blog
and install all the necessary dependencies. Open this directory in your favorite editor to officially start editing.
2. Connect to the PostgreSQL Database
Next, we will integrate a PostgreSQL database. Following the official recommendation, we’ll use TypeORM as the ORM. The role of an ORM is to integrate the database into the code.
Install Dependencies
npm install @nestjs/typeorm typeorm pg
-
@nestjs/typeorm
: The official Nest.js module for TypeORM, adapting TypeORM for Nest.js. -
typeorm
: The TypeORM library itself. -
pg
: The Node.js driver for PostgreSQL, enabling Node.js to read and write to PostgreSQL.
Set up the Database
To speed up the tutorial, we will skip the step of installing and setting up a database locally. Instead, we’ll provision an online database directly.
We can create a free database with one click on Leapcell.
After registering an account on the website, click “Create Database”.
Enter a Database name, select a deployment region, and you can create the PostgreSQL database.
On the new page that appears, you will find the information needed to connect to the database. A control panel is provided at the bottom, allowing you to read and modify the database directly on the webpage.
Configure the Database Connection
Open the src/app.module.ts
file and import TypeOrmModule
.
Fill in the connection information using the database configuration from Leapcell. Note that sslMode
must be set to true
, otherwise, the connection will fail.
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'your_leapcell_host', // Replace with your Leapcell host
port: 5432,
username: 'your_postgres_username', // Replace with your PostgreSQL username
password: 'your_postgres_password', // Replace with your PostgreSQL password
database: 'personal_blog_db', // Replace with your database name
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true, // Set to true in development, it automatically syncs the database schema
ssl: true, // Required for services like Leapcell
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Create the Posts Module
Next, let’s create a module to manage blog posts.
You can use the Nest CLI to quickly generate the required files:
nest generate module posts
nest generate controller posts
nest generate service posts
After that, we need to create the Post
entity file to connect it with the database. In the src/posts
directory, create a file named post.entity.ts
:
// src/posts/post.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
@Entity()
export class Post {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
title: string;
@Column('text')
content: string;
@CreateDateColumn()
createdAt: Date;
}
Next, we need to connect to the database.
Register TypeOrmModule
in PostsModule
: open src/posts/posts.module.ts
and import TypeOrmModule.forFeature([Post])
.
// src/posts/posts.module.ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { PostsController } from './posts.controller';
import { PostsService } from './posts.service';
import { Post } from './post.entity';
@Module({
imports: [TypeOrmModule.forFeature([Post])],
controllers: [PostsController],
providers: [PostsService],
})
export class PostsModule {}
Go to the Database details page on Leapcell and execute the following command in the web editor to generate the corresponding table.
CREATE TABLE "post" (
"id" UUID PRIMARY KEY DEFAULT gen_random_uuid(),
"title" VARCHAR NOT NULL,
"content" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
Finally, we need to create the PostsService
. The PostsService
will be responsible for handling all business logic related to posts. Open src/posts/posts.service.ts
and add the following code:
// src/posts/posts.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './post.entity';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private postsRepository: Repository<Post>
) {}
findAll(): Promise<Post[]> {
return this.postsRepository.find({
order: {
createdAt: 'DESC',
},
});
}
findOne(id: string): Promise<Post> {
return this.postsRepository.findOneBy({ id });
}
async create(post: Partial<Post>): Promise<Post> {
const newPost = this.postsRepository.create(post);
return this.postsRepository.save(newPost);
}
}
Set Up EJS for Page Rendering
Now, we’ll set up EJS so that we can render dynamic HTML pages.
Install Dependencies
npm install ejs
Integrate the View Engine
Open the src/main.ts
file and change it to the following:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { join } from 'path';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
app.useStaticAssets(join(__dirname, '..', 'public'));
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('ejs');
await app.listen(3000);
}
bootstrap();
In the project root directory (at the same level as src
), create views
and public
folders:
- personal-blog
- src
- views
- public
Implement the Frontend Pages
Create the following files in the views
folder:
-
_header.ejs
(Reusable header)
</span>
lang="en">
charset="UTF-8" />
name="viewport" content="width=device-width, initial-scale=1.0" />
< %= title %>
rel="stylesheet" href="/css/style.css" />
href="/posts/new" class="new-post-btn">New Post
-
_footer.ejs
(Reusable footer)