Class 3 (NestJS) (EN)
Class 3 (NestJS) (EN)
A REST API, unlike a web page, will send raw data in the JSON format. It is made of routes, and uses HTTP
verbs (/methods) to process different actions.
Other methods exist, that we won’t use ourselves, that are here to help the browser manage requests
2
API REST
- Server URL
- HTTP method
- Server response
3
API REST
A request will sometimes send data (a response body), and always an HTTP code. Codes go between 100
and 527. Each hundred corresponds to a kind of result
1xx information (ex. processing)
2xx everything went right
3xx request is redirect
4xx user error
5xx server error
Exemples:
200 OK
204 Created
401 Unauthorized
403 Forbidden
404 Not found
418 I’m a teapot
500 Internal server error
4
NestJS
The NestJS, inspired by Angular will allow us to build an API using a modular architecture.
It is based on the express library that allows us to transform our server NodeJS into an HTTP server ready
to receive and respond to requests.
The modular architecture promotes the responsibility separation. Each kind of class has a precise function
and is splitted into its own file. Then, for each functional notion, we create a module that builds every layer
of this feature.
5
NestJS
Preparation:
- Download the m1-api folder
- Run command npm install to install dependencies
- Run command npm run start:dev
- While you’re at it, you can install the dbaeumer.vscode-eslint VSCode plugin that will help us on
code style (we will come back to that later)
The console would look like this.
6
NestJS
Unlike before, we are running a server that won’t stop until a crash happens, or until we manually stop it
with CTRL + C.
We are running the server in “watch mode”: as soon as we save our changes, the server reloads with the
updates.
npm run start:dev allows us to run a script defined in the package (npm run), the name of the script is
start:dev.
7
NestJS
We go back to Postman, where we create a request calling the root of our API, on the port 3001. The port
3001 is defined (in the main.ts file) by an environment variable, which are defined in the .env file. This file,
coupled with the dotenv package, allows us to define environment variables existing only in our application
at runtime.
8
NestJS
- imported modules
- defined controllers
- defined providers (we will come back to that)
Modules are all centralized in the AppModule, that is used to start the server.
They are defined in a <x>.module.ts, and each have their own folder.
You can find the first module in the app.module.ts file (CMD/Win + P in VSCode to search a file by name).
9
NestJS
Controllers are by convention defined in a <x>.controller.ts file. They export a class whose name ends with
Controller.
The @Controller() decorator lets NestJS know that this class is used to define routes.
The @Get() decorator above a function allows me to define a route whose method is GET.
I can give it an argument to add a prefix to my route, or to get variables from the URL.
10
NestJS
For now, we have a controller with no prefix, and a GET route with no prefix, so we can only call
localhost:3001/ (localhost is the local server, your computer)
11
NestJS
For a REST API, convention is that if I touch a resource, books for instance, routes are prefixed by the
pluralized name of the resource, here, /books.
12
NestJS
A variable contained in an URL, represented by :<nom de la variable> is called a parameter. I can get it in
NestJS with the @Param() decorator.
13
NestJS
Exercices
1. Create a new route GET /:id in the AppController that sends back the given ID
2. Create a new controller BookController, declare it in AppModule and implement following routes :
a. GET /books get a list of books
b. GET /books/:id get one book by its ID
c. POST /books create a book
d. PATCH /books/:id update a book
e. DELETE /books/:id delete a book
f. Just return the name of the function on each route to test if they work
All decorators are imported from the @nestjs/common library, you can do CMD / WIN + . so that VSCode
offers you to add the import to your file.
Once the routes created, test them on Postman to make sure they work
14
NestJS
Before proceeding, we are going to create our book module and put our controller in it. We put this module
in a books folder, and we create another level before that with the modules folder.
15
NestJS
Then, we import our new module in the AppModule, so the application can be aware of it. For the import,
VSCode should automatically offer you the right path, don’t write it by hand. We check that the routes still
work with Postman.
16
NestJS
If I want to send data to the server, I first have to type my input using a DTO (Data Transfer Object)
A DTO allows me to receive a bigger payload than a string contained in the URL, while making sure of the
data type sent to me (never trust the external user)
17
NestJS
To use it, the @Body() decorator of @nestjs/common allows me to get the payload in a POST request like
this
18
NestJS
19
NestJS
@IsString() allows me to check the field is a string, if I try to send a number, the server automatically replies
with a 400 (Bad Request) error.
20
NestJS
Many things are possible with validation, the most common are :
21
NestJS
Exercices
1. Create a DTO for authors with a first name and a last name
2. Create a DTO for book creation, with
a. a title field
b. a “publishedYear” field, that is an integer between 1500 and 2024
c. an author field corresponding to the previously created DTO
3. Create another DTO to update a book. I don’t have to update everything at the same time, so all
fields are optional.
4. Get those inputs in creation and update route, and log them or return them to make sure they are
received
22
NestJS
As said before, since the controller only handles input and output, the service will have to handle business
logic.
Let’s create a file book.service.ts, that declares a class BookService. The BookService will then be declared
in the BookModule, in the providers property.
23
NestJS
24
NestJS
The @Injectable() allows NestJS to know that this class can be injected into another.
Once declared in a module, NestJS will instantiate a single instance of our class, that we can use like this :
25
NestJS
Now, the problem we have is that we have DTOs in our service, but they are supposed to be only in the
controller layer. We don’t want that, because the user input might need transformation before being
processed by the service, and it’s the controller’s job to do that.
We are going to define a model file book.model.ts. We will define types just like our DTOs, and those types
will be used in our service.
For models, we usually end their names by Model to get a faster understanding of what they are:
26
NestJS
Exercices
1. Create the BookService and move controller functions so that they only call the service
2. Create BookModel, CreateBookModel and UpdateBookModel models.
a. For UpdateBookModel, use the CreateBookModel type and a native utility type from
TypeScript
3. Use those types in your service
4. In your BookService, add the following line.
5. Really implement all functions, using this array as a database
a. Watch out, this “DB” will be emptied everytime the app reloads
Now that our CRUD is working again, we still have one layer to see: the repository
The job of the repository is to abstract the storage layer of our application.
It is going to give the service the functions it needs to read / write data, while hiding the exact
implementation of those functions.
Whether our database is SQL, noSQL or something else, the service should not know.
The final goal is to split each layer this way is about code reusability.
If I switch from SQL to noSQL, I only change code in the repository.
If I switch from REST to GraphQL or SOAP, I only change code in the controller.
28
NestJS
Exercices
1. Create a BookRepository class in a book.repository.ts file.
2. Inject it into the BookService
a. Use the @Injectable() decorator
b. Declare the repository in the providers of our module
3. Fully move the “database” implementation in the BookRepository
29