---
title: "Developing a Secure API with NestJS: Creating Endpoints"
description: "Learn how to use NestJS, a Node.js framework powered by TypeScript, to build a secure API."
authors:
  - name: "Dan Arias"
    url: "https://auth0.com/blog/authors/dan-arias/"
date: "Sep 1, 2020"
category: "Developers,Deep Dive,Nest"
tags: ["typescript", "angular", "nest", "auth0", "full-stack", "frontend", "backend"]
url: "https://auth0.com/blog/developing-a-secure-api-with-nestjs-creating-endpoints/"
---

# Developing a Secure API with NestJS: Creating Endpoints



<details>
<summary>Are you starting from this chapter?</summary>

Clone the application repo and check out the `creating-data-models-and-service` branch:

``` bash  
git clone git@github.com:auth0-blog/wab-menu-api-nestjs.git \
nest-restaurant-api \
--branch creating-data-models-and-service
```

Make the project folder your current directory:

``` bash  
cd nest-restaurant-api
```

Then, install the project dependencies:

``` bash  
npm i
```

Finally, create a `.env` hidden file:

``` bash  
touch .env
```

Populate `.env` with this:

``` bash  
PORT=7000
```

</details>

## Define the Endpoints

For this application, you'll create endpoints to access an `items` resource to perform read, write, update, and delete operations on menu items:

``` bash  
# get all items
GET items/
# get a single item using an id parameter
GET items/:id
# create an item
POST items/
# update an item
PUT items/
# delete an item using an id parameter
DELETE items/:id
```

Only the `GET` endpoints will be public, so let's start with those.

## Create a NestJS Controller

To create an `ItemsController`, execute the following command:

``` bash  
nest generate controller items --no-spec
```

Once executed, this command creates an `items.controller.ts` file under the `src/items` directory. The CLI automatically registers `ItemsController` as a controller of `ItemsModule` for you.

``` typescript  
// src/items/items.module.ts

import { Module } from '@nestjs/common';
import { ItemsService } from './items.service';
import { ItemsController } from './items.controller';

@Module({
  providers: [ItemsService],
  controllers: [ItemsController],
})
export class ItemsModule {}
```

Your application will only recognize `controllers` registered in the metadata object that the `@Module` decorator receives as argument.

