Nestjs Restful Crud Api
Nestjs Restful Crud Api
Build a RESTful
CRUD API
by Kerry Ritter
Welcome . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Introduction to NestJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
What is NestJS? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
What is TypeScript? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
What about Express? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
remove() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Ready for integration! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
Hi! I’m Kerry Ritter, the author of this eBook and course author at Ultimate Courses¹. I have been
working in NestJS since it was released and I’m excited to write this eBook to help you get started
on your journey with this fantastic framework.
NestJS has been exploding in popularity since its release in 2017 - and for good reason. NestJS
provides a great way to build server-side Node applications with modern tooling like TypeScript
and the latest ECMAScript. With architecture patterns that will be familiar to developers of many
backgrounds - most notably Angular, Java, and ASP.NET - NestJS is fairly easy to get started and
be productive with.
In this eBook, you will:
Whether you’re new to Node or new to server-side development, this eBook will guide you in
successfully getting started with NestJS and TypeScript.
If by the end of this eBook you’re as excited about NestJS as I am, I highly recommend you pre-order
the NestJS Basics and NestJS Pro courses coming soon at Ultimate Courses².
Thank you for reading and I hope you find this eBook helpful. If you do, let me know in a tweet³!
¹https://www.ultimatecourses.com
²https://www.ultimatecourses.com
³https://www.twitter.com/kerryritter
Introduction to NestJS
What is NestJS?
NestJS is a JavaScript application framework for building efficient, scalable Node.js server-side
applications. It uses progressive JavaScript, is built with and fully supports TypeScript - yet still
enables developers to code in pure JavaScript - and combines elements of OOP (Object Oriented
Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
That’s a technical way of saying that NestJS enables you to build readable, maintainable, and
enterprise-ready applications on the Node platform. For developers in the ASP.NET or Java worlds,
this means you can now write applications in Node using tools and paradigms you might find
quite familiar - tools such as decorator-driven (also known as attribute-driven) controller routing,
inversion-of-control techniques, and MV* application architectures. For developers in the Angular
world, this means you can use techniques you’ve used in your client-side Angular code on the
server-side - techniques such as module containers, injectable services, pipes, guards, and more.
Unlike many other popular Node-based web frameworks, NestJS takes a fairly opinionated approach.
NestJS provides refined, standardized architecture patterns that are supported by the CLI and
encouraged by the framework design. The framework also provides a fair amount of tooling
out of the box: a dependency injection container, end-to-end and unit test suite setups, and
scripts for production builds, a development server, formatting, and linting. While not immediately
packaged with tools for configuration (@nestjs/config) or database interactions (@nestjs/typeorm
or @nestjs/mongoose being the popular two), the modularity pattern built into NestJS makes it very
easy to bolt functionality such as these into your application rather quickly.
What is TypeScript?
As mentioned, NestJS is a JavaScript framework for NodeJS that is built using TypeScript. If you’re
unfamiliar with TypeScript, this can be a confusing statement - how can a framework written in one
language be targeting another language? The explanation is a rather simple premise: TypeScript is
a superset of JavaScript and all TypeScript code is “transpiled” to JavaScript code. Being a “superset
language” means that all JavaScript code is inherently valid TypeScript code - all of the added
syntactical sugar and functionality is optionally added on top of the existing JavaScript language.
Transpilation is the process in which code written in one language is converted or compiled into
another language. For TypeScript, this means the new features are converted into code that runs
in pure JavaScript environments and abstract features like data typing is stripped out of the actual
executable output. Take the following example:
Introduction to NestJS 3
You may notice that in the TypeScript constructor, there are some techniques that you won’t see
in JavaScript code. The most obvious differences are the : string and : number declarations. These
type declarations are a way to tell the transpiler and the intellisense tooling in your code-editor
that you expect these parameters to be of a certain data type. So, if I try to do something like this,
I will get an error. This type mismatch will cause the TypeScript transpilation to fail because the
constructor expects a string and a number, not two strings.
You may also notice that the JavaScript code does not have any reference to input or output
types. This is because typing is only a concept used in TypeScript by the TypeScript compiler and
Introduction to NestJS 4
integrated intellisense tools; JavaScript run-times such as in Node or your browser cannot interpret
this additional syntax, so the compiler removes them.
Similarly, the getDetailOutput() method has a return type declaration of string. This tells the
consumer that this method will return a string, and the transpiler and intellisense tools will respond
accordingly.
In addition to these type declarations, you may have noticed that the readonly modifier in the
constructor. This is a future feature of JavaScript that is not currently available and is another aspect
that makes using TypeScript such an enjoyable experience: TypeScript developers get to use many
of the future JavaScript features today! As you may see in the two code examples, the readonly
modifier is a short-hand way to automatically assign the constructor parameters to class-level
variables on the instance of Dog. This and many other features are immediately available in the
TypeScript language, but not yet available when using pure JavaScript and Node.
TypeScript has a plethora of functionality and features to offer to make your life as a developer easier.
While the examples in this eBook will be written in TypeScript, we will be using the simpler aspects
of the language; a deep understanding of TypeScript is not a prerequisite to being productive in this
eBook! However, if you’d like to master the TypeScript language, Ultimate Courses offers courses
that will take you from beginner to expert⁴.
structured and dependency injection-friendly ways to interact with the Express pipeline, such as by
using abstract base classes for @Injectable middleware and decorators for tools like @Pipes and
@Guards. While the implementation may not look exactly the same, at the end of the day you are
still working with Express under the hood, just in a more opinionated and structured fashion.
Note that Express is the underlying HTTP framework used by NestJS by default: NestJS’s design
allows for changing out what underlying platform is being used to handle web requests. Another
popular framework that is supported by NestJS is Fastify, a library focused on high-performance
web applications. Leveraging NestJS allows you to swap out these underlying platforms with relative
ease!
Review
Hopefully this section has left you with the following take-aways as you begin your journey into
NestJS:
1 node -v
2 npm -v
Once installed, open your command-line tool of choice and verify your installation with this
command to check the version of NestJS. This eBook leverages version 7 of NestJS, but should be
compatible with future major versions.
1 nest -v
nest new
This command kicks off a wizard that will scaffold a new NestJS web application. It automatically
generates your project structure, required NPM dependencies, and provides scripts for building,
linting, formatting, and testing.
nest start
This command handles the transpilation of your TypeScript or JavaScript code and kicks off the
NestJS web server at http://localhost:3000 by default. Note that passing the --watch flag enables
the file watcher that will automatically transpile your code and restart the web server whenever a
file in src changes.
nest generate
This command allows you to generate any of the NestJS elements, such as modules, controllers,
services, pipes, guards, and more. In addition to the element you specified, it will also set up a .spec
test suite file that is immediately ready for you to write your unit tests.
Postman
Postman is a fantastic tool that makes it easy to send web requests to APIs. In this eBook, we will be
making web requests to our NestJS API, and using Postman will make testing these requests much
simpler. The Postman API client can be downloaded at https://www.postman.com/downloads/.
Starting Your Application
Now that you have an idea of what NestJS is all about and you have installed the tools that you
need, let’s get started!
The CLI will generate some files for you and then ask you which package manager you prefer to use.
In this eBook, we will be using npm and recommend you do the same. Once selected, the CLI will
begin installing all of the required dependencies. You can now open the project at todo-list-api in
your code editor of choice; I recommend Visual Studio Code⁵ because of its lightweight nature and
excellent TypeScript support.
This command will transpile your TypeScript code into Node-ready JavaScript code. The output of
this process can be found at dist. These contents will be what you deploy to your server.
⁵https://code.visualstudio.com/
Starting Your Application 9
This useful command leverages the library prettier to format the code to a uniform style guide.
This is a great tool, especially for teams, to create a cohesive and uniform code base. Typically you
will want to run this before each commit or code change.
This command runs the code in the dist folder created by the npm run build command to start the
NestJS web server. This command is typically used on the server where your code is running.
This is similar to the npm run start command, but it uses the --watch flag to enable a file-watcher
that automatically rebuilds your code and restarts the web server. This command is what you will
want to use when developing on your machine.
Application Files
In NestJS applications, all of the application source code lives in the src folder. Opening this folder,
you will see four TypeScript files and one test suite.
main.ts
main.ts is the entry point of your NestJS application; when the nest start is executed, it finds
the main.ts file and executes it. This file is particularly small and straight-forward. Inside of the
bootstrap() method, the NestFactory class generates an INestApplication (the app variable) by
instantiating the AppModule class (outlined below).
Once you have your INestApplication, you can apply global middleware, make global application
changes, and start your process - much like one would do when using Express. In our case, which
is set up by NestJS by default, we want to start the Express web server. This is done with the
app.listen() method. The application will run on whatever port you supply to the listen method
which is 3000 by default.
app.controller.ts
app.controller.spec.ts
This spec file is a test suite for the AppController class. Suites like these are automatically generated
whenever you create elements using the nest generate command and provide a great boilerplate
to get started with your tests.
Starting Your Application 10
app.service.ts
The AppService is a simple NestJS injectable provider. In this default class we return a ‘hello world’
message, but in real-world applications you will see these service classes doing the bulk of the
business logic work.
app.module.ts
The AppModule is your root module. Modules are used as the dependency injection container and
is where all of your controllers and providers will be registered for use. As you will see, the
AppController and AppService classes are registered here for the application to use.
It is common practice to keep this particular AppModule module going forward and import all
subsequent modules in the imports array.
2. In your browser, go to http://localhost:3000. You will be greeted with the “Hello World!”
message.
Congratulations - you ran your first NestJS application! In the next section, we will start extending
this default application with our own routes and services.
Setting up the Todo Module
Understanding NestJS module structure
The NestJS application architecture takes a “per-feature” module approach, akin to what you may
see in the Angular or Python Django worlds. With this design, all of the classes and functional layers
required to make a single feature functional (i.e. CRUD actions for a database model) will be grouped
inside of one folder and injected into a module.
ASP.NET, Java, and other communities often employ a pattern that groups classes by type (i.e.
a Controllers folder, a Services folder, a Models folder). For example, an ASP.NET MVC API
application structure may look like this:
1 Controllers/
2 |--- TodoController.cs
3 Services/
4 |--- TodoService.cs
5 Models/
6 |--- TodoEntity.cs
Following the NestJS structure, your app will instead look like this:
1 todo/
2 |--- todo.controller.ts
3 |--- todo.entity.ts
4 |--- todo.module.ts
5 |--- todo.service.ts
This may be a shift in thinking if you’re accustomed to the former approach, but grouping
functionality per feature can be a great paradigm that keeps related code easy to discover. This
approach makes it rather painless to pick up and move large portions of functionality to new
codebases or into packagable libraries, and also makes easy to split up code responsibilities to teams
or team members.
In the above example, I noted three elements of a NestJS modules that are important to understand
before going forward: modules, controllers, and providers.
Setting up the Todo Module 12
Modules
Modules are where all of the controllers and providers are registered, along with all of the imported
sub-modules which contain their own controllers and providers. Modules hook up the dependency
injection containers and enable the resolving of required dependencies for controllers and providers.
In NestJS, modules are created by decorating a class with the @Module decorator. A simple example
of a Module class would look like:
1 @Module({
2 imports: [DatabaseModule],
3 controllers: [TodoController],
4 providers: [TodoService],
5 exports: [TodoService],
6 })
7 export class TodoModule {}
• imports: Importing other @Module classes allows for using other sub-modules and creating
the hierarchy of functionality. For instance, the AppModule will be importing the TodoModule,
enabling the TodoModule functionality to be enabled at the root of the application. This is also
how third-party modules such as @nestjs/typeorm, @nestjs/config, and others are wired into
your application.
• controllers: The controllers array contains the @Controller classes that are registered to
this module. By defining them in this array, they are made accessible via HTTP requests.
• providers: The providers array contains all @Injectable classes such as data services, instance
factories, pipes, guards, middleware and interceptors.
• exports: This array allows for making providers and imports registered to this module available
for use outside of the current module. For example, a TodoService registered to a TodoModule
is only available classes inside the TodoModule. Once TodoService is added to the exports array,
it is now available to outside modules.
Controllers
Controllers are responsible for interpreting input, passing the request details to services, and then
formatting the service results in a way that can be exposed as responses. In the case of NestJS,
“Controller” specifically refers to a class that handles HTTP requests.
In NestJS, controllers are decorated with the @Controller decorator. By passing a string into the
@Controller argument, we define a base route path for all of the routes inside of the controller. The
routes inside this class are notated with HTTP method decorators and path designations.
It is best practice to keep controllers relatively “thin” and avoid putting significant business logic,
data logic, or otherwise processing logic in your controller route methods: that work belongs in
Setting up the Todo Module 13
a provider service. By keeping the controller’s responsibility clearly defined as input and output
transformation, it becomes easier to write, read, design, and test your code.
A simple example of a Controller class would look like:
1 @Controller('api/app')
2 export class AppController {
3 constructor(private readonly appService: AppService) {}
4
5 @Get('health')
6 getHealth(): HealthCheck {
7 return this.appService.getHealth();
8 }
9 }
Providers
Providers is a blanket term in NestJS that encompasses service classes, repositories, factories, utilities
classes, and anything of that nature. This also includes important NestJS elements like middleware
and guards. These are marked with the @Injectable() decorator.
• todo/
• todo/todo.controller.ts
• todo/todo.interface.ts
• todo/todo.module.ts
• todo/todo.service.ts
1 // todo.interface.ts
2 interface Todo {
3 }
1 // todo.interface.ts
2 interface Todo {
3 id?: number;
4 label: string;
5 complete: boolean;
6 }
Now that we’ve defined the structure, we will need to make this interface available to the other
TypeScript files using the keyword export. Without this keyword, the interface is essentially private
to the file in which it is defined. In the end, your todo.interface.ts file should look as such:
1 // todo.service.ts
2 import { Injectable } from '@nestjs/common';
3
4 @Injectable()
5 export class TodoService {
6 }
In this eBook, we will be using an in-memory array as our storage mechanism. This will not persist
and will be cleared out every time the development server is restarted, but it will serve as a simple
mechanism to start your NestJS journey. To create this array, we will define and instantiate a storage
class-level variable. Once added, your todo.service.ts class should look as such:
Note that NestJS providers are singletons by default and any injection of the TodoService will share
the same in-memory array.
1 // todo.controller.ts
2 import { Controller } from '@nestjs/common';
3
4 @Controller('todo')
5 export class TodoController {
6 }
To leverage the TodoService we created, we will use NestJS’s dependency injection through
constructor injection. Using TypeScript’s constructor assignment feature, we can easily set the
provider as a private, readonly class-level instance property.
Setting up the Todo Module 16
1 // todo.controller.ts
2 import { Controller } from '@nestjs/common';
3 import { TodoService } from './todo.service';
4
5 @Controller('todo')
6 export class TodoController {
7 constructor(private readonly todoService: TodoService) {}
8 }
NestJS will analyze the parameters in the constructor, find the types in the dependency injection
container, and resolve them. We will now be able to use the TodoService in controller methods via
this.todoService.
To validate our module, we will set up a single GET route that returns an empty array (note the
added import). Routes are enabled using HTTP method decorators: @Get, @Post, @Put, @Patch, and
@Delete. Just like the parameter passed to @Controller, the value passed to the method decorator
defines the path of the route. In the case of this GET request, we will leave it as undefined, ultimately
resolving to the base route path of /todo.
1 // todo.module.ts
2 import { Module } from '@nestjs/common';
3
4 @Module({})
5 export class TodoModule {}
Now we can add the TodoService as a Provider to this module to make it available for injection.
1 // todo.module.ts
2 import { Module } from '@nestjs/common';
3 import { TodoService } from './todo.service';
4
5 @Module({
6 providers: [TodoService],
7 })
8 export class TodoModule {}
With the TodoService now registered, we can register the TodoController which will resolve the
service as a dependency.
1 // todo.module.ts
2 import { Module } from '@nestjs/common';
3 import { TodoController } from './todo.controller';
4 import { TodoService } from './todo.service';
5
6 @Module({
7 controllers: [TodoController],
8 providers: [TodoService],
9 })
10 export class TodoModule {}
1 // app.module.ts
2 import { Module } from '@nestjs/common';
3 import { AppController } from './app.controller';
4 import { AppService } from './app.service';
5 import { TodoModule } from './todo/todo.module';
6
7 @Module({
8 imports: [TodoModule],
9 controllers: [AppController],
10 providers: [AppService],
11 })
12 export class AppModule {}
With your server still running, open the Postman application. Make a new request to the TodoController
route at http://localhost:3000/todo. The controller will return a JSON response of an empty array,
which we return in the findAll() method.
Setting up the Todo Module 19
We are now resolving HTTP requests to our TodoController and are ready to start adding in the
CRUD actions to the controller and service.
findAll()
1 // todo.service.ts
2 import { Injectable } from '@nestjs/common';
3 import { Todo } from './todo.interface';
4
5 @Injectable()
6 export class TodoService {
7 private storage: Todo[] = [];
8
9 findAll(): Todo[] {
10 return this.storage;
11 }
12 }
1 // todo.controller.ts
2 import {
3 Controller,
4 Get,
5 } from '@nestjs/common';
6 import { TodoService } from './todo.service';
7 import { Todo } from './todo.interface';
8
9 @Controller('todo')
10 export class TodoController {
11 constructor(private readonly todoService: TodoService) {}
12
13 @Get()
14 findAll(): Todo[] {
15 return this.todoService.findAll();
16 }
17 }
Ensure that the development server is still running in your command-line tool. Open Postman and
make a GET request to http://localhost:3000/todo. In response, we’ll get an empty array (again).
Implementing the Data Service and Controller 22
create()
1 // todo.service.ts
2 import { Injectable } from '@nestjs/common';
3 import { Todo } from './todo.interface';
4
5 @Injectable()
6 export class TodoService {
7 private storage: Todo[] = [];
8
9 create(todo: Todo): void {
10 const currentMaxId = Math.max(...this.storage.map((t: Todo) => t.id));
11 todo.id = currentMaxId + 1;
12 this.storage.push(todo);
13 }
14
15 findAll(): Todo[] {
16 return this.storage;
17 }
18 }
1 // todo.controller.ts
2 import {
3 Body,
4 Controller,
5 Get,
6 Post,
7 } from '@nestjs/common';
8 import { Todo } from './todo.interface';
9 import { TodoService } from './todo.service';
10
11 @Controller('todo')
12 export class TodoController {
13 constructor(private readonly todoService: TodoService) {}
14
15 @Post()
16 create(@Body() todo: Todo): void {
17 return this.todoService.create(todo);
18 }
19
20 @Get()
21 findAll(): Todo[] {
22 return this.todoService.findAll();
23 }
24 }
We can now send a POST request to http://localhost:3000/todo with a JSON payload to add it
to the storage array. In Postman, do the following to set up your POST request:
1 {
2 "label": "Create an awesome API",
3 "complete": false
4 }
Send the request. Our response will be empty, but with a status of 201 Created.
Implementing the Data Service and Controller 24
We can now make a GET request to http://localhost:3000/todo again to see our new Todo item
in our array with a set id of 1.
NestJS provides a Logger implementation that makes it easy to add logging to your application. It
is best practice to use this Logger class over the console log methods because the log messages are
formatted in a cohesive fashion, and the Logger allows you to swap out functionality or disable
logging completely at a global level with simple configuration in your main.ts file.
To add logging to our controller, create a new instance of a Logger as a class-level variable on the
TodoController. We will add a log statement to each of our methods as well.
1 import {
2 Body,
3 Controller,
4 Get,
5 Logger,
6 Post,
7 } from '@nestjs/common';
8 import { Todo } from './todo.interface';
9 import { TodoService } from './todo.service';
10
11 @Controller('todo')
12 export class TodoController {
13 private readonly logger = new Logger(TodoController.name);
14
15 constructor(private readonly todoService: TodoService) {}
16
17 @Post()
18 create(@Body() todo: Todo): void {
19 this.logger.log('Handling create() request...');
20 return this.todoService.create(todo);
21 }
22
23 @Get()
24 findAll(): Todo[] {
25 this.logger.log('Handling findAll() request...');
26 return this.todoService.findAll();
27 }
28 }
Now when we make a request to either method, we will see items in our console such as:
findOne()
1 import {
2 Body,
3 Controller,
4 Get,
5 Logger,
6 Param,
7 Post,
8 } from '@nestjs/common';
9 import { Todo } from './todo.interface';
10 import { TodoService } from './todo.service';
11
12 @Controller('todo')
13 export class TodoController {
14 // ...
15
16 @Get(':id')
17 findOne(@Param('id') id: number): Todo {
18 this.logger.log('Handling findOne() request with id=' + id + '...');
19 return this.todoService.findOne(id);
20 }
21 }
Since the storage array was cleared when we restarted our development server, we will do the
following to test:
1. Send the POST request outlined in the create() section to create a new Todo item.
2. Send a GET request to http://localhost:3000/todo to see all the items in storage, including
your new Todo.
3. Send a GET request to http://localhost:3000/todo/1, our new route, to retrieve the Todo
item directly.
Implementing the Data Service and Controller 28
It is important to understand that by default, @Param values are strings. While we gave it the type
of number, recall that TypeScript types are abstract and have no impact on the actual executing
code; this is to say that the executing code had no way of knowing it should have parsed the route
parameter id to a number. This is where the @Pipe decorator comes in.
1 import {
2 Body,
3 Controller,
4 Get,
5 Logger,
6 Param,
7 ParseIntPipe,
8 Post,
9 } from '@nestjs/common';
10 import { Todo } from './todo.interface';
11 import { TodoService } from './todo.service';
12
13 @Controller('todo')
14 export class TodoController {
15 // ...
16
17 @Get(':id')
Implementing the Data Service and Controller 29
This will parse the value to a number and now our .find() method in our data service will find
an exact equality against the Todo item we POST. Since the storage array was cleared when we
restarted our development server, we will do the following to test:
1. Send the POST request outlined in the create() section to create a new Todo item.
2. Send a GET request to http://localhost:3000/todo to see all the items in storage, including
your new Todo.
3. Send a GET request to http://localhost:3000/todo/1, our new route, to retrieve the Todo
item directly.
With this pipe in place, we’re now able to retrieve the Todo item.
update()
1 // todo.service.ts
2 import { Injectable } from '@nestjs/common';
3 import { Todo } from './todo.interface';
4
5 @Injectable()
6 export class TodoService {
7 // ...
8
9 update(id: number, todo: Todo): void {
10 const index = this.storage.findIndex((t: Todo) => t.id === id);
11 this.storage[index] = todo;
12 }
13 }
1 import {
2 Body,
3 Controller,
4 Get,
5 Logger,
6 Param,
7 ParseIntPipe,
8 Post,
9 Put,
10 } from '@nestjs/common';
11 import { Todo } from './todo.interface';
12 import { TodoService } from './todo.service';
13
14 @Controller('todo')
15 export class TodoController {
16 // ...
17
18 @Put(':id')
19 update(@Param('id', ParseIntPipe) id: number, @Body() todo: Todo): void {
20 this.logger.log('Handling update() request with id=' + id + '...');
21 return this.todoService.update(id, todo);
22 }
23 }
Implementing the Data Service and Controller 31
Since the storage array was cleared when we restarted our development server, we will do the
following to test:
1. Send the POST request outlined in the create() section to create a new Todo item.
2. Send a GET request to http://localhost:3000/todo to see all the items in storage, including
your new Todo.
With the storage array set up, let’s now send a PUT request to update the "complete" flag to true.
1 {
2 "id": 1,
3 "label": "Create an awesome API",
4 "complete": true
5 }
Implementing the Data Service and Controller 32
To validate the value has changed, send a GET request to http://localhost:3000/todo to see all of
the items in the storage array.
remove()
1 // todo.service.ts
2 import { Injectable } from '@nestjs/common';
3 import { Todo } from './todo.interface';
4
5 @Injectable()
6 export class TodoService {
7 // ...
8 remove(id: number): void {
9 const index = this.storage.findIndex((t: Todo) => t.id === id);
10 this.storage.splice(index, 1);
11 }
12 }
As with the GET and PUT routes, we’ll need to make sure ParseIntPipe pipe is in place.
1 import {
2 Body,
3 Controller,
4 Delete,
5 Get,
6 Logger,
7 Param,
8 ParseIntPipe,
9 Post,
10 Put,
11 } from '@nestjs/common';
12 import { Todo } from './todo.interface';
13 import { TodoService } from './todo.service';
14
15 @Controller('todo')
16 export class TodoController {
17 // ...
18
19 @Delete(':id')
20 remove(@Param('id', ParseIntPipe) id: number): void {
21 this.logger.log('Handling remove() request with id=' + id + '...');
22 return this.todoService.remove(id);
23 }
24 }
1. Send the POST request outlined in the create() section to create a new Todo item.
2. Send a GET request to http://localhost:3000/todo to see all the items in storage, including
your new Todo.
3. Send the PUT request outlined in the update() section to set the Todo item to "completed":
true.
4. Send a GET request to http://localhost:3000/todo to see the updated item in storage.
5. Send a DELETE request to http://localhost:3000/todo/1 to remove the item in storage.
6. Send a GET request to http://localhost:3000/todo to see the now emptied storage.
With logging enabled, your command-line console will look something like this:
Implementing the Data Service and Controller 34
Enable CORS
If you check out your browser console, you’ll see some errors about a missing “Access-Control-
Allow-Origin” header. These errors are due to cross-origin reference sharing (CORS) restrictions
that prevent web applications from making XHR requests to other domains. These restrictions are
a security feature browsers have put in place using what are known as pre-flight requests and API
response headers.
Pre-flight requests are OPTION HTTP requests that are executed before the actual XHR request.
Your browser automatically injects this request and if the restrictions specified in the response
headers are met by the request parameters, then the actual request will continue as expected. By
default, these restrictions prevent requests from any other origin; in our case, our server at localhost
is preventing XHR requests from stackblitz.com.
NestJS makes opening these CORS restrictions simple. In your main.ts file, call app.enableCors();
in the bootstrap() method. This will add headers to your controller route responses that enable
cross-domain requests.
1 // main.ts
2 import { NestFactory } from '@nestjs/core';
3 import { AppModule } from './app.module';
4
5 async function bootstrap() {
6 const app = await NestFactory.create(AppModule);
7 app.enableCors();
8 await app.listen(3000);
9 }
10 bootstrap();
Integrating with a User Interface 36
Summary
This example is a great demonstration of building loosely-coupled REST APIs and user interfaces. By
building REST APIs with standardized route patterns and clearly indicating data transfer contracts,
we are able to separate our concerns between back-end processing and front-end user interactions
in a way that is maintainable and scalable.
This pattern can be leveraged to create very stable and maintainable applications, from small
applications to large products. Creating a full-stack TypeScript application with NestJS and the front-
end framework of your choice is a recipe for success.
Integrating with a User Interface 37
Through this eBook, I hope to have demonstrated how the NestJS framework makes it easy to
create server-side codebases that scale. Where do you go from here? I recommend you pre-order
the NestJS Basics and NestJS Pro courses coming soon at Ultimate Courses⁶, which will teach you
all the fundamentals, how to get started with database management, how to build more complex
REST APIs, and how to fully master the NestJS framework.
⁶https://ultimatecourses.com