
In this presentation, we will talk about the problem to be addressed by this course, its purpose, and finally its structure. We'll also see its contents and the prerequisites.
Here, the tools and software necessary to proceed with the course will be presented.
If node is not being recognized in the terminal, you may need to set the corresponding environment variable on Windows. A post appended to this lesson may help. Citing the author of the main answer:
Open Control Panel - System and Security - System - Advanced System Settings - Environment Variables
In "User variables" or "System variables" find variable PATH and add node.js folder path as value. Usually it is C:\Program Files\nodejs;. If variable doesn't exists, create it.
Restart your IDE or computer.
The Extensions for VSCode being used are:
ESLint
Prettier - Code formatter
Docker
DotENV
GitLens - Git supercharged
IntelliCode
Material Icon Theme (optional cosmetic)
Prisma (for the Parallel module)
Just in case you already developed the project using Nest v9, in this video we'll learn how to upgrade it to v10 and also update the other dependencies, using the ncu package. Some files will also be updated, according to the v10 template.
Finally, in case you were using LF line break on Windows, here we'll learn how to switch it back to CRLF, as now the Nest CLI generates files with line break depending on the OS.
Here, we'll upgrade the NestJS version from v10 to v11, while updating other packages, as usual. The project's templates will also be updated to match this new version.
To actually begin the course, the project will be created using the Nest CLI. Due to the scaffolding, much of the heavy lifting will be done for us.
Before starting the actual business logic, let's apply some configurations to have an overall better development experience (DX).
What will be achieved with this lesson:
Format code/Organize imports automatically on save
Use the baseUrl and paths options in TSConfig to have shorter paths
Adjust some options related to type safety strictness
Delete app.* generated files that won't be used (cleanup)
Using a Nest schematic, let's generate the resource for the first entity - User.
A resource in NestJS includes:
The entity class
DTOs for creating and updating an entity
A module to encapsulate this context
A controller with the basic routes for this entity
A service with the logic for the routes
Automated tests
Right now, our focus will be on the entity. In later lessons, we'll develop these other elements.
We shall discuss a bit about the topic of the next few upcoming lessons: Validation. It is important to avoid badly formatted data or malicious data from accessing the system. We'll also use this opportunity to set some configurations in Insomnia, for better ergonomy once again.
Let's begin with a very basic validation, so that not absolutely everything is accepted anymore.
We could have stricter validation on some of the fields, as only requiring them to be strings is too lenient. Luckily, Class Validator has several decorators for fine-tuning our validation.
Through some options that can be set in the Validation Pipe, we can avoid accepting strange fields not present in the DTO definition. We can also, through another option, automatically have an instance of the DTO from the incoming JSON.
A brief discussion about another element that could be validated, the id. This case is a bit different, because the id arrives as a Path Parameter.
We also use this opportunity to set the requests of the remaining users' routes in Insomnia.
The id is a path parameter, therefore it comes over the network as a string, even though it should be a number. Let's learn how to validate it using a DTO.
We'll also learn how to transform it to number before validating it.
Just a minor change, the Validation Pipe options will be put in another file for better organization, and typed accordingly.
A database will be created so that we may soon have real functionalities for the user entity. This will be done using Docker, which is very practical and quick.
In the next few upcoming lessons, we'll integrate our system with TypeORM, so that we may communicate with the database. TypeORM is considered one of the most mature ORMs in the Node.js ecosystem, so it will be used throughout the course.
Prisma is another ORM that is algo gaining high popularity due to its fame as a next-generation ORM. If you are interested in learning more about it, there will be a module focused on it.
The first step is to connect our system with the database. Fortunately, Nest eases its integration with TypeORM and many other popular libraries through specialized modules.
The entity must be mapped to the database so that we can generate a table for it through our code and, soon, perform operations with the records.
After mapping the entity, we can apply this mapping to the database through the usage of migrations. They are a versioning system for changes made to the database schema, kind of like Git.
We can now have actual interactions with the database by using a TypeORM repository. After being injected, it becomes an interface for performing database operations, in our case the CRUD operations of User.
Still, we check manually if the user exists when searching for it. This may quickly become repetitive and error-prone. Apart from that, If we try to create a user with an existing email for example, we receive a generic exception instead of an adequate one. This will be improved in the Extra module 2 - Exception Filters.
We have to be careful when fetching all the entities at once, as they may be too numerous. It may be a nice idea to limit this quantity somehow. In this lesson, we'll use offset-based pagination in order to limit the results. It is simpler and well suited here, due to the way we naturally see results in a shop: flipping pages, like a book.
While doing so, we also learn to have better type safety in the default page sizes object using the satisfies keyword from TypeScript. With it, this object is only allowed numeric fields.
The topic of pagination, alongside filtering and sorting, will be better explored in the Extra module 5 - Advanced Querying.
We used and will continue to use DTOs extensively to validate incoming data, and they are indeed very useful, but not bulletproof. Here we discuss a bit about their limitations.
One way to avoid repetition in DTOs is to combine many decorators into one when their combined use is frequent. We can achieve that with a composite decorator.
In addition, we'll also improve the decorator by:
Using a JSDoc to offer a brief description
Tidying up the tooltip by expliciting the return type
Using Validation Options for better versatility (Validate an array, for example)
In the next few upcoming lessons, we'll learn how to deal with data that is sensitive and changes according to context.
We are exposing sensitive data inside some files, and sometimes even duplicating this data. But it does not belong here really, as it is bound to the machine where the system is running. Therefore, this cannot stay in the codebase. It will be relocated as environment variables.
Later, we'll learn how to have a bit more of type safety with these variables by using Configuration namespaces and Partial registration.
The environment variables are related to the machine where the system is running, not to the codebase itself. Thus, they are a great candidate for the task.
We still cannot use the environment variables. They need to be made available to the codebase. Also, variable expansion is not natively supported. Lastly, it would also be interesting to validate them before system startup.
Fortunately, NestJS comes with the ConfigModule, which eases the solution to these situations. Let's then use it to progress nicely.
Now these variables can finally be used, and this kind of data will no longer be visible explicitly in the code itself. It will also be dependent on the machine that runs the system. Let's then learn how to use these variables.
We can have more type safety when managing the environment variables, and only make them available where they will actually be used, allowing for better encapsulation. Let's learn how to achieve this.
This is just a bit less verbose way to achieve the same result of the previous lesson. With this solution, we just need to insert the other necessary data in the namespace. And then, the injection process becomes much simpler.
In order to have an environment variable for the app port, we cannot use a configuration namespace, as the main file is not in the context of a module. Still, we can use here the ConfigService. It's not as robust as the former, but it will suffice in this case.
We are generating the database url in the .env file itself, as an alternative we'll learn how to create it inside the configuration namespace. This allows for:
Removing this env var from the .env file
Easier deployment phase
Going back to the business logic itself, we now implement the Order entity. We also start learning about relations.
Topics that are introduced in this lesson are:
Enums (And mapping them to the entity)
Relations (One To Many)
Now for the Payment entity. This entity will not be generated with CRUD entry points, as it will have only a single route. This will also be a very simple entity.
Here we'll also learn:
One To One relation
Cascade (save both entities in single operation, same for delete)
Then, the Category entity. Nothing out of the ordinary so far.
Proceeding, we now implement the Product entity.
Here we'll also learn:
Many To Many relation
Mapping the price attribute, which is a decimal
Here we implement the Order Item entity, which represents a many to many relation with custom properties. We'll learn how to relate it to the other two entities it connects, and also how to create its id, which will not be a serial id this time, but a composite id.
Now we start implementing the logic, beginning with category. As it will be very similar to the user logic that we already have, we can copy its service and adjust where necessary.
Here we also forbid the deletion of categories that have associated products, and thus avoiding orphan products.
And then, the product logic. There will be some differences, such as:
Validating the price
Receiving an array of categories that
Should not be empty
Must contain unique values
If we use the approach to receive entities in the payload as objects with an id field, instead of just the ids themselves, TypeORM will automatically map them as relations in the database for us. Let's learn how to validate these objects with nested validation.
In addition, even though objects are compared by reference, we'll maintain the validation for a unique array, by using identifier functions.
If we decide to just send the entities' ids, we can then wrap them in objects when inside the service. In order to have a single function to wrap both a single id and an id array, and also offer adaptive tooltips depending on the input provided, we'll use a function overload.
We then proceed to implement the order logic. Here, we'll use many concepts that we learned and structures that we already implemented. We'll also need a dedicated DTO for the order item. Then, it will be necessary to fetch the prices of the items from the database to prevent arbitrary prices from being sent. Due to this, we'll use an auxiliary method in the service, and also use Promise.all() to fetch all prices in parallel.
The total and subTotal are get fields, therefore they are not attributes themselves but calculations based on them. But we'll bring them as though they were, in a very practical way.
And finally the payment logic. Only a single method will be implemented, to pay an order. The order will be saved in the database again with its payment, and altered status, in a single operation.
A user could pay the order of another user, to avoid this we'll learn how to check if the user performing the payment is the order's owner, in the Extra module 1 - Authentication/Authorization.
Welcome to NestJS Unleashed! In this section, I will explain the problem this course aims to address, its purpose, and finally its structure.
Problem
Node.js is a backend JavaScript runtime environment that gained high popularity in recent years, and one of the reasons for it is because it allows for using the same programming language (JavaScript) for both frontend and backend development, amongst others. Even then, if used by itself, it is a quite "crude" environment for development as you are responsible for:
Libraries configuration
Code architecture
Routing
Among other things
To develop robust systems, in a professional fashion, it may not be so interesting to write code in pure Node.js. Many times, you may find yourself "reinventing the wheel" when there are already established and refined solutions, and it may become increasingly harder to maintain the system over time. In summary, this is the problem that this course aims to solve.
Purpose
The main intention of this course is to show how to harness most of the potential of the NestJS framework, which arose quickly in recent years. We'll do so while focusing on elegant architectural solutions, while learning everything in an intuitive and pleasing manner.
Here, all the lessons will be taught using a completely practical approach, and we will study theory only when necessary to solve problems that arise during our journey. The course is also incremental, meaning that we will start developing our backend system from scratch and improve it with more functionalities and upgrades along the course.
An important note, the approach to the architecture and code design used here is akin to a proposal. There is no completely perfect and flawless way of developing a system, even more so a complex one. That being said, feel free and even invited to make changes to the code whenever you see fit. It is also a great way to develop your abilities and critical sense about the code quality.
Structure
We will be developing the backend system of an online shop. Everything we'll study will be oriented to the creation of this application. Its business rules and therefore UML diagram are simple, even though there are interesting relationships and cardinalities. The focus, however, will not be the business logic itself, but the solutions we'll implement to make it come to life. The class diagram is very similar (with minor changes) to one found in the Java course (also in Udemy) by Professor Nélio Alves, due to its simplicity but also completeness.
The Core module explores these topics:
Project creation
Project configuration (ergonomy/DX)
Resource generation and setup (entity/module/controller/service/etc)
Validation (100% DTO-based)
Database creation (with Docker)
TypeORM Integration
Configuration (Environment variables)
And more
The full course comprises the following modules:
Core module - Backend Development with NestJS
Improvements/Tips module
Extra module 1 - Authentication/Authorization
Extra module 2 - Exception Filters
Extra module 3 - OpenAPI Specification
Extra module 4 - File Management
Extra module 5 - Advanced Querying
Extra module 6 - Automated Testing
Parallel module - Prisma
Further Improvements
And lastly, I would just like to remember that, throughout the entire course, architecture is always paramount.
The Journey Begins
With all this said, I hope you have a great experience developing this project with me and improve yourself in many ways as a backend developer. Should you acquire it, see you there. Best wishes.
Promo video credits:
Animations/Video editing - Fluxo Digital + EngajeEasy
Music - AudioCoffee