Following the architectural conventions of other frameworks, [NestJS controllers](https://docs.nestjs.com/controllers) are responsible for mapping endpoints to functionality. Controllers are created using a class and a decorator as follows:

``` typescript  
@Controller('path')
export class NameController {}
```

The `@Controller` decorator takes as argument the resource path that it will handle.

Populate `src/items/items.controller.ts` as follows to create all of your application endpoints:

``` typescript  
import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Put,
} from '@nestjs/common';
import { ItemsService } from './items.service';
import { Items } from '../items';
import { Item } from '../item';

@Controller('items')
export class ItemsController {
  constructor(private readonly itemsService: ItemsService) {}

  @Get()
  async findAll(): Promise<Items> {
    return this.itemsService.findAll();
  }

  @Get(':id')
  async find(@Param('id') id: number): Promise<Item> {
    return this.itemsService.find(id);
  }

  @Post()
  async create(@Body('item') item: Item): Promise<void> {
    this.itemsService.create(item);
  }

  @Put()
  async update(@Body('item') item: Item): Promise<void> {
    this.itemsService.update(item);
  }

  @Delete(':id')
  async delete(@Param('id') id: number): Promise<void> {
    this.itemsService.delete(id);
  }
}
```

Let's explore in detail what's happening within `ItemsController`.

All of your endpoints share the same resource path: `items/`. You pass the path _without_ the trailing forward slash to the `@Controller` decorator as argument.

You import the `Item` and `Items` classes to add typings to your controller handlers as needed.

To handle specific HTTP requests that use the controller's path, like `GET` or `POST`, you create class methods for each one of them. These methods are called _route handlers_ and use HTTP request method decorators, like `@Get` or `@Post`, that match the type of request they handle:

``` typescript  
  @Post()
  async create(@Body('item') item: Item): Promise<void> {
    this.itemsService.create(item);
  }
```

The function name of the route handler is not relevant to how your API is consumed by clients, but it helps you to easily identify the method for code maintenance and debugging. 

Notice that the `findAll` handler is an `async` function. Why's that?

``` typescript  
  @Get()
  async findAll(): Promise<Items> {
    return this.itemsService.findAll();
  }
```

As mentioned in the [_Asynchronicity_ document](https://docs.nestjs.com/controllers#asynchronicity), data extraction is mostly asynchronous. For this reason, NestJS supports modern `async` functions, which must return a `Promise`. As such, you can return deferred values that NestJS will be able to resolve by itself.

In order to keep `ItemsController` lightweight, you delegate the business logic of each endpoint to methods from `ItemsService`. To access this service within your controller class, you inject an instance of the `ItemsService` class into it through its class constructor. The service instance is marked `private readonly` to make this instance unchangeable and only visible inside the controller class.

``` typescript  
constructor(private readonly itemsService: ItemsService) { }
```

## Make API Requests

With the service and controller in place, you can start the API and make a request to `GET items/`:

``` bash  
# start the development server
npm run start:dev

# make a request to GET items/
curl http://localhost:7000/items/
```

If the app is working as expected, you get an object containing items data as response. The `curl` command works on both macOS and Linux terminal apps as well as on Windows PowerShell.

> Developing on Windows? Learn more about [Windows PowerShell Commands for Web Developers](https://auth0.com/blog/powershell-commands-for-web-developers/#Marking-Server-Requests).

When handling requests that have a payload, you need to extract the payload data from the `body` property of the `request` object that the route handler receives. With that goal in mind, you use the `@Body()` parameter decorator to pluck properties from the `body` property. `@Body()` receives the name of the property that you want to pluck as an argument; in this case, you want the `item` property:

``` typescript  
@Post()
create(@Body('item') item: Item) {
  this.itemsService.create(item);
}
```

The argument passed to the `@Controller()` decorator defines the route path prefix, which is a path shared by all the endpoints defined within the controller. You can then create different path branches by using _route parameters_, such as `:id`. This allows you to create two distinct `GET` endpoints within the same controller:

``` bash  
GET items/
GET items/:id
```

You then pass the route parameter, `:id`, as an argument to the HTTP request decorators `@Get()`, `@Put()`, and `@Delete()` to define dynamic paths. Consequently, this route parameter lets you update, delete, and retrieve a single record.

Using the `@Param()` decorator, you can pluck properties from the `params` property of the `request` object and access the value of `id`:

``` typescript  
@Get(':id')
async find(@Param('id') id: number): Promise<Item> {
  return this.itemsService.find(id);
}
```

> `@Params()` and `@Body()` are both decorators that extract data from the `request` object.

Test the `POST items/` endpoint by executing the following command:

``` bash  
curl -X POST -H 'Content-Type: application/json' -d '{
  "item": {
    "name": "Salad",
    "price": 4.99,
    "description": "Fresh",
    "image": "https://cdn.auth0.com/blog/whatabyte/salad-sm.png"
  }
}' http://localhost:7000/items -i
```

> The `-i` flag includes protocol headers in the output.

You should get `HTTP/1.1 201 Created` as response.

Verify that an item named "Salad" has been added to the store:

``` bash  
curl http://localhost:7000/items -i
```

## Enable Data Validation in NestJS

As explained before, using classes to create custom types is recommended since they are persisted as JavaScript entities after TypeScript transpilation. During development, TypeScript's type system will help you catch errors and debug your code. However, during production, your API will face unpredictable request payloads. You can mitigate this point of failure by validating the data it receives as payload.

To validate incoming requests, you can add validators to your `Item` class properties and execute them by using the built-in NestJS validation pipe.

As it is, the application won't return an error status if you `POST` invalid data. For example, the following request should return a [`400 Bad Request`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) response status; but instead, it returns a [`201 Created`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201) response status:

``` bash  
curl -X POST -H 'Content-Type: application/json' -d '{
  "item": {
    "name": 1.99,
    "price": "Salad",
    "description": "Fresh",
    "image": "https://cdn.auth0.com/blog/whatabyte/salad-sm.png"
  }
}' http://localhost:7000/items -i
```

_Output_:

``` bash  
HTTP/1.1 201 Created
X-Powered-By: Express
Date: Fri, 20 Sep 2019 23:48:46 GMT
Connection: keep-alive
Content-Length: 0
```

To solve this problem, you'll set up your app to automatically validate all incoming requests using the built-in `ValidatorPipe`. In NestJS, a pipe is a class used to transform input data to a desired output or to validate input data.

To enable auto-validation, install the following NPM packages that are leveraged by `ValidationPipe`:

``` bash  
npm install class-validator class-transformer
```

[`class-validator`](https://github.com/typestack/class-validator) makes validation easy by using TypeScript decorators. [`class-transformer`](https://github.com/typestack/class-transformer) offers proper decorator-based transformation, serialization, and deserialization of plain JavaScript objects to class constructors.

Once that's installed, update `src/main.ts` as follows:

``` typescript  
// src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';
import { ValidationPipe } from '@nestjs/common';

dotenv.config();

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(new ValidationPipe());
  await app.listen(process.env.PORT);
}
bootstrap();
```

Next, you need to add validation decorators to your `Item` class. Update `src/item.ts` like so:

``` typescript  
// src/item.ts

import { IsString, IsNumber, IsOptional } from 'class-validator';

export class Item {
  @IsNumber() @IsOptional() readonly id: number;
  @IsString() readonly name: string;
  @IsNumber() readonly price: number;
  @IsString() readonly description: string;
  @IsString() readonly image: string;
}
```

In essence, since you expect the payload to your endpoints to have the same structure of an `Item` object, the `Item` class is doubling as a type class and [a data transfer object (DTO) class](https://en.wikipedia.org/wiki/Data_transfer_object). As requirements change, you may need to create a separate class to define a data transfer object for a payload that includes more data than what the `Item` class provides. Increasing the data present in the payload is done to reduce the number of method calls needed for your application to fulfill a request.

The `id` property is marked as optional since the `POST items/` endpoint doesn't require an `id` as a route parameter or a payload property. Remember that the `id` of a new `Item` is created automatically by the `create` method of `ItemsService`.

Using this pipe, if the data is invalid, you throw an exception and return an error message to the client.

To test this, issue the following invalid `POST` request:

``` bash  
curl -X POST -H 'Content-Type: application/json' -d '{
  "item": {
    "name": 1.99,
    "price": "Salad",
    "description": "Fresh",
    "image": "https://cdn.auth0.com/blog/whatabyte/salad-sm.png"
  }
}' http://localhost:7000/items -i
```

This time, you do get a `400 Bad Request` response. The `message` property of the response object also reveals the payload type errors:

``` json  
{
  "statusCode": 400,
  "error": "Bad Request",
  "message": [
    {
      "target": {},
      "property": "name",
      "children": [],
      "constraints": {
        "isString": "name must be a string"
      }
    },
    {
      "target": {},
      "property": "price",
      "children": [],
      "constraints": {
        "isNumber": "price must be a number"
      }
    }
  ]
}
```

You can disable detailed validation error messages by passing an options object to the constructor of the `ValidationPipe` pipe if you need to:

``` typescript  
// Extract from src/main.ts

app.useGlobalPipes(
  new ValidationPipe({
    disableErrorMessages: true,
  }),
);
```

With error messages disabled, the response to an invalid payload looks like this:

``` bash  
{"statusCode":400,"error":"Bad Request"}
```

<include src="tutorial/NextButton" nextPart="developing-a-secure-api-with-nestjs-adding-authorization/" completedMessage="I've created endpoints with validation for my NestJS app"/>

<include src="tutorial/IssueButton" communityTopic="developing-a-secure-api-with-nestjs/33026"/>
