NestJS is a Node.js back-end development framework built upon Express, leveraging the power of TypeScript.
In this lesson, we will learn how to create a Rest Api using NestJs. Get the source code here.
To add swagger docs and deploy to heroku check here
🚀 Quick start:
Install node
Install nest cli: `npm i -g @nestjs/cli`
Initialize a nestjs project `nest new project-name`
Technology used:
Nodejs, NestJs, Psql, TypeOrm, Git
Agenda
🐶 Register a puppy
🐶 Get a puppy
🐶 Get all puppies
🐶 Update puppy profile
🐶 Delete a puppy profile after adoption
For the love of puppies, let's get started
Let's start by creating our project. I will call it puppies.
Let's change into the directory to run the puppies app
Lets see the folder structure that came preinstalled with NestJs
To start the app, run yarn start:dev
The above command produces the dist folder, this is the compilation of our Type Script files into Vanilla JavaScript .
Now, let's see if our app is running. NestJs by default, runs on localhost:3000
. To see that in action we use:
Now that we have got our app with no error, let's dive into each file.
Main.ts
Let's go into the main entry file. Our app runs on port:3000 like I said earlier. We can change the port to other than 3000. we will use port 7890 in this tutorial.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
const port = process.env.PORT || 7890;
async function bootstrap() {
const app = await NestFactory.create(AppModule, {
abortOnError: false,
});
await app.listen(port);
}
bootstrap();
If you noticed i added the abortOnError: false
, this will not make your app exit if any error happens instead it throws an error
Controllers
Controllers are responsible for handling incoming requests and returning responses to the client.
import { Controller, Delete, Get, Post, Put } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('puppies')
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Post()
registerPuppy() {
return this.appService.register();
}
@Get(':id')
getPuppy(id: string) {
return this.appService.read(id);
}
@Get()
getPuppies() {
return this.appService.readAll();
}
@Put(':id')
updatePuppy(id: string, puppy: any) {
return this.appService.update(id, puppy);
}
@Delete(':id')
deletePuppy(id: string) {
return this.appService.delete(id);
}
}
Let's move to our Service
to flesh out the register
, read
, readAll
, update
and delete
logic.
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
register() {
return 'Puppy registered!';
}
read(id: string) {
return `Puppy with id ${id}`;
}
readAll() {
return 'All puppies';
}
update(id: string, puppy: any) {
return `Puppy with id ${id} updated`;
}
delete(id: string) {
return `Puppy with id ${id} deleted`;
}
}
Database and entities
Let's us design our database entities[schemas] should look like. We install the typeorm library that will help us connect to the db.
Before we go further, let us create our database using the terminal.
Install the pg, the non-blocking PostgreSQL client for Node.js.
Next, we create our ormconfig.js
file where our database credentials lies
require('dotenv').config();
module.exports = {
name: 'default',
type: 'postgres',
host: process.env.DATABASE_HOST,
port: 5432,
username: process.env.DATABASE_USERNAME,
password: process.env.DATABASE_PASSWORD,
database: process.env.DATABASE_NAME,
synchronize: true,
logging: true,
entities: [ 'dist/**/*.entity.js'],
};
Env variables
Notice that i don't want to expose my database password. So we need the
dotenv
library
Install dotenv
by running this command
yarn add dotenv
.
Create a .env
in your root and past these credentials there.
PORT=7890
DATABASE_HOST=localhost
DATABASE_USERNAME=postgres
DATABASE_NAME=puppies
DATABASE_PASSWORD=your password here
Lets create our data structure in the app.entity.ts
import {
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('puppies')
export class PuppyEntity {
@PrimaryGeneratedColumn('uuid')
id: string;
@CreateDateColumn()
created: Date;
@Column({
type: 'text',
unique: true,
nullable: false,
})
name: string;
@Column()
age: number;
@Column()
breed: string;
@Column()
color: string;
}
Run yarn start:dev
again and let's our database connection result.
Data Transfer Objects: app.dto.ts
This is an object is an object that defines how data will be sent over the network.
Install and import class-validator
import { IsNotEmpty } from 'class-validator';
export class PuppyDTO {
@IsNotEmpty()
name: string;
@IsNotEmpty()
age: number;
@IsNotEmpty()
breed: string;
@IsNotEmpty()
color: string;
}
Final result:
app.controller.ts
import {
Body,
Controller,
Delete,
Get,
Param,
Post,
Put,
} from '@nestjs/common';
import { PuppyDTO } from './app.dto';
import { AppService } from './app.service';
@Controller('puppies')
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Post()
registerPuppy(@Body() data: PuppyDTO) {
return this.appService.register(data);
}
@Get('/all')
getPuppies() {
return this.appService.readAll();
}
@Get(':id')
getPuppy(id: string) {
return this.appService.read(id);
}
@Put(':id')
updatePuppy(@Param('id') id: string, @Body() data: Partial<PuppyDTO>) {
return this.appService.update(id, data);
}
@Delete(':id')
deletePuppy(@Param('id') id: string) {
return this.appService.delete(id);
}
}
app.service.ts
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { PuppyDTO } from './app.dto';
import { PuppyEntity } from './app.entity';
@Injectable()
export class AppService {
constructor(
@InjectRepository(PuppyEntity)
private puppyRepository: Repository<PuppyEntity>,
) {}
getHello(): string {
return 'Hello puppies!';
}
async register(data: PuppyDTO): Promise<PuppyDTO> {
const puppy = await this.puppyRepository.create(data);
await this.puppyRepository.save(puppy);
return puppy;
}
async read(id: string): Promise<PuppyDTO> {
const puppy = await this.puppyRepository.findOne({
where: {
id,
},
});
if (!puppy) {
throw new HttpException('Puppy not found', HttpStatus.NOT_FOUND);
}
return puppy;
}
async readAll(): Promise<PuppyDTO[]> {
const puppies = await this.puppyRepository.find({});
return puppies;
}
async update(id: string, data: Partial<PuppyDTO>): Promise<PuppyDTO> {
let puppy = await this.puppyRepository.findOne({
where: {
id,
},
});
if (!puppy) {
throw new HttpException('Puppy not found', HttpStatus.NOT_FOUND);
}
await this.puppyRepository.update(id, data);
puppy = await this.puppyRepository.findOne({
where: {
id,
},
});
return puppy;
}
async delete(id: string) {
const puppy = await this.puppyRepository.findOne({
where: {
id,
},
});
if (!puppy) {
throw new HttpException('Puppy not found', HttpStatus.NOT_FOUND);
}
await this.puppyRepository.delete({ id });
return puppy;
}
}
Test all endpoints using postman
Homepage
localhost:7890/
POST Create profile for a puppy
localhost:7890/puppies
GET All puppies
localhost:7890/puppies/all
GET single puppy
localhost:7890/puppies/:id
DELETE puppy profile
localhost:7890/puppies/:id
UPDATE puppy profile
localhost:7890/puppies/:id
Conclusion:
I hope this was helpful in starting out with NestJs. Thanks for reading.
Top comments (6)
Glad it was helpful
@amrelmohamady 😃😃😃😃 okay i will do for cats too
@andrewbaisden... You are very correct and thanks for the contribution
Cool tutorial I like how easy it is to build an API using NestJS. Most of the setup is done for you whereas when using Express you have to create the whole project architecture from scratch.
You can't do this Nestjs is for cats only no puppies!