DEV Community

Fernando
Fernando

Posted on • Edited on

Learning to build an API in NestJS (Node + Typescript) - Part 02

Intro

If you have been following this series, skip the intro!

This is the second part of a series about learning NestJs for people who have experience building web applications but in languages other than typescript.

The structure of these articles is not scripted 👮 neither random 😕. My goal is to lay out everything I learn with as much depth as I can understand. I try to build working code that I understand and can replicate in upcoming real projects. That's why I don't write about any foo bar examples.

This is my learning process 📚... hopefully it will serve you as well.

The articles are written at the same time as I code and read the docs for what I am trying to build. After the draft is completed, I revise it, edit it, and reorder stuff to make reading this article easier for you and me in the future.

Recap

At this point, we managed to code a module composed of a controller which returns a simple string when requesting its path.
Now we could start interacting with our endpoints, maybe adding some POST, PUT and DELETE requests. For that, we should probably know how to manage data. The concepts of imports and providers remain unexplored and seem to be what we need.

Let's dive into the documentation for providers.

Providers

Right off the bat, the analogy with service injection is made. So, we could think of providers as services that will be injected into other parts of the app, such as modules or controllers, or even other services?

Let's make a service for our controller to consume it. The database part will come later. For now, focus on getting a service up and running.

Again, a decorator is needed to make our service an injectable entity.
These four methods seem reasonable to develop:

import { Injectable } from '@nestjs/common';

@Injectable()
export default class ExampleService {
  get(id: string) {
    return 'Hello';
  }

  getAll() {
    return ['Hello'];
  }

  create(entity: string) {
    return 'Hello';
  }

  delete(id: string) {
    return 'Hello';
  }

  update(entity: string) {
    return 'Hello';
  }
}
Enter fullscreen mode Exit fullscreen mode

Now inject it into the module! 😎
After this step, the service should be accessible at runtime by our controller.

import { Module } from '@nestjs/common';
import ExampleController from './example.controller';
import ExampleService from './example.service';

@Module({
  controllers: [ExampleController],
  providers: [ExampleService],
  imports: [],
  exports: [],
})
export default class ExampleModule {}
Enter fullscreen mode Exit fullscreen mode

In the controller, let's make up some endpoints and call our newly created service. The injection is realized by adding the service as a parameter to the controller constructor.

import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
} from '@nestjs/common';
import ExampleService from './example.service';

@Controller('example')
export default class ExampleController {
  constructor(private readonly exampleService: ExampleService) {}

  @Get()
  index() {
    return this.exampleService.getAll();
  }

  @Get(':id')
  details(@Param('id') id: string) {
    return this.exampleService.get(id);
  }

  @Post()
  create(@Body() dto: string) {
    return this.exampleService.create(dto);
  }

  @Put()
  update(@Body() dto: string) {
    return this.exampleService.update(dto);
  }

  @Delete(':id')
  delete(@Param('id') id: string) {
    return this.exampleService.delete(id);
  }
}
Enter fullscreen mode Exit fullscreen mode

When running the app in watch mode, our changes seem to go unnoticed... Where are the routes we just defined!? 😮

Recently added endpoints are missing from the registered routes listing

Imports

It so happens that this is what imports are for. Following the NestJS architecture, we need to import the Module into the main AppModule.

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import ExampleModule from './example/example.module';

@Module({
  imports: [ExampleModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

And just like that... 💥

Endpoint routed successfully

Right now, there is not much more to say about imports. Maybe we will return to this topic in the future.

Where to go now? 😕

I would like to implement something useful. Authentication comes to mind given that almost every application needs it. But I don't want to mess with database access or data encryption for the time being. So I will code a semi-public scheme. No users, for now, just an unguarded endpoint that always returns a valid token and a bunch of real authenticated endpoints properly guarded.

Wrap up

Congratulations if you made it this far! 😄🙌

In this article, we managed to implement a super basic service, inject it into a controller, route endpoints properly, capture params (query parameters, URI embedded parameters, and request body), and import a module to the main module.

That's all for now. I would like to make the next article completely about coding this authentication structure and not mix it up with this one.

Top comments (2)

Collapse
 
paulasantamaria profile image
Paula Santamaría

Always wanted to try Nest.js so I really appreciate your walkthrough! Looking forward for the next post on this series 👏👏

Collapse
 
fekabas profile image
Fernando

Thanks! It's great to hear this article is helpful. More to come. Stay tuned, Paula!