NestJs: 🐶 Puppies Api
Tags: node, psql, programming, javascript
Published: 2022-05-21 11:30:25

_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](https://github.com/drsimplegraffiti/puupies-api).

> To add swagger docs and deploy to heroku [check here](https://dev.to/drsimplegraffiti/nestjs-deploy-to-heroku-and-add-swagger-docs-2292)
>

---

##### 🚀 Quick start:
```
Install node
Install nest cli: `npm i -g @nestjs/cli`
Initialize a nestjs project `nest new project-name`
```


<br />


##### 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](https://media.giphy.com/media/qPuhFBQt8xLEY/giphy.gif)

---

#### Let's start by creating our project. I will call it puppies.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fj2kphvhwqcwpnim4497.png)

On successful pull, you get:
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hp384hz8leqo1lf88gad.png)

---

Let's change into the directory to run the puppies app
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8xsxkbt751d4gfxeo5p0.PNG)

Lets see the folder structure that came preinstalled with NestJs 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0lyne1wqj9zce23usbvd.PNG)


To start the app, run `yarn start:dev`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wjrb7xelp9wub60n5xqt.png)
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gg0u5vqg7imq9v2ivebh.png)
   
 ---

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cdnj0pf389mfpr4mr8ky.png)

 ```typescript
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.

```typescript
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.
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4ndf1khdzhsxyknrs9ip.PNG)

Before we go further, let us create our database using the terminal.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qwntbtp1trvfnw8ci9rf.png)

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

 ![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7ud2163jek1qwjoged9k.PNG)
 
Next, we create our `ormconfig.js` file where our database credentials lies

```javascript
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.
```env
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`
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mvwc0tr6b0ec70t65qvm.png)
 

---

#### 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`

```typescript
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
```typescript
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
```typescript
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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0iinr3ljjyjggwoib3a2.png)
 
##### POST `Create profile for a puppy`

> localhost:7890/puppies

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gzbahznjo9s8u2jinar8.png)
 

#### GET `All puppies`

> localhost:7890/puppies/all

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6lbarseqlzxq47o0m6mq.png)
 
---

GET `single puppy`

> localhost:7890/puppies/:id

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2nyyzqe8lx7iktmfkpug.png)
 
---

DELETE `puppy profile`
 
> localhost:7890/puppies/:id

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/axu4zy67ongz4m1dlxlr.png)

---

##### UPDATE `puppy profile`

> localhost:7890/puppies/:id

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/smkpcysu7m41j8oa540d.png)
 



---

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

#### Resources
[Nest js](https://docs.nestjs.com/)