Building A Todo API With NEST-JS And JS Data Structure To Persist Data - 1

7 min read · August 17th, 2019

A colleague showed me the piece of code below and it was difficult to decipher. Thus I couldn’t interpret the code, making it impossible to explain the code to him. As an Engineer, I had to break down the problem which came down to me asking myself “what superset of javascript is this?” and “how do I learn it?”.

After a little research, I discovered it was the TYPESCRIPT…😔. A language I have been trying to do without… with its so-called typed language. However, as an Engineer with a growth mindset, I set out to learn the basics of Typescript before I dive into the documentation of the Nest-js framework.

###BELOW ARE THE PREREQUISITES

  • Knowledge of Typescript
  • Understanding of Node or have created an API with Express js
  • Understanding of Angular is a Plus

###WHAT WE ARE DEVELOPING

  • A Todo List CRUD API

###GETTING STARTED For you to follow along its expected you have

  • Node (at least v6)
  • NPM (at least v5.2) installed on your machine
  • A preferred code editor/IDE (I make use of Vscode.

To confirm you have it installed, type the following in your command line/terminal.

Last login: Tue Aug  6 10:07:57 on console
➜  ~ npm -v 
6.10.2
➜  ~ node -v
v12.1.0
➜  ~ 

###STEP-1 PROJECT SETUP Navigate to your preferred directory for installing the project files

  1. $ npm i -g @nestjs/cli
  2. $ nest new project-name
  3. $ npm i shortid
➜  Documents npm i -g @nestjs/cli
/usr/local/bin/nest -> /usr/local/lib/node_modules/@nestjs/cli/bin/nest.js
+ @nestjs/cli@6.6.3
added 12 packages from 46 contributors, removed 194 packages and updated 33 packages in 29.183s

In this instance, project name will be todo-app

➜  Documents  nest new todo-app    
⚡  We will scaffold your app in a few seconds..

CREATE /todo-app/.prettierrc (51 bytes)
CREATE /todo-app/README.md (3370 bytes)
CREATE /todo-app/nest-cli.json (84 bytes)
CREATE /todo-app/nodemon-debug.json (163 bytes)
CREATE /todo-app/nodemon.json (67 bytes)
CREATE /todo-app/package.json (1804 bytes)
CREATE /todo-app/tsconfig.build.json (97 bytes)
CREATE /todo-app/tsconfig.json (325 bytes)
CREATE /todo-app/tslint.json (426 bytes)
CREATE /todo-app/src/app.controller.spec.ts (617 bytes)
CREATE /todo-app/src/app.controller.ts (274 bytes)
CREATE /todo-app/src/app.module.ts (249 bytes)
CREATE /todo-app/src/app.service.ts (142 bytes)
CREATE /todo-app/src/main.ts (208 bytes)
CREATE /todo-app/test/app.e2e-spec.ts (561 bytes)
CREATE /todo-app/test/jest-e2e.json (183 bytes)

? Which package manager would you ❤️  to use? npm
✔ Installation in progress... ☕

🚀  Successfully created project todo-app
👉  Get started with the following commands:

$ cd todo-app
$ npm run start                         
       Thanks for installing Nest 🙏
                                         
➜  Documents npm i shortid

So we installed 2 packages.

The first package has two steps. The first step is to setup Nest-js cli (Command Line Interface). It gives users a massive head start when building a Nest-js app, it saves a user from time-consuming setup and configuration.

The second stage is using the installed Nest-js cli to create a new app called todo-app.

Finally, the package called shortid helps generate a random id. if all packages have been successfully installed, the folder structure should be similar to the image shown below. PS😉: check the package.json file to see the shortid version installed.

We will be creating additional folder and files to the ones shown above. Create a folder called Todo in the src folder and create these set of files.

  1. create-todo.dto.ts
  2. todos.controllers.ts
  3. todos.module.ts
  4. todos.service.ts

A controller’s purpose is to receive incoming requests for the application. The routing mechanism controls which controller receives which requests and helps to return a response.

A module is a class annotated with a @Module() decorator. The @Module() decorator provides metadata that Nest-js makes use of to organize the application structure. It also houses a controller, import and service which is then bundled and parsed along.

Service is similar to a helper function that can perform specific tasks. An example is reaching out to a database, so as to make the controller code lean.

###STEP-2 SERVER SETUP Open the command terminal in the current project directory, then $ run npm run start:dev This script is automatically created by Nest-js and its found in the package.json file. The script is ran because we are working locally which is the development env, production has its own script to run.

The App should be running on localhost if all is set up correctly. You should be greeted with a Hello World on your browser.

➜  Documents npm run start:dev
npm ERR! path /Users/openwell/Documents/package.json
npm ERR! code ENOENT
npm ERR! errno -2
npm ERR! syscall open
npm ERR! enoent ENOENT: no such file or directory, open '/Users/openwell/Documents/package.json'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/openwell/.npm/_logs/2019-08-09T17_29_28_506Z-debug.log

If the above message pops up, you are not in the right directory.

###STEP-3 SETTING UP CONTROLLER IN TODO FOLDER

import {Controller,Get,Body,Post,Param,Delete,Patch} from '@nestjs/common';
import { TodoService } from './todos.service';

@Controller('todos')
export class TodoController {
  constructor(private readonly todoService: TodoService) {}

  @Get()
  getAllTodos(): any {
    return this.todoService.findAll();
  }

  @Post()
  addTodo(
    @Body('title') todoTitle: string,
    @Body('description') todoDescription: string,
  ): any {
    return this.todoService.create(todoTitle, todoDescription);
  }

  @Get(':id')
  getTodoById(@Param('id') todoId: string): any {
    return this.todoService.findOne(todoId);
  }

  @Delete(':id')
  deleteTodoById(@Param('id') todoId: string): any {
    return this.todoService.deleteById(todoId);
  }

  @Patch(':id')
  updateTodoById(
    @Param('id') todoId: string,
    @Body('title') todoTitle: string,
    @Body('description') todoDescription: string,
  ): any {
    return this.todoService.UpdateById(todoId, todoTitle, todoDescription);
  }
}

Nest-js has done the heavy lifting for us, by providing @nestjs/common that has many decorators within itself. All we need is to import @Get as done above to create a get route, which applies to all other Http methods. If you observe the @controller decorator has ‘todos’ in it. which translate to localhost:3000/todos like we have in a normal Express-js app. All Http methods in the controller will be inheriting from it. Meaning the post or delete will be translated toPOST http://localhost:3000/todos DELETE http://localhost:3000/todos

In the TodoController class, we have Constructor that expects a todoService of type TodoService which we imported.

We made use of some other decorators which gives us access req.body/@Body('') or req.params/@Param(''). For the @Param to work we need to add it to the route by using ‘:id’ inside the desired method.

The rest of the function just returns the output from calling the respective services.

By Default a post route return 201 status code while others return 200. To change the default code all you need to do is make use of the @HttpCode(200) decorator. It also detects the kind of data send out and based on that set the header to suit it. To set your preferred header, make use of @Header('Content-Type', 'application/json') decorator for example.

###STEP-4 SETTING UP SERVICE IN TODO FOLDER

import { Injectable, NotFoundException } from '@nestjs/common';
import { Todo } from './create-todo.dto';
import { generate } from 'shortid';
@Injectable()
export class TodoService {
  private todosDb: Todo[] = [];
  findAll(): any {
    return [...this.todosDb];
  }
  create(todoTitle: string, todoDescription: string): any {
    const id = generate();
    const newTodo = new Todo(id, todoTitle, todoDescription);
    this.todosDb = this.todosDb.concat(newTodo);
    return newTodo;
  }
  findOne(id: string): any {
    const todoIndex = this.todosDb.find(elem => elem.id === id);
    if (todoIndex === undefined) {
      throw new NotFoundException();
    }
    return todoIndex;
  }
  deleteById(id: string): any {
    const index = this.todosDb.findIndex(elem => elem.id === id);
    if (index === -1) {
      throw new NotFoundException();
    }
    this.todosDb.splice(index);
    return { message: 'Todo Deleted' };
  }
  UpdateById(id: string, todoTitle: string, todoDescription: string): any {
    const index = this.todosDb.findIndex(elem => elem.id === id);
    if (index === -1) {
      throw new NotFoundException();
    }
    const singleTodo = this.todosDb[index];
    if (todoTitle) {
      singleTodo.todoTitle = todoTitle;
    } else if (todoDescription) {
      singleTodo.todoDescription = todoDescription;
    }
    this.todosDb[index] = singleTodo
    return { message: 'Todo Updated' };
  }
}

We imported the Todo which is DTO (Data Transfer Object) schema for the todosDb Array and generate for the shortid package.

We created a TodoService class which was exported with several methods handling the data passed to it. Something to point out is the NotFoundException(), its a method which helps in error handling, it throws 404 automatically and a custom message can be passed into it. The rest of the methods are self-explanatory.

###STEP-5 SETTING UP CREATE-TODO-DTO FILE IN TODO FOLDER

export class Todo {
  constructor(
    public id: string,
    public todoTitle: string,
    public todoDescription: string,
  ) {}
}

The file export a class Todo with the expected @body input types

###STEP-6 SETTING UP MODULE IN TODO FOLDER

import { Module } from '@nestjs/common';
import { TodoController } from './todos.controllers';
import { TodoService } from './todos.service';

@Module({
  controllers: [TodoController],
  providers: [TodoService],
})
export class TodosModule {}

The module exports the compliation of all codes to a format understood by Nest-js.

Before we wrap up, it is necessary to import the module in the Todo folder into the main module.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodosModule } from './Todo/todos.module';

@Module({
  imports: [TodosModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

###STEP-7 TESTING OF OUR TODO API WITH REST CLIENT(VSCODE EXTENSION) OR POSTMAN.

I will be making use of Rest Client in this tutorial.

Create a http.http file in the main directory and add it to .gitignore.

### 
GET http://localhost:3000 HTTP/1.1

### 
GET http://localhost:3000/todos HTTP/1.1

### 
POST http://localhost:3000/todos HTTP/1.1
  content-type: application/json 

  {
    "title": "Wednesday Blog",
    "description": "I lost my dinner to rodent"
  }

### 
GET http://localhost:3000/todos/TGj-TyeBk HTTP/1.1

### 
DELETE  http://localhost:3000/todos/Tg3zyAxj_ HTTP/1.1

### 
PATCH   http://localhost:3000/todos/46szeJhw HTTP/1.1
 content-type: application/json 

  {
    "title": "Monday Blog"
  }

If you are using Vscode, you will see a send request directly above Get request or any of the Http method created kindly click for the desired route. For those that will be making use of Post-Man, this is self-explanatory, select the right method postman and insert the respective URL. For post and patch send the body in raw and JSON(application/json) or any other format suitable.

Kindly restart the server with npm run start:dev.

###CONCLUSION

In this tutorial, we’ve learned how to setup Nest-js Todo server.

Many thanks to Azeez Olasoji for helping out in editing this. Thank you. Thank you.

You can find the complete source code for this project here on GitHub. For more information about Nest-js, you can visit the official documentation.

© 2019–2023 Copyright Timileyin Ojo. All rights reserved.

This site is built with Gatsby and hosted on Netlify . The source code is hosted on Github .