NestJs: ๐Ÿถ Puppies Api

NestJs: ๐Ÿถ Puppies Api

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.


๐Ÿš€ 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

puppies


Let's start by creating our project. I will call it puppies.

Image description

On successful pull, you get: Image description


Let's change into the directory to run the puppies app Image description

Lets see the folder structure that came preinstalled with NestJs Image description

To start the app, run yarn start:dev Image description 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: Image description


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. Image description

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. Image description

Before we go further, let us create our database using the terminal. Image description

Install the pg, the non-blocking PostgreSQL client for Node.js.

Image description

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.

Image description


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/

Image description

POST Create profile for a puppy

localhost:7890/puppies

Image description

GET All puppies

localhost:7890/puppies/all

Image description


GET single puppy

localhost:7890/puppies/:id

Image description


DELETE puppy profile

localhost:7890/puppies/:id

Image description


UPDATE puppy profile

localhost:7890/puppies/:id

Image description


Conclusion:

I hope this was helpful in starting out with NestJs. Thanks for reading.

Resources

Nest js

ย