s js is a framework for building scalable Node js
web applications with TypeScript in this course, Vlad will teach you an S JS by teaching you how
to build a REST API. Hello, and welcome to this course. My name is Vladimir, I am a full stack
software developer. In this course, we are going to build a CRUD REST API with Nest. Yes, our goal
is to build something as close as possible to a production app, we will implement authentications
and to and tests. And we will work with relational databases as well as with a modern development RM
such as prisoner. My teaching style is very hands on. I'm a firm believer that you only learn by
doing all my code is very easy to follow. But if you're stuck, you will find a GitHub repository
with the completed project in the description below. Additionally, feel free to leave questions
in the comment section or on my channel code with flood. I will try to answer to them as soon as I
can. This course requires a basic understanding of JavaScript ESX and TypeScript. But don't worry if
you don't have it, as I will explain everything in a step by step fashion. So what is nest GS?
Not to be confused with Nex GS, which is a front end react framework. NES GS is a no GS
backend framework. It fully embraces TypeScript. And it's also very different problem from
let's say, express GS, it solves architecture, the weak point of Express GS, see, if you're
using Express GS, it's very unimplemented, it doesn't give you a direction of how your product
should be structured, or how you should do things. And while Express GS can work out very fine for
small projects once your product starts to grow. And if you are not an experienced developer that
knows how to structure it and scale it properly, it can quickly become a mess. But NES GS is not
just another framework in the sense that it does not try to reinvent the wheel, it actually uses
Express GS under the hood. So you can see it as an abstraction of Express GS. And in a nutshell,
it allows to create testable, scalable, and easily maintainable applications for NSGs. Modularity is
very important. It uses concept like dependency injection, and it is often branded as the Angular
for backhand. So now that you have an idea of what NES GS is, why would you use it? Well,
structure, modularity, native TypeScript support a lot of functionality out of the box easily
integrates with Graph QL micro services REST API, and has very good documentation on security. It
also tremendously grows in popularity, which is a very important factor to consider. In fact, right
now, NES GS is the most popular back end framework below express on the same level as Koa, Gs, and
above, Adonis, loopback, feather Gs, and even Meteor. Plus, employers really love it because it
brings architecture structure. And it's easier to hire developers because they don't need to learn
a new custom architecture every time they are on boarded on a Express GS project. So after that
long explanation, let's jump into our project and code our NSGs application. And in this project, we
are going to build a API application programming interface. This will be a CRUD API Lucia's very
common terms for API's and the resources that we're going to either update, read, delete, or
create will be bookmarks. So without further ado, let's get started. Before we start, we need to
have no GS installed. I'm using version 16. But any future version with a long term support should
work. You also need to have Ness GS installed. So you need to install their CLI globally and
yes, yes CLI. Then we can drop into our terminal and create our new project with Nest new.
And let's name it nest GS API tutorial. The CLI will ask you to choose a package manager.
I usually like to go ahead with yarn. Perfect. Now our product is installed, we can navigate into it.
I will open it with code VS code. But you can of course use any editor of your choice. And let me
make the code bigger. This is the VS code Visual Studio Code. The theme I'm using here is material
theme. And the icons you see here is material icons. You can download them and install it here
via their extension menu. So NES GS has created a starter project for us. It has a source folder
where our files are, it has a test folder, where it keeps the end to end tests. And it
has some configuration files here. Let's first clean it up, we don't need the app controllers
back. And we don't need the app dot service. And nor do we need the app dot controller. And since all those files were imported into the
app module, we need to go and delete them here. So for NES modules are very important. You
see that this file, the global file is called app.module.ts. This is similar to something
that we have in React, where we have an app.gs or app.ts file. So this will be the main module or
the app that will import other modules. So I have talked about modules, but I haven't introduced
what is a module. So for any concept related to nest, yes, you can just go into the nest Yes,
documentation, and it's on the nest years.com. And you click on documentation, and you'll have
this page where you will have a lot of docs on any concept that you want. And for instance,
here module, it's written that it's a class annotated with the module decorator. And in fact,
we have a class called app module that has been annotated with the module decorator, if it's not
annotated with the module decorator for net, yes, it's not a module. And if you don't know what a
decorator is a decorator is just a function that adds some metadata to the to the
current class or function that it is kind of decorating. So just adds more property to
to that class. So if we go back to the modules, documentation, we see that a module can import
other modules. And it is the case for the application module. So the main module, the root
module, it can, for instance, import User Module, or there's model chat module, any module that
you want. And every module can itself import, also controllers, and providers, we are going
to talk about those a bit later. But for now know that a module can import other modules.
And usually I like to organize my modules into feature modules. So if I have an application,
where, for instance, we have bookmarks, well, what would be the part of the like the
application, right, you will have the authentication logic where a user can create its
profile login sign up. So for me to be ot module, then you will have maybe user module that will
handle the logic related to the user will have also the bookmarks module, and maybe the database
module. So you see, you organize your app into those features, right, and you break it down into
modules. And the result is that it makes your app much more easier to handle. And to reason about,
let me show you a another project I'm working on, just to illustrate the structure. So I have a
source folder. And you see I have an old module, category, common post Prisma, which you see all
around for the database user. And if I click here, you see that you have some data, you have the
module as as, as we expect, and we have some providers, those we are going to talk about it
a bit later. But this is an example of a kind of production ready, project. So you guys can see the
organization and the cost structure in a nutshell of of next year's projects. Okay, so we now know
that modules organize your app, right? And they are declared with a class annotated by a decorator
module. So let's go ahead and declare another class another module to break down our application
into those small sub modules. So we know that we are creating a bookmark application, what would
there be in a bookmark application, while I have already touched upon the authentication. So in our
CRUD API, I want to be able to create users and to allow them to log in, right, so we will have
an authentication module. But let's go ahead and create a auth module. And usually the convention
is that you need to put the modules to create in a folder, and the only time that you can not
respect this convention is when you're using the main app module component that will import all
the other kind of modules or components. So let's go ahead and create the
folder here and let's name it out. And at first, I'm going to show you how to
create it manually when you're using a framework for the first time. It's Better to actually write
everything on your own and not use any generators. Because nest yes has a generator has a CLI command
that allows you to kind of automate that creation. And we're going to see that in a moment. But at
first just write it by hand. So we get a bit of a muscle memory going on. So we create a folder
called auth. Right? What do we know about modules, we know that it should be it should be a class
that should be annotated by decorator module. So let's create a file. And we just respect that
convention that if we have a file, we try to add the its kind of function to its extension. So this
is an extension right.gs is an extension.ts is an extension dot, CPP for C++ would be an extension
as well. So here, it's since it's module, it will be.out.module.ts. And we're using TypeScript
for all the products. So it's Ts, and we need to create a class. So it will be od module, right.
And we need to decorate it with a decorator, called module. And that decorator
comes from the Nash Yes, common module, and we just need to open it provide
an object. And that's it. That's the minimum requirements to create a module. So all we need
to do is to save. And that's it, we have created a module, and we have forgotten something important,
we have forgotten to export that class. So if we don't type export, this class will be available
only inside that module only inside that file, when you export it to allow other files of our
application to use it. So we go back to app module. And now we can just import our module.
And we can use TypeScript intelligence to help us to find our modules and files. So here, it
knows that the auth module exists inside out. So all we need to do is to click here. If it doesn't
appear, you can always do command.or control dot, and it will propose you an option to import it
from the odd module file. And here it is to verify that our logic works, let's go ahead and launch
our NES GS application. So the bootstrap logic is inside the main.ts. It looks a bit like what
we could expect from a Express GS application, right? So we have the app that is being
instantiated here. And then we launch a server on port 3000, I will use Port 3000 333, because
usually, the port 3000 is reserved for react in my development pipeline. So I prefer to use
3333. I save it. And now I open the terminal in the project. Let me clean that out. And all I
need to do is I need to do yarn, start Dev. And all those scripts are actually defined here inside
the package that Jason, we have start. And we have start Dev. So the reason why I'm using start Dev
and not start is because start dev What are files, and recompile the code when needed. So if we
do yarn, start Dev, it will launch NES Gs, and we don't have any errors, which means that
it's very good. Everything has compiled correctly, NES GS has generated a dist file, so a kind of
output compiled JavaScript file from TypeScript, we don't need to really worry about it. It's just
how naseous works. And that's it, we have our API working. So let's clean that up and, and come
back here. So modules allow us to break our app down into smaller components that we can easily
manage. Let's go ahead and create the user module and the bookmark module. In this case, I'm going
to use the nest CLI to create those modules. So you have an idea about how it works. Just open the
terminal, and we'll going to open a new terminal session. And we execute nest G for generate
module. And we name it user and nest will generate the module for us and automatically imported into
the app dot module for us. So we have a folder user, with the user, that module class here.
And we do the same thing with a bookmark. Right. And it also generates a bookmark
module for us. And if we check our terminal, let's kill this one. We see that the application
is still logging. If I for instance, press Ctrl save, you see that it is recompiled so everything
is working correctly. Now let's add more logic to our application. So let's add the login logic. So
when we build a nest GS application will usually separate our logic into controllers and service,
we can refer to the documentation. And basically it says that controllers are responsible for
handling incoming requests and returning responses for the clients and providers or services are
responsible for executing the business logic. So we separate our logic into controllers and
service providers to be able to, to simplify what we do, right. So let's go ahead and implement
it. We go now into ot module, and we create a service and a controller. So again, the same can
be done with the NES GS CLI, and we're going to do it by hand the first time. So let's create
a controller, dot Ts, and ot.service.ts. If we go to the controller, we need to create a class
class or controller, we need to annotate it with the decorator controller. So nest years knows that
it's a controller. And we need to do the same for that service class service. For the service, we
need to annotate it with the decorator injectable. And that just means that it's going to be able
to use the dependency injection that NES GS uses under the hood. But more on that
a bit later. And we need to of course, export those otherwise, we will not be able
to import those classes in our application. And since we have added that, we need to add
that to the module as well. So controllers, or controller, and providers, or that service, let's
check our application if it's running correctly. Okay, no errors. Cool, running good. So I've
introduced the dependency injection, but I haven't really said what it is, you can of course Google
it online. But let's go ahead and introduce it with a concrete example to see how it works. So we
have a auth service class and we have a controller Christ, right? What is happening usually is that
the controller will need to call the service. So the controller will receive a request from
the internet, for instance, a post request asking to login a user, right. And then it's
going to call a function from the audit service class and return its result back to to the client
back to the browser. But to do so, ot controller will have to instantiate a auth service class,
right, because in the end, it is JavaScript. So if you want to create that instance,
for instance, it will be service is equal, new or service, right? To avoid doing that. To
avoid having to manage where it's created, and who manages it all, we use dependency injection, that
means that instead of our controller to actually have to declare it, for instance, to actually
have to do something, something like that, give me the service here, give me that, an
instance of that class. And I don't care how to instantiate it, just give me an instance. And
this is how you do it in SCS. Private or service. Service. And that's it. Nice, yes, we'll handle
on itself, How to instantiate the auth service, and how to pass it to you in that old controller
file. So you don't need to really worry about and private here, if you have never seen that
notation, it just means it just means that and then have to do something like
this, or services equal or service. Instead of doing that, you just write private, and
you scrap off this. That's that just a shorthand. And if we check our code, we see that it
is being executed. So everything is okay, everything is compiled. And now if we
want, like if we put a function into the odd service called test, this function is not
going to return anything. If we want, we can call this function directly like that. Right? So
that's dependency injection in a nutshell, if you want more information, just Google it.
There's a plethora of explanations online, how it really works. This is how Nigeria's use
it and basically it allows us to not handle dependency management. And it's just easier to
work with when we have dependency injection. So let's come back to the auth service and create
two functions because our auth module will manage to functionality, login and signup. So let's go
ahead and create a function, login and sign up. Right. And that's all is going to do for now,
the auth service will store the business logic right now there is none. So let's come back
to the controller and create two endpoints, an endpoint for login and an endpoint for signup.
So the way to do it again, is using decorators, it just simplifies how you write your logic. So
we expect a post request on signup and a post request on login. So we create a function,
let's name it sign up, right, and sign in. And, and to make it a route, we just need to
annotate it with the post decorator that comes from Ness GS common, and let's call it signup. And
sign in. And since we are in the odd controller, it's usually a good practice to put a global
prefix route called odd. So when we are going to call that route, we're going to do a post
request, have to out signup, right. And if we call that route, it's going to be that that request.
Let's just return a string for now. I am sign up. And here I am signed in.
Let's verify that the product is compiling all good. And let's go ahead and
test our API with insomnia. So insomnia is a HTTP client, similar to postman, you can also
use postman if you have if you're used to it in some way, just a bit more minimalistic
and easier to use. So let's go ahead and test our endpoint. And if you remember, our old
controller was on the endpoint, odds sign up, and right. And we do the Send Request. And we see
that we have response, right, I am sign up. And that is basically what we have written here,
right. And if we launch and free request sign in, it's going to print us the other response. One
interesting thing, though, is that if we look at the headers, we see that it is powered by
Express. So as I said, In the beginning, an S Gs use Express under the hood, you can replace
this by another framework called fast Defy. But for most use cases, you will probably use
Express, and that's going to be just fine. That also means that everything that you know
about Express, you can access it. So this is the nice thing about NES GS, if you like Express
Gs, and you need more structure in your app, you can use NES Gs, and still have access to that
express ecosystem if you need. One other thing that is very interesting is that Express has
sent us a content type of text HTML, right? So this is a text. So NES GS will automatically
convert the data type based on the return. So here, it's a string, if I send a object,
for instance, message, hi, or Hello. And we send it to sign up. We now have a object
and the header is application that Jason So that's a very handy, you don't need to worry about
sending the right data type, ness, GS will do it for you. Let's go back to our app. And let's
create more logic. So as I said, In the beginning, the controllers will handle the requests. So it
will fetch the body of the request if needed. It might check some headers or any work related
to the request. Regarding the business logic, the actual execution, we offload that to the service.
Usually the pattern is that we are going to create the same function on the service side. So let's
go ahead and do it. We have a function now sign up and sign in. Right? And instead
of returning something from here, we can actually call the service and
we can put that message here. Sign up. Hello. And that's right. I have signed up.
And here I have signed in right there things correct and let's call those functions from the
service, these odd service that sign up and this out service that sign in. So what it does is
that it allows to keep our controller clean, and only busy with a logic with to the requests,
while our while our service will be busy with the business logic like connecting to the database,
editing the fields, etc. So let's go ahead here and do it again. And we see that it works as well.
And if we launch sign in here, it also works. Now we know how a controller works, we know
that it is responsible for handling requests. We know how a service works or a provider, it
is responsible for handling the business logic. But what would be a business logic without
connecting to the database, right. So we need to somehow set up a database and connect to it.
And by that time, I guess you have noticed it, my style of teaching is to introduce concepts
bit by bit. So now we need the database. Let's go ahead and set up a database.
For that we are going to use Docker, Docker is an amazing tool that will allow us
to run our database directly on our computer, but so we don't have to install it. So it will
run in a containerized environment I have Docker already installed, you can go ahead and install
it. And one quick way to check if it works. You just write docker ps and if it inputs something
that means that it's working, my version is is version 20. With that being said, let's go
ahead and and create our database. So when I work with databases, I prefer to use Docker Compose,
Docker compose will automatically allow me to spawn the Docker containers and to destroy
them. So it's very handy. Let's go ahead and create Docker hyphen, composite Yamo. So here's
the configuration of our Docker Compose, I use version 3.8. In the services we define our Docker
container that we want to run is going to be dev dB, this is just the name I give it to, to it. The
image I'm using is Postgres 13. So we are going to use the relational database Postgres version 13
is going to be exported on the port 5334. So if we connect to Postgres 5334 on our computer, we'll be
able to access it. And the environment variables I'm going to use is the user who is going to be
Postgres, the password 123, as you have seen, very secure, and the database name will be nest,
and it's going to be on the network Free Code Camp that is defined here. That's about it. So to to
launch our database, we go back to our terminal, we open a new one. And we do Docker compose up, we
choose the DB that we want to run. So it's dev dB, the DB, and we will run it in background. So with
Duke and that's it. And we if we go to docker ps, we see that we have a container now that is
running. And we can even access a lock if we want. So Docker logs. And it's written that the
database system is ready to accept connection, it has deployed, it has compiled everything is
running correctly. Cool. Let's go back to our app. So we have the database running in Docker. But how
do we access that there are libraries like SQLite, typo RAM mongoose, that allow you to
connect to the database. In this example, I'm going to use a new IRM that I really love and
I have been using for over a year now. It's called Prisma. So you can access the website on Prisma.
That IO, what Prisma is, in a nutshell, is that it's kind of a query Query Builder, but it's so
easy to use. So basically, you define a model, for instance, that will be a post with ID title
and all the fields that you want in a database. And you can get them from your JavaScript code
or your TypeScript with that syntax. And we're going to explore it straightaway. So with Prisma,
we are going to create the database connection logic the database module in a way. So that
means that we need to create another module since the database module will be able to use
to be used by auth module because you need to login sign up. It should be used by bookmark
module and even by user module. So it's a new feature of our app for Prisma. We need to have two
libraries installed. The first one is Prisma CLI, it will allow us to create Our schema and
run migrations deploy the migrations into the database. So it's more of a maintenance library.
And the other one is the Prisma. Client. So Prisma has different clients, the one we're going to use
is for JavaScript. So let's install Prisma CLI yarn add, and we're going to add it as a
development package Prisma and yarn add Prisma client for the client. Okay, let's
clear that out. With that being installed, we now have access to the Prisma. CLI. Let's run
it and px Prisma in it. And it's going to generate several files for us. First of all, it generated
this dot env file for the environment variables. And it just created a Postgres connection
string by default, that we're going to change a bit later. And it also generated that Prisma
folder in the root directory. So what it has, it just has its schema. And this is where we're
going to declare our models. See, when you use the library like type or M, you will create entities
and you will place them inside your logic for Prisma. There's only one place that you need to
handle your your models a bit like with Graph QL, actually. So you would declare those structures
that will describe the shape of the data that will be in your database right here, it says that
it's going to use the Prisma client GS library, which we have installed, the provider will be
Postgres, but you can have any other library Prisma supports my sequel, and even MongoDB.
And the URL is the URL of the connection to the database. So it's going to grab it from
the first environment, a file that it finds. So if you place one inside the prison folder,
it will grab this one. For that tutorial, we're just going to leave it here in the global
root folder. And let's go ahead and create our models. So the way to declare the models is just
to type model, and you name it was a singular word. So if it's users, it's going to be user.
And if it's bookmark, it's going to be bookmark, right. Model bookmark, bookmark. And what do
we have in our app, we have users and basically bookmarks, right, so we only have two entities
in our app. So the user will have an ID field. It's an integer, we need to
tell the Prisma that it's an ID. And we need to set a default as auto increment.
Or that can be found in the Prisma documentation, of course, and same goes for bookmark, we also
have to add the created Add Field. Otherwise, we will not know when it has been created, it's going
to be a date time. And the default would be now which means when the record is created in the
database, a default value of now means the time at the creation is assigned to that variable. And
we also have to add updated add, I'd like to add that as well. Date Time and prisoner has a special
command here updated. And we can copy that to the bookmarks as well. Right. So for the user, what
we will have, well usually we store the email of the user is going to be a string. And it's going
to store also the password, at least the hash of the password, so we're going to put hash shrink.
By the way, if one of the fields needs to be optional, you can just put the, so let's create
two optional fields. First name is going to be an optional string and last name, optional string
as well, right, we also have a bookmark. So the bookmark that we're going to save is going to
have a title is going to have a description, which could be optional. And we are going to have
a link which will be also a string but will not be optional, because when you set a bookmark to a
certain link, well, a link needs to be there. Here we go. We have created our two models, we save
the file, and now you see auto formats it nicely. Then we need to add the database connection
string to our to our database URL variable here. So let's check it here. So the user is Postgres
password is 123. And the database name is nest. So let's go ahead and change that. User is Postgres.
password is 123. The port is at 54345434. The database is nest and scheme not public, we can
leave it out like that. So now when we run Prisma commands, prisma will be able to access our local
database that is running here. docker ps. By the way, if you have the Docker app installed, like
I have on Mac here, you can have, you can access the dashboard for Docker and see your container in
action here, next year's API tutorial. And you can even see the logs, right, so let's run our Prisma
command. So the way to do it is to run MPX Prisma. And let's press help for good to get some help.
So we have several commands, all that is explained into the Prisma documentation. Of course, it's
quite nice, by the way, you can check it out. So we have several commands that will be useful
for us, we have the Generate, we have the migrate. And we have the studio Studio will allow to create
a kind of a online client so we can explore our database through the browser, we're going to see
that soon. But the one that we're interested at that point is Prisma migrate devs, what it's going
to do is that it's going to read that schema, and it's going to generate the migrations in that
folder. So let's go ahead and run that terminal, clear MPX Prisma and migrate data and basically
says that we you have added tables, bookmarks and users for those migrations to be applied,
we need to visit the database. And it's usually common when you rise my when you run, migrate Dev
is only for development. If you need to push your migration to production, there's another command
that will not delete the data. But for now, we can just delete it. And we can press yes. And
it's going to ask us the name of the migration, we can just say in it. Now we see that
it has generated a migrations folder. And basically it just generated some
SQL. And when we run Prisma, migrate Dev, it actually does two other things. First of
all, it pushes automatically, that schema, that SQL to the database. So the database if we
go there, and we check the logs, here, we see that there is some command that has been run insert
into public users created an email hash. So Prisma has already pushed some migrations onto
the database. So the database has the tables, the tables of users, and bookmarks. And one
other thing is that Prisma has also automatically run the Generate command. So when you use Prisma,
migrate, Devon Prisma, also does and pm Prisma. generator, and what generate does, and it's
actually a very good, cool thing is that it takes your schema here. And it creates TypeScript
types for your schema. So it creates the user interfaces or classes. And same for model, the
bookmark interfaces or classes. So you we can directly use those fields in our code. So we
can go. And now we see that we have a user here, the user and the bookmarks as well, that
are that are exportable as TypeScript types from the Prisma client. So we can use those
types directly in our application, which is awesome. They're just very awesome. We don't
need to code that by hand, we can use it directly with Prisma. Right. So let's come back
to the schema and inspect our database with Prisma studio. So Prisma gives us a very handy tool
called Prisma. Studio, we can execute it by typing MPX Prisma. Studio, and it's going to connect
to the database at the URL in the end v file. Here we go at Port 5555. We are now at the Prisma.
Client and we see that we have a user and a bookmark models, right. And we can go here and
inspect our database, when we'll have some some stuff there. And we can even add records if we
want. So it's totally compatible with the Prisma schema. It's actually quite cool. Let's discard
the changes for now and come back to our code. We can leave that running that a that Prisma studio
running, and we can come back to our code here. So far, we have created the Prisma kinda migrations
folder, and we have created the Prisma schema. But we don't have any way from our code to
actually connect to the database. And for that, we're going to create a module to do it easily,
right? We're going to encapsulate all our logic regarding the database in the module and only
export from the module, the stuff that we need to be accessed by the application. Alright, let's go
ahead and create A module I'm going to generate, I'm going to use the NES CLI, because I'll be
quite lazy here, module Prisma. And I'll just call it the same name as, as this folder. But
it's, of course, two different things here, this Prisma folder in the root, stores the schema
and migrations, while this one will be our module, following the NES, GS modular structure,
right. So we will create an SG module Prisma. And now it's here. And we
will also create the service, we have created the service manually here,
let's go ahead and create the service with the CLI. So instead of module, we have
Prisma, or sorry, we have service. And by default, it is going to create the spec files. So the test
files, we don't want that here. So we say no spec. And now we have the service that is
important, as provided in the module, right. And in the service, we are going to create
our logic that connects to the database. So the way I like to do it is that I like to have
the Prisma service extending the Prisma client. And Prisma client is a class that allows to
connect basically to the database, right, it has a constructor constructor, it has connect,
disconnect, and execute SQL and everything. So what I'm what I want to do is I want to configure,
I want to instantiate it with its configuration, so I need to constructor and I need to call super.
So super, will call the constructor of the class I'm extending, and the constructor of Prisma
client needs to have data sources needs to have DB and URL and the URL will point to that trink.
In the moment, we're going to see how to use config variables, so dot n and everything in
SGS. But for now, let's just put that hard coded into our Prisma. Service. Right. And I think that
from now, we're going to need a bit of more real estate to run our scripts. So instead of running
every time, the terminal here, I'm going to kill it actually prisoners to you as well. And I'm
going to run my scripts in a dedicated terminal. Let me make it a bit bigger. So I'm going to
do yarn start demo. And I'm going to split it and do yarn MPX Prisma. Studio. And this is item
two, of course, is nice terminal I use on Mac. So if you have any questions regarding the
terminal, that's the that's the one. Let's go back to our code. We have the prisoner service declared
here. And we had it imported into the modules. So technically how it works in SGS. So let's say we
want our module to have access to the prisoner module, because it's all modular, right? Well,
you could, you could do something like import Prisma module, right. So let's see if
that works, or works. And normally, you would have access to the providers that are
inside that module, right. So let's go ahead and try it out. So in order to service that is part
of the auth module, and that has access to the Prisma module now, because auth module imported
in auth service, we can use dependency injection, to get reference to that service to Prisma service
from our auth service code. So constructor, private Prisma. And we can just reference it by
Prisma. Service. Let's press save. And let's see what we have, oh, we have an error. Well, why do
we have an error? It says that nest can resolve dependencies of auth service, please make sure
that the argument Prisma service at index zero is available. So what it does is that it says hey,
you're trying to import a a service here. And I don't know what is that? I don't I don't have
access to that. And even though we have imported the Prisma module from the in the art module, we
have not allowed the Prisma module to export it to export the prisoner service to other providers. So
if we want to do that, we just need to do exports. Prisma service, right. And now the error should
be resolved and we don't have we don't have any errors. right now. So basically, that means that
if you have a module here, you will need to import it in auth module to make it work. But that's a
bit tedious. Because we have our Prisma module where the user will need to have access to
the database, the bookmark will need to have access to the database. And if we add to other
modules, they will all need to have access to that prison model. So do we need to really import
that every time like every time we need to create an import and create a model. Now, what is
possible in in next year's is to create a global module. So instead of importing it like that,
what we can do, we can go into Prisma module and add another decorator called global. And
just by adding that and making sure that the stuff that we want to be exported, it also
is also in the exports array, just by writing that this prisoner service will be available to
all the modules in our app. And if we check our logging, everything works correctly, just make
sure that your global module is also imported into the app module into the root module. And in
fact, it is important here. Now we can freely go and and do what we want. And here comes the
interesting part. Because this is where we really start to write business logic. This is
where we really start to do programming and not configuration. And if it was a bit tedious to set
all this up, well, if you do something like that properly, with Express alone, it will take
you much more time. And you need to be really good with architecture to be able to make
everything work flawlessly here we're in SGS, we have everything working out of the box. And of
course, it takes a bit of time to get used to the way of SGS. But in the long term is going
to save you so much time. And here we don't actually need the object so we can delete it. The
injectable can accept other objects. But for now, it's not important in the scope of this of this
tutorial. So without further ado, let's go ahead and start writing our logic. So for this signup,
what do we need? The signup is the creation of a user right, we need to create a user based
on something that we pass in the body usually, so usually it's going to be as post route to all
sign up. We are going to save Send the body to that route. And the body will contain the email
and the password and is going to receive something back about something back we're going to discuss
more into details because we are going to touch to authentication and especially DWT JSON web
tokens. We're going to talk about that a bit later. Right now just let's focus on the process
between the request and what is happening in the in the logic. So net GS has a lot of
decorators. So this is a decorator. This is a decorator, right? It has a lot
of decorators that that have different functionalities. Remember I said that under
the hood is express GS? Well, if you want, you can access that express GS under the hood, all
you need to do is write a decorator called Rec. And it comes from Nash yes common. And let's
call it Rec. And we give it the type of request, which comes from Express actually, it's
going to be a bit careful about that, where things come from. And if we now press
and if we now console log rec, we will see that rec will have a lot of properties. And
those properties are actually coming from the Express request object. So let's just log
it for for the loads and CO the odd sign up. I have signed up amazing. Let's go back to our
logs here and we see that we have logged the request object. And that request object is
basically the same as C Express request object. And you can get stuff like Heather's like body,
etc. Right. So if we want to log the body, it will be something like request body and then
if we want, we can pass that body to the signup function. So let's go ahead and request body
and provide a body of course it's going to be form URL encoded. Here we have an email and
a password let's send it over and let's log that into the console and we see that the body
has been locked. Now usually how it will work is that we will pass the request body inside our
function and of course here it complains because it the sign up doesn't doesn't have any declared
parameters. But what happens if the email is not defined if the password is too weak, or even,
there's no password? Well, this process of validating, that is called the validation. And SGS
has a lot of tools for that to make it really easy to do. But before even going to to that direction,
I want to point out that this method is not very clean every time we need to get a request. And
then we don't have any, any information of what is inside the body. And SGS uses DT O's data transfer
objects in a nutshell is just an object where you push your data from, let's say a request. And you
can run validation on it if you want. And you can even have the shape of those details because we
use TypeScript. So we can say that the body should have an email, and it should have a password,
right? So let's go ahead and implement that when working with NES GS, you should never really use
the request object of the underlying underlying library. Because what if you switch to fast defy
what if you switch to another framework that they might add in the future, you will use that kind
of independence that NES GS has, you will not be able to reuse the same code pretty much. So
what we're going to do is that we're going to use another decorators called body. And it just
allows us to get the body of the request, right, let's call a DTO. And let's declare it as any,
because right now, we don't know the shape of it. And let's console log that detail. And
I'm going to use the My Favorite logging pattern. So basically, is just going to create
an object and put the DTO inside it. And that's the shorthand for that. Let's go ahead and do
the request again. And you see that it also works. So the advantage of using decorators like
that is that express will get the right body, depending of the framework for you. So you don't
need to really worry where the body is express or fast defy or any other framework. Message. Gs does
it for you, right. But here, it only brings us half of the answer, because we don't know the
shape of it. So we need to create a interface for that shape. Let's go ahead and create it. So
usually, I like to create a folder called DTO. And inside the DTO, I use the Baron export
pattern. So I have an index.ts that is going to export all the fields. So here we are going to
do what while it's going, let's call out.dto.ts. And there's going to be interface called odd DTO,
it will have an email, which will be a string, and it will have a password, which will
be a string, and we need to export that. Right when to close that. And we're going to index
and we need to export that detail from the index. And if you have never seen that
pattern, the Baron export pattern. The main advantage is that
now I can just do odd DTO. And it's going to import all the details from
that folder, instead of doing something like odd detailed Ts. And if we have something else
like sign up the detail that is instead of doing that is going to import everything from the same
folder, all the imports will basically be there in those brackets instead of being spread out
in your in your code, right. So that's a nice pattern that I love using it brings a bit of
more code. But honestly, it's worth it. It makes your code much readable. And now we have
access to this parameters, email and a password in the DTO. Right. And let's let's clean it a bit.
Delete the requests. Recall, let's try it again. And it works again. But here, it's only
TypeScript. So what if we don't pass the email? What if we, we forget to
pass the value to the email? Well, if we log that, we don't have any errors, we, we
have email that is void and we have a password. So that's a problem because now we need
to write codes like that if DTO dot email, if not then throw error and is this is very
verbose. We can simplify that a bit by using the class transformer and class validator libraries.
So let's go ahead and implement them. And just before we implement them, let me introduce pipes.
What are pipes in SGS pipes are just a function that transform your data. So if you have a string
that comes from the request, because usually apart from if you send the JSON If you send a string
in the in the in a query something like sign up, for instance, user ID is equal one or user one,
that one will be a string. And if in your code, you need to use a number, that could be a
problem. So one way of doing that is to use the built in types. So what we can do is that
we say email is going to be a string. And we can isolate the email field directly from the
body like that. And the password would, will be, will be created like that, right. And let's, let's log goals email password. And for some
reason, my pitch here is not working. See this line is a bit big. So I'm going to go into
preach here, and reduce a bit the, the print with and you try that out. Okay, 50 works
well. And let's also look the type of type of email, which will type of email. And same for
type of password, type of password. So again, I go here, and we say that the email is a string,
the password is a string. So what if we want to transform the password into a number? Well,
it could be done simply like that by using inline pipes, parse int, type, and there are other
pipes. But usually, it's the this one that you're going to use. And here, since it's not a number,
the password will throw an error. So it's going to stop the execution of our code. And if you
check here, there is no console log is going to stop the execution of our code before even we run
the business logics. So this is amazing. But if the password is a number, well, it's going to have
is going to work right now. And we see that here, the password has been converted by the pipe to the
number, obviously, this process is a bit verbose because you need to create a pipe for every
data field, what we can do visit DTO is just is just apply those transformation and
validation directly on the on the detail. Right. So let's go ahead and continue what we're
doing with that small explanation about what are pipes. But I think it's quite important
to understand the pipes before we move into the detail of validation. So there's more
explanations about pipes in on the NES GS website. And you see that we have different types, both
pipe float pipe, uu ID, which stands for unique, unique identifier, but there's something
called Class validator. So basically, this is what we need, we need to install
those two packages, class validator and class transformer. So let's go ahead and add them.
Yarn Add Class validator and class transformer. Here we go. And now what we can do is that
we can go into the DT O. And to apply the transformation and the validations, we need
to use a class not an interface. Basically, it is the same for us, it doesn't change anything,
it just that instead of interface, it's a class. And we need to add something here is email,
for instance, from the class validator package, and is not empty. And is going
to be and this is going to be a string. And it's going to be erased, not empty. And let's
go ahead and throw the request again. And we see that it does not work right. Why doesn't it work?
Well simply because we don't tell nest yes to use the pipe logic to use the validation pipe globally
everywhere. So we need to go into the main Ts. And just before the abduct, listen, write app, use
Global pipes. And here we just write validation type. And validation pipe is a built in pipe by
NSCs for that same purpose, right? And we need to instantiate it like that new validation pipe.
So now that we have added that validation pipe, let's go back to the insomnia and run the request.
And here we see that we have two errors. The first one is email should not be empty an email must be
an email, right? So that's cool. So if we provide something like test, well, it's still going
to complain that it must be an email. So flat, Gmail dot Come. And now everything works. This
is validation in a nutshell. Thanks to DT O's, the validation pipe can do other stuff such as
transform automatically your data. One thing that could be interesting for you guys is to see that
use case. So if I'm going to my controller, and I'm going to console log, the DTO. Right. And if
I'm going to execute that logic, we log that. But what if I try to cheat? What if I have identified
a maybe a vulnerability in your server and I want to inject a variable, for instance, ID is equal
one. Or any other variable? Well, if I console log, that ID will be passed to our DTO. And maybe
that's not something that we want, right? One of the things that the pipe validator can do is that
it can strip out the fields that you don't need, what we can do is just set whiteleys to true, and
it's going to do, right, we send the same request. And now we see that the ID is not here, what it
is doing is that it's stripping out the elements that are not defined into our DTO. So now we can
be sure that rd to has an email and a password, and know all the fields that we
have defined. So this is amazing. Let's go back to our controller now. And
write the rest of our logic. Now we know that the DTO is validated, and it has an email and
a password. Let's pass it to the signup function. To make it all clear. Clean that and let's open
the audit service. And in the audit service, here, the signup function will receive a detail of the
deal, right. And now we can run our business logic with self assurance that the data that we
receive from the client from the browser is actually correct. So the first thing that we're
going to do is that we need to generate a hash based on the password, I like to use Arcanum. So
a lot of people use B crypt for password hashing. And that's a fine solution. But I also had the
refresh token in my in my application. And there's a problem with B crypt because its verification
algorithm is only limited to the first 72 bytes are going is considered to be a better solution
overall. So I'm going to go ahead and use argon. For that we need to install argon, two.
And we can import it into our oath service. So all as argon from argon two. And the first
thing is that we need to generate the password. Then save the new user in the DB, right. And we
need to return the user, the saved user. We're going to do those three things first. So let's
generate the password hash is SQL Argan. Hash. The first will come the plain text that we want our
hash. So it's going to be detailed or password. And that's it. Then we're going to say the user
in the database. So const user is equal await. And it will be in a sync function, of course, because
it's called Prisma. A synchronously this prisoner user. And since we have defined
user in our prisoner model, create data. And this is the data word that we're
going to use to create the the user with. And what we have here we have email, which can be
detailed email, and we have hash, which will be that hash. Right, we can save it. And it's
not happy because it says promise string is not assignable to type string, and of course, are
going to hash is in a sync function. So our wait and the user is created. And then the user is
created. We just returned to the user. Let's go ahead and, and create our user. So we can go back
to insomnia and delete that ID and create a user. And our user has been created. Obviously,
we see that we have returned a hash, which you should not return because that's
basically the hash password of the user. Not very secure. And let's let's try it
out again. So we go to Prisma studio. We gonna reload, and we see that the user has
appeared here, we can delete it, delete record, here we go. And go back to our code and just say
that, listen, I don't want to return the hash. Well, how do I do it with Prisma? Prisma has a lot
of possibilities. Either you, you create a select, and you select only the fields that you want.
For instance, if we want only the ID, we say Id true email true. And, for instance, created
add to true. So it will only return those fields like that. And let's go ahead and delete that. But
obviously, it's not very handy, because there's a lot of logic to, to write, right. So what I prefer
to do is to write transformers. So we're going to cover that topic a bit later. But for now, an easy
and dirty solution will be to do delete user hash, there is going to just strip out the hash
out of that user object and return that. So let's go ahead and create one. And we have the
user object without the hash. Amazing, right? So one other thing that will be interesting, what
happens if that I, if I send the user again, well, if I do it, again, it's going to be created again,
and another user and another user. That's because email field is not set as unique in the prisoner.
So let's go ahead and run the migrations to set it unique. And we can delete that. Otherwise,
we'll have some problems. And let's go ahead and get back to the Prisma model. And this is how
migrations of Prisma works. So those migrations have created, the bookmark and the user. Now
we're going to modify our model. And to make it all compatible with our database. So we can
simply include the new updates to our database, well we need to do is we need to modify our
schema. So for instance, email should be unique. We might want to rename the the table user
to users, because right now it is great user is good for Prisma, because Prisma
Prisma loves those kinds of things. But we're going to just map these names
to another name. So map users, and map. book marks, everything seems to be correct.
Amazing. One thing that we might add as well is the relation between bookmarks and user. So
bookmarks will belong to the user. The way to do it with Prisma is to create a link to the user
in the bookmark because many to one connection, so many Bookmarks can belong to one to the same user.
In other words, this user can have many bookmarks, but any given bookmark at any given time belongs
only to one user and not to several users, so many to one, let's create a user ID variable is shall
be an int. And user, it shall refer to the user model, we need to tell it it's a relationship is
going to use fields. So we use fields to indicate a prisoner which fields are used for primary
keys. And we use reference to indicate to Prisma to which variable this primary key references
to so it's going to be to the ID, which is an integer, or fuser. So we just name ID, right.
Amazing, right. So we have made some changes, and Prisma automatically included an array bookmark
here, we just need to change it book marks. And is going to be an array of bookmark module.
All good. So now we have made our changes, we need to run the migrations, how to do it,
we come back to the terminal. And we do NPM Prisma. Migrate, Deb is going to connect to the
database, enter the name for the new migrations. Let's just name update models. It has run
the generate. So it has generates the new variables in TypeScript for bookmarks and for user
ID. And it has made the email unique and it also has pushed the new migrations here into the
database. And we might need to restart studio. Sometimes it's a bit buggy
when you run the migrations, and it works correctly. So now we have the user
and with the new fields, email should be I believe it should be unique somewhere it should be ready
Unique. But anyway, we have bookmarks here. And the bookmark has been updated with user ID and the
user object. So which is, which is really nice. So go back to our code and write continue writing
our logic. So we go back to the audit service, let's see if our logic against the duplicate
user works. So we create one user ID one, because we have visited database. And if we
create the same user with the same email, again, what we have is a five or not very cool, right?
Because 500 errors don't tell us anything about what happened. It's better that we provide to the
user a kind of phase specified error saying, Look, in that case, it is a forbidden exception, because
credentials is already taken, right? So we need to create that error in SGS, it's actually quite
easy to do. And if you see here in in the logs, we see that the there's an error with the
unique constraint failed on fields email. So prismas tells us that email is unique,
and we cannot really use it. So what we can do is that we can say, we can add try catch,
block. So we put all that into the try catch error. Here, we can just isolate if the error
comes from Prisma or not. So if error instance of prison Prisma known client request
error, and all that is detailed in the docs, right? The only time where you would
write those detailed error exceptions, is basically when you create a unique field with
unique properties. Basically, you don't need to cache all the possible Prisma errors here, I
just want to make sure that if I'm catching the Prisma, duplicate, unique duplicate error, so if
the error is is Prisma error, and not something else is that happens if error dot code is
equal to p 202. Which stands for duplicate field like Prisma has error codes defined and this
specific code stands for you try to create a new record with the unique fields that has been
violated. So if that happens, we throw a naseous exception, throw new, forbidden exception.
Credentials taken, right. And that new forbidden exception actually comes from from SGS. It is well
documented in the docs, and is the error is not does not come from the Prisma what we do is that
we just throw the error, right. And let's go back to our insomnia send request. And here we see
that the status code is for free. And we have a descriptive message error message telling
to the client, hey, the credentials are taken, you cannot use that. Amazing, right. So we
have created the signup service function. So let's go ahead and create the sign in. So
what happens when we sign in while the user will provide the password and the email? The first
thing is that we need to find the user by email. If user does not exist, throw exception,
then compare passwords. And if password incorrect, we throw an exception. And if
everything goes well, we just send back the user. Okay, so the sign in function will also receive
the same DTO as signup, we don't really have a difference between the signing and signup process
in some application, they do in some application, the signup object, the signup detail will be more
complex, but in our case, it's going to be the same. So the first thing is to find the user. So
we go and find the user const user is equal await this prisoner user find. So the way you can find
elements in Prisma either you find them by find unique or find first. So if you want to find a
single element from the database using Prisma you can either find it with unique or first
find first will allow you to get the element by any field and find unique will allow you to get
the element by unique fields. So in our case, either a ID or a fields with unique
property. So let's, let's use find unique. Where email is DTO that email, right? So the first
thing that can happen is that someone tries to log in with an email that does not exist in our
database. So to handle that use case, we're going to create what's called a guard conditions.
So if not user, we're going to stop the code at that point and throw an exception, throw new,
forbidden exception, credentials incorrect. And if the user has been found, we can just
continue our logic and compare the passwords. For the password comparison, we're going to use
the compare function of the argument. So for the compare password, P W, mattress, physical await
Argan. Verify the first argument will be the hash. So the hash password, it is in the user, of
course, so user dot hash. And the second argument is the password in plain text, and it comes from
the detail DTO dot password here. And again, we put a guard condition if not PW matches, if
password does not match, we fire an exception, the same exception as above, here we go. And if
everything goes according to plan, we can send back the user. And before sending back the user,
we can delete the hash field on the user object. That looks good. Now before we go ahead and try
it out, let's create some handy scripts for us. So first of all, let's just write a script that
will allow us to respond our database instead of typing it manually here in the terminal. Let's
create a script in the package Jason. So we can just run it and it will rebuild our container
for us. So our Postgres database. And the second thing is that, let's also write a script that will
allow to apply those migrations to the database. So let's start by the database. So let's, let's
call, let's call this clip D dB. Dev restarts. So that means restart the database with Dev
development environment. So by definition, we only are in development. But if you have a
project, and that has several environments, such as testing environment, maybe staging or something
else, well, you can create several scripts with that syntax. So for now, let's go ahead and first
try it out our script here. So Docker Compose. So to remove a container, and here in our Docker
compose our container or service is called dev dB. To remove it, we use RM. And let's go ahead
and see what what functionalities we have here. So we have forced to, to remove them without
asking us for confirmation, we also have stopped to try to stop the containers before
removing them, it could be nice, it's a nicer way of stopping them instead of
just killing them. And we also had this v dash v to remove the volumes attached to that
containers. And that's a good thing to do. So to for our cleaning script is going to
be Docker Compose, remove dev DB as f v. So let's go ahead and put that into
our maybe let's do something like that, let's put our logic into several sub scripts.
So it's just easier to manage them the B dev RM, right. And the same would be dB dev up.
But instead of removing is going to, to push it to create it and with is going
to be done in background. And for the DB dev restart script is going to do
yarn dB, the RM and yarn DB Dev. Up. Let's try our our newly created script in our
terminal and see if everything works correctly. Yarn DB dev restart. So we see that it runs
the first one and then it runs the second one. So we had the Remove thing here. And we have
the creation of a new Docker container here. And if we do docker ps, we see that we have
one created here for us The problem, though, is that even though we have created the
database, it doesn't have migrations applied to it. So it does not have tables of users and
bookmarks. So we also need to create a script to automatically apply the migrations. So
remember, in the beginning, we have run the command Prisma, migrate Dev, what is going to
do is that is going to generate a new migration based on the current migrations. And we don't
really want to do that, what we want to do is that we want to apply those existing migrations to to
the database. So that process is much more safer than regenerating the whole migrations every time.
Additionally, when you do prism, migrate Dev, it asks you for confirmation. And it asks you
for name of that migration, for instance, we have in it here, and we have update models. So it's
very hard to automate. However, the other script that Prisma has is called MPX, prisma. Actually,
let's go ahead and check the Migrate Doc's help. And we have Maghreb data, that's the one that we
have used in the beginning to generate the initial schema and to run the migrations. But if we
just need to apply the migrations to a database, we can just run Prisma migrate, deploy. So let's
go ahead and create that script. So Prisma, Devil deploy. So this is going
to deploy the migration to our dev dB, which is here at that, at that at
that link. So in so Prisma, migrate, deploy, right. So and what we're going to do is that
as soon as the database is up, we're going to run that script as well. So it's going to also
deploy our current migrations to the database that we're working with. And yarn Prisma dev dB, what
might happen as well is that if you have a slow computer, the database might take a bit of time
to start. So it's usually nice to put this bit of sleep here, I'll put sleep one second. So we are
sure that before trying to apply those migrations, our database is running, and everything is
set up correctly. So we don't try to apply the migrations on a database that is in Bootstrap
mode. So let's go ahead and try that out. Again, yarn DB restart, is killing the database. It's
restarting it is waiting a bit, and it applies the migrations here. And we we see that it's written
that we have, we have found two migrations, and the following migrations have been applied here.
Amazing. Now we can go ahead and start our server. And thanks to TypeScript, we
now know that we have an error amazing, we don't need to run the script
at runtime to notice that we have an error, we have it here even before starting the server.
So we have what we have in the OD controller, you have not given a DTO while in the service,
you require the to, let's change that. So in the surface, the sign in function, it
requires the DTO while in the controller, you don't pass it. So let's copy the body here, the
body logic and pass it to to the sign in function. That should resolve the issue. Amazing. Now we can
come back to insomnia. By the way, I noticed how to make the font bigger. And I just didn't think
about it. But yeah, basically, there's an option in the in the in the journal options where you can
resize the font. So sorry, for the smaller fonts in the beginning, I'm learning as well. Let's go
ahead then and do the signup requests. So there's Senate and we had the user, if we try it again, we
receive an error. And if we do sign in and provide the same credentials, we have the user again, if
we do a mistake with a password, we have an error, which is nice. And if we do a mistake with
the email, we have an error as well. Amazing. We basically have implemented the sign in function
and everything works correctly. One thing though, in the real applications, when you sign
in, of course, this information can be sent to the browser. So it can it can, for instance,
render information about the user. But it is not enough to help the API to help the server to
actually identify the user connecting to it, we see that in our case, we send the email and the
password. And it's called Basic Authentication. But we cannot do that every time. Right? So there
are some API's where you need to send the email and the password. And basically anything required
by the basic authentication at every API request, encoded, maybe in the base 64 format. That might
be a bit technical, but that exists. But in our case, for the user experience, we want the
user to login only once in a while, right. So to allow the server to track the user to
know who the user is, there are two techniques. The first is sessions, you probably have heard
about them already. And the second one is G wt, Jason Web Tokens. And that's another way of, of
tracking the user on the website. So know who the user is and allow or forbid, requests based
on user identity. Because if the user sends that back, it's not enough, of course, anyone can fake
that. This whole process is called authentication, and authorization, right? So we identify the
user. So the user provides some credentials. We know who that is, we identify then the user,
but then we need to give something to the user. So we can authorize that user through subsequent
requests, for instance, login, signup, everyone can call that route, right. But there
might be something private, for instance, user, slash me, and that only attentive FIDE users can
call it. So let's go ahead and implement that functionality. I said before, we are going to use
DWT, for that we have already prepared modules for NES GS. Now. So far, we only have seen custom
made modules. So it's module that we have made ourselves. But Ness, GS also has modules that
you can use in your app, like any NPM library, so let's go ahead and implement some of them.
The first module that we're going to implement is the Config Module. So for now, we have used hard
coded environment variables. So this database URL, we have actually hard coded in Prisma, right. So
instead of doing something like that, which is not secure at all, because that will be available
in your GitHub repository. And also, it is error prone, because what happens if you do an error
here, right, it's very hard to notice. And it can be only detected at runtime efficiently. So that's
something that we're going to fix with the Config Module. So let's go ahead and install it,
we can kill the server and install yarn, add nest GS config. And we can start the server
back again. And let's go ahead and implement that Config Module. So the Config Module is
usually implemented on the root module. So here, or you can put it in a in a custom module and
implemented there, if you need to use something like validation on that. So for instance, you can
check that, oh, this variable should be a string, this variable should be a number, this can
also be checked. But it's out of the scope of this project. So let's just leave it out. But
know that you can actually put the Config Module inside a custom module called config if you want.
But for now, we're going to just import it here. We have just installed the Config Module, let's
go ahead and implement that Config Module. And, and you see it comes from Nash TS config, I
actually like to move all the node modules packages on top. Now Config Module requires some
configuration. And if we add it requires a four route option. And that's all we need to do
to load that dot n file into our application. Under the hood. The Config Module uses dot and for
library, I actually have a nice video about dot n on my channel. So if you are curious how it works,
check it out. Like any module that we have here, it also have a service and also uses dependency
injection. So we can import that Config Service inside any of our modules of our
app. So where do we need it? Well, at first, we're going to need it in Prisma. So let's go
ahead and use it in prisoner to use dependency injection. Don't forget to annotate a class with
the injectable decorator. So if it has injectable that would be able to use dependency injection.
You don't need to put inject inject trouble if you don't have any dependency injection requirements.
So if you don't use something like Prisma Prisma service, for instance, if you use that Ness, GS
will complain somewhere, for instance, prisma. User, find many, if we do that it should complain,
because it's going to try to get that object and that will be undefined. So it will not know what
the user is. And in fact, it says Cannot read property of undefined, right. So for any class
in net, she has to use dependency injection. And that class, of course, needs to be inside the
module, we need to annotate it with injectable. decorator. So now it will work. But of course, we don't want to use Prisma. That
was an example. What we want to use, however, is the Config Module. So let's go ahead and name
it config Config Service. Config Service is a service declared in the Config Module that we have
imported here, I'm just repeating it so you guys can absorb the new, the new concept easier, right?
Repetition is key. And what I'm going to do here is that this string, now, I'm not going to
hardcore it, I'm going to import it from the dot and file. And the way the Config Module
exposes that dot and file contents, is by having getters in on the config object. So
this has a config object, we can do config. dot get. And we just named the the name of the
variable and database URL, let's put it in quotes, clean that and fire it up. And now we have an
error saying basically that, okay, we know what a Config Module is, but the Config Service is not
exposed. Well, that happens, because, by default, all the modules are kinda contained. So this
Config Module would be only available in the app module, but will not be available into the Prisma
module. So it's very similar to what we have here. With a global, you can expose our Prisma
module to the whole app. Well, any NES GS compatible module also have this functionality.
Of course, we cannot use the decorator here. But inside this object, there's an option
called is global. So we can just enable it, and that basically does the same thing. Let's
come back in now, it should work to make sure that our prisoner Service is working correctly. With
config, we can go ahead and print the config. And we see that we get the right URL. So everything is working. And let's
test it out. Let's create a new user have led to amazing the new user has been
created. And that proves that our logic works. Now let's come back to the GW T, the subject of
authentication seems to be quite easy, right? Like how hard can it be just login and sign up? Come
on, every website has that. But in fact, under the hood, it's very, very complex. And there are
companies, multi million multi billion companies, if I'm not mistaken, making hundreds of millions
of revenues per year, providing that service to corporate and, and and companies. So that's a
quite edgy subject to get right is also very hard. So in this example, I'm going to
provide a very simplified functionality, very simplified authentication. If you need to
get more information about that. I have a two hours video on that subject alone on my YouTube
channel. It's about net CFG WT authentication. If you're interested, check it out. But without
further ado, let's get started and dive in and try to add authentication to our CRUD app. Now SGS
has some decent documentation on authentication and authorization. And behind the hood, it uses
passport passport is a authentication framework for Express GS with a lot of strategies. So you
can log in with Facebook login with Google auth zero Twitter govt and has a lot of functionality
that you can use in our app. In our example we are just going to use G wt. So basically the user will
give the email password pair, and it will receive a G wt, what is it readable T, I have a video on
that on my channel, but basically is just a string with a signature, some data and some description
about the kind of string it is and which algorithm it is using. And basically inside it to say,
Jason that is encoded in base 64. And this is the information that the server could pass to
to the client. So for instance, in our case, we could pass that the sub which refers to the
ID will be a an ID or the user. The name could be Vladimir, in my case, and the email could be
Vlad G Gmail. com, and you can add as many claims as you want, though, all those fields
are called claims. There see expiration, you can put an expiration we can put a creation
date. Basically, you can put any data you want. And you pass that the server creates that
when the user logs in or signs up, and it passes it back to the browser. So every time
the browser goes somewhere on the application, it sends it over to the server and the server says
okay, yes or no you have access to that or not. This behavior is quite similar to sessions, except
that sessions are being passed automatically, with each request while govt needs to be passed,
basically with code, but apart from that, they solve pretty much the same problems,
or at least people use them to solve the same problems. Of course, some might argue
that G diabetes are bad for authentication, but I don't necessarily subscribe to that point of
view. If people are using DWT for authentication, we should at least show them how to do it
correctly. So let's go ahead and install the needed packages, we need to install quite
some packages if you have any problems with following the steps. Of course, all the code
will be uploaded on GitHub in the description below this video. But without further ado, let's
let's proceed and install all the needed packages. So what we need to have we need to have
Ness GS passport module, we need to have passport and passport to local we don't need
passport local is a library to handle local authentication. So we don't need passport local
nor do we need the types we only need passport and NES GS passport. This is basically the passport
library. And this is the porting to net GS kind of a, a module for Nash Yes, for passport. So we
need to install those two. Let's kill it and yarn add those. We also need to install g WT specific
libraries. So here NES GS govt model, and pass for govt package of passport. So it's a bit a lot of
libraries to install. But don't worry, just follow the steps everything is going to be okay. And we
also need to install the types for those paths for G WT thing because no library and they don't have
TypeScript inside the repository, it's in another repository. And we are going to save it as a
development dependency. Here we go yarn, add D for development dependency in types password TWG.
Now we will have all those added here somewhere. Right. And let's see how we can use them in
our application. First of all, everything regarding authentication, of course goes
into authentication module. And that's nice thing because everything will be organized
and clean in our project. The first thing that we need to import is the G WT module. So
remember what we have installed. This NES GSG WT is basically used to sign in the code tokens.
This under the hood uses JSON Web Tokens library so it just kind of a niche yes modularization
of that library, while the other one in the beginning that uses passport. This is a true kind
of module that pours passport to to the next GS. So the reason why I'm saying that is that this we
are going to use it in our service while the other library, we are going to kind of put it in
another folder called strategy because the first time you see that kind of pattern, it's
a bit weird. So we're going to import it here, G WT module. So this is to sign in decode the the
JSON Web Token and we just need To register it. And there are several ways to do it, you can
provide some the secret because the DWT needs to sign with a secret, you can provide it here,
an expiration date and stuff like that. I usually like to leave it blank, because in most of
the use cases, here, we're not going to use a refresh token. But most of the time when
you deal with Govt, you also need to use a refresh token. And the refresh token will have
a different secret and expiration date from the from the access token, and more about those
in a minute. But that's why I kind of like to declare the module here and customize it further
into the auth service provider. Right. So we this is to sign in decode JSON web tokens. So
let's, let's do it. Now let's do it. First, we go here. And since it's a module, because
everything in Nigeria are kind of modules. It also has a what a service of course, well, we need
to import it again with dependency injection. And this DWT models is registered inside this module.
So it will be accessible here. We don't need to make it accessible there. And the use case of that
module is just to sign some tokens. So let's go ahead and do it. We need to import it here as
the dependency injections of private govt G WT service. Right. And this is imported from DWT
from the same package. And if you have any doubt, you can also just check the package and see what's
what's there. There's a god service God module and some interfaces. And I think it's actually a
good practice to always kind of inspect a bit the the libraries that you use, it will allow
you to get a better understanding of what what is inside instead of just blindly following
documentations and see things just appearing there out of nowhere. So we have signup and sign in?
Well, we said that we don't need to return a user, we actually need to return tokens. But to return
that we need to sign that information, though, like this user information and transform
it into the token, obviously, like here, so we need to generate something like that,
right? Based on for instance, data like email and ID. So let's go ahead and create a function
for that. This is going to be signed token. And I like to put it in a sink as well. And this
sign token will take the fields that we need to sign, it's going to be user ID, which is going
to be a number and a email, which is going to be a string, so we're going to send the user ID
and an email. So later on, when he passes back, something like that, we can get those two
information out to that token, and run validations or assume that all this is the user ID one. So
whatever action that he does on our platform check first is that he has authorization to pass
that specific action and then perform that action. So this is the kind of information
that we're going to extract from the govt and kind of trust what's inside. So we're going
to construct an object that we're going to sign, let's call it data user. Let's code
sub user ID sub is just a standard of of the GW T's kind of convention that that
you need to use a unique identifier for a sub field. And let's add any field that we
want for in our case is going to be an email. And what we're going to do is that we're going
to return this G WT sign and all those functions come from Jason Web Token library that you
can find on NPM if you need sign a sync. And the first thing is going
to be a payload and the second, the second argument is going to be a options for
the signature. And this is where we can set our secret secret information that we're going to sign
the data with. So only us know that information. So if a user comes back with a token ad with
token, we know that it comes from us because we have signed it with our secret password. And
actually, we can name it payload because it's going to be more descriptive payload. And the
second option will be an object. First of all we want to set when the token will be expiring.
So let's set it at fixed 15 minutes. 15 m So that means that once we give that token to the
user, the user can do some actions on our platform for 15 minutes after that, the token will be
rejected and the user needs to sign in again. And we also want to add the secret, of course, secret
is mandatory. Otherwise, we can't really sign up for the secret. Since it's a secret, we should
definitely not commit it to GitHub, let's put it into the MV file as well, as called GBTC. Correct.
And let's name it whatever like super secret. And let's copy that and use the same
process that we have using to Prisma Prisma service, let's import the
Config Module, private config, sorry, Config Service. And let's get that the
GW t here. Cons secretly SQL these config get govt secret. Here we go. And since we're
using TypeScript, we can actually provide some interesting function descriptions, like the
return type is going to return a string. Right. And here. And for signing, for
instance, instead of returning the user, what we do is that we return this sign
token user.id and user dot email. Perfect. Since we return in a synchronous function, we
don't need to put a sink here. This is only useful if we do some asynchronous operations, when with
await here, since we're returning the promise, we don't need to put it here. The code will know
that it's a promise. And same thing happens here. And we don't even need to do it, the user here,
we can clean that out. And let's also return the user here. Awesome. Let's get back to our
insomnia, and pass the URL request. So now if we do sign in, is going to return us. Of
course, we forgot to start the server. Silly me. The server is running no
errors, let's do the request again. And now we have a text, which is not
very nice, though I didn't really want a text. But it gets us the the JSON web
token. And by the way, if we copy that, and paste it here into govt.io website, we see
that we have the sub, which has the user ID and our email. So this information that we have
signed is correctly included into the body. Now into the next part, one thing that we might want
to add is that instead of returning a string, because here is going to return a string. And of
course, next js is going to convert the headers to text HTML, I'd like to return a object. So instead
of doing that, we can just change the logic a bit. Here in our sign token, we can
return an object with access token, I have noticed that usually, the
convention for access token is to have this snake case convention. And we can just
return the access token here. Like that. And of course, is going to complain because our type now
is incorrect. Our type is promise string. So it should be promise of object access token, which
is a string. Now everything is working correctly, and we execute the sign in again, we get a object
with the access token key. Amazing. Now on to the next part, the strategy part, we have generated a
access token. And basically token a user can use to pass requests, protected requests to our
application, the next step is to actually write the logic that will be able to intercept that
token. So that token that we the user passes to us validated, make sure that it didn't expire and
make sure that the signature was which we created, the token is correct. And then only allow
the actions that the user wants to do on our platform. So to summarize, we have written
the logic that allows us to create a token, right? It returns a access token to the user.
Then what would happen? Well, the user will call another route, for instance,
users slash me to get information about the current user and He would provide in the
headers authorization token, something like that bearer space. And that would be the access
token. So the logic that will verify that this bearer token is correct is called a strategy.
And this is what we're going to implement now. For that we create a folder in our module
called strategy. We'll create a baron expert. And we'll create the strategy which is
basically just a class strategy, the Ts, the strategy, the G, the Ts, export class, G
devotee strategy. We have an example in the NES she has documentations. Let's check it out. Here.
The strategy should extend the passport strategy, which comes from the NES GS passport module. And
it implements the G WT strategy, which comes from this passport, DWT library. And what it does
is that it configures it in the constructor calling super. So it calls this module this
service. It says that the govt should be extracted from the headers as a bearer token. So it just
means that it should be it should have this kind of format bearer space token, which is a standard
for a bearer token. This property is set to false by default. So we don't need to set it to false.
It just allows us to ignore the expiration date of the access token which can be useful when
you were testing but we don't need it at that point. And it takes the secret key as a as
a parameter. And the secret is basically this thing that we have defined here, we
have signed our token with a specific secret. Well, we need to allow the strategy to decode that
same token with that kind of secret, not really to the code, but more to verify that the signature of
the token has been really signed by that secret. So we know that the token comes from this server
from our API. So let's go ahead and implement it. The jeido which is strategy should extend passport
strategy that comes from Ness GS module passport, and it should take the strategy that
come from passper jadibooti. Right. And it has a constructor because we are extending
a class we need to call its its constructor, the constructor of the parent class like that.
And we need to provide the configuration of that of that password strategy class. So we
can just go ahead and copy what is there. And delete that. Because it's false by default,
and import extract g, oo, t and x. And this is basically the secret, right? So this class is
also a provider like the auth service class, right, so it can use injectable. The reason
why we separate it in a different folder is just to make sure that we don't we know
that this class has a specific use case. And it's for validating the D token, the access
token. So that's why it's inside a folder. But it can also use dependency injection under the
hood. So that means that it can inject Prisma inject Djibouti and inject the config and this
is what we need because we need to get that that secret from the environment variable
so let's go ahead and do it injectable and here is going to be config Config
Service. config dot get and it's going to be I think we call the govt secret everytime I forget
how we call things. G WT secret. Amazing. Now, now everything's okay. The only thing that we need to
do is that we need to export that class from our Baron export, which is a good pattern. And we
then need to import that strategy as a provider. So NES GS is aware of that of the strategy being
part of our application. So G to bootstrap the G. And that's all we need. Let's go ahead and start
the server. Nice. Everything seems to be correctly instantiated, and we now have the DWT
strategy configured. Now we can protect some of our routes with that strategy. That means
that you can access a route only if you have the valid strategy here we are using CWT but if you
want you can also have strategies like login with Facebook login with Google. And they have their
own configuration options and stuff like that. So again, we generate the token with the auth
service. And then we have a strategy that we can decorate other routes with. So only
people with a valid token can access it. But we don't have any other routes, any other
controllers are part of the odd controllers. So let's go ahead and create one in the user module,
I'm going to use the CLI nest G controller user. I would name my controller users based on
the REST API format. And let's go ahead and implement a simple get endpoint is going to be get
me if you leave get decorator blank, it will, it will catch any any requests. So it will catch any
requests like that. If you put something there, it will actually catch it with that pattern.
So if you leave it blank, it will just use the pattern of the controller to catch it.
Obviously, if the controller has nothing, it will try to catch it at root, which is not
a very good idea because it can have conflicts and undesired behavior. But in this case,
we are going to use me. So we are going to call that endpoint with users slash me. And in me,
we are going to return something return user info. It's just a drink, right? Right now we are not
doing anything. And if we go back to insomnia, and CO users me, and let's actually delete that,
and it's not the post request, it's a get request is going to return us user info because we have
just mapped that route on to our application, but we haven't protected it right. So there
see. So to protect a endpoint to kind of have a condition that will say, Okay, if you meet certain
conditions, we are going to execute that, that logic, else, we are not going to execute that. To
do that in Ness. Yes, we use what we call guards. And again, you can use the nest GS documentation
to read about guards. A guard is basically a function that will stand in front of a route
handler. So in front of a endpoint, and to allow or not allow the execution of that endpoint. In
our case, our guard will check for the strategy. And if the strategy digital which strategy is
correct, is going to allow the execution of the route if not, is going to block it, you can of
course, create custom guards. But in our case, we can use something that has already been
pre made. And that comes from the NES GS passport module. And it's not called. So to use
guards on a given route or on a given controller, you can use pretty much everything at a global
level at the controller level, or you can use it at the route level. So in our case, we want to
block the GET ME route if you don't have a valid token. So let's go ahead and create that we
need to use a decorator called Youth guards. And in the youth guards, we can
provide the guard that we want to use. And Neji has passport, the module that
we have here somewhere, actually here. NES GS passport, it already has a guard
for to work with that strategy, right? So if we inspect the code here, we see that will
do what does it have? It has a note card, it has some interfaces. It has passport
module, but this is what we are interested in. It's not guard and basically. So it's
basically a guard that is compatible with Nash's that we can use. And that works very
well with the strategy. So let's come back. Let's go back to the user controller and import
it here into the youth guard. decorator. So old guard. And here in the parenthesis we provide what
kind of guard it is what kind of strategy it is guarding for. So it's G W team. And when we create
our strategy with with that was that strategy with that strategy object that comes from passport,
Govt it by default assigns DG WT kind of key to it so so you can assign any key that you want. For
us, for instance, GT 32 But by default, it just leaves it like that. So it will be identified
by the odd guy with that keyword. So if you have a refresh token, you can do something like
that. You can name it really the way you want, you can give it any name that you want. But by
default is G wt, you can leave it blank, or you can leave it here Djibouti, I will prefer to leave
it like that, because it's going to be clear, is going to be a bit less of magic, because the first
time I encountered the old guard, I was wondering, how does it know that this job jadibooti is
linked to that strategy. And that's how, so we are going to say that, Oh, this route should
be protected by that strategy. Now, let's go into insomnia and try to use the same request again.
And see we have a 401 status unauthorized? Well, it's because the strategy is being executed. And
it does not receive the bearer token. So it throws an error. So let's recreate a bit the flow, I
cleaned up the database, I have now three routes, get me route, a signup and sign in route. So let's
go ahead and sign up and create the body email if lad@gmail.com passport, sorry, password 123. And
this create the account, we have an access token, that access token, we can use it in our bearer
tokens. So if we, if we throw the request in right now if we use a request, right now,
we're going to get a 401. Because we haven't required any we haven't given any bearer token.
Let's go ahead and implemented authorization. Bearer. And then we paste the token. And now
we have another error, which was interesting. Let's see what is going on. This validate is
not a function, okay, we forgot one function in the in the GW strategy, and it's validly
date. So let's go ahead and write it. And it's going to take a payload
of any, let's log that payload. Cool. One important thing that we forgot
to implement is that we forgot to have valid function. So the token is going to be
transformed into that object and put into payload. And then we need to return it. So here we can
perform any validation that we want. And then we can return the user. In our case, we're just
returning the Jason. So what it's going to do by returning the payload is going to append the
payload to the user object of the request object. It sounds a bit confusing, but under the hood
of Nigeria's, we use Express, right? And we can get the request and the response of express with
the right decorators, which I have shown in the in the beginning of the course, right? What the
WG strategy does, is going to attend that a user object to the request object, so we can use it
in our route. So the user is going to be that payload. So it's going to be that object with
the sub email expiration date, etc. But let's get back to the strategy and just run it again.
It is passing correctly, right? So we have the the console log the payload here, as I said,
it's an object with a sub of the user ID email, our email and other information that was contained
in the token, right. So let's clean that. And let's see, why is it useful for us?
Well, if we go back to the controller, I said that the strategy will put that decoded
payload or whatever value we pass here, whatever value we pass in, the validate function is going
to append it to the user object on the request object of the request of the or the or
the API. So we can get that object here. Request. Actually the Ric Rec. And Rec object
is from nest GS and request interface is from Express. And we can do console log user is equal
to rich dot user. Record user. And you see that even expressed now that a user is possible because
because when you validate the identity of a user, its identity can be appended to the
request object. So here we are going to log that is going to log the payload.
So the object that we have returned from here. So if we run the requests, again, we'll come
back to the thing. And we see that we now log, the same payload, but from the controller, because
we have written user here. So if we pass any other value here, let's say hi,
Well, hi is going to be logged. Here, because we basically attach whatever we
export from the validate function to the request user object, right? Does that make sense? If that
doesn't make sense, don't worry about that, we are going to build upon that concept. And I'm going to
come back to it a couple of times. So basically, that means that we can get the information of
the GW T from the token and do something with it. Since we have an ID, we can get a user by the
letter Z, since we have an email, we can maybe get the user by that email, everything is
possible, we just know who that user is, that requests the users me endpoint, let's
clean the strategy and return the payload, we can close the strategy file and come back to
the user controller file. So what we're going to do now is that we need to get the information of
the current user based on the access token, right. So now we need to get information of the user and
return it back. So that can be done in several ways. In that example, we are going to get the
user object from the database directly in the validate function. So on top of having
config, we also need to inject Prisma Prisma service and here we can get the the object
so it's not going to be any it's going to be, it's going to have a subfield which
will be a number, and it's going to have an email field, which will be a string, validate will be in a sync function can get the
user const user is equal await the prisoner. And I forgot to put private. The reason why
I don't put private in front of the context is because if we do something
like that, I can't really use this because super must be called before
anything. And since I'm not using config in my, in the rest of my application, there's no point
to use private and remember when we use private is just means that we declare the
variable here automatically, right? So we don't need to do that I can leave it
without private and of course, it's going to be config dot get. But here we can use private
because we use it elsewhere. So this prisoner user, find a unique ID is going to be
where ID is equal payload dot sub. Also, very important thing to notice is we if we
return Nope, it's going to throw an error. Right? So the user will be now if the user
will be not found. And a 401 error will be returned. And if the user will be found, that user
object is going to be appended to the user request object on the request. And the thing that we
can do before doing so is to delete the hash. So we don't inadvertently export some sensitive
information, and then we return the user. And in our user controller, what we can do
is that we can return a rack dot user. Now if we run the requests user, slash me,
we're going to get back the user based on the access token. And now we have our application
working, we can sign in, sign up and get the information of the user. So that's quite cool. But
there are a couple of things that we can enhance, before I told you should not use the request
object immediately because it is error prone. And this is correct. So we can fix that.
And we also can make that a bit more clean. So this is easier to do. So let's start
with that. Every time that you need to put a string somewhere. It's called the magic drink.
It can create errors, and we can make that cleaner by abstracting it in its own class. So let's
go ahead and do that. This functionality will be part of the odd. So let's go ahead
and create a guard, a custom guard. So new father guard, and again, the Baron export
here, and we are going to create the duty guard.ts. And what is going to do is going to just be a class jadibooti guard. And all we
need to do is to extend the old guard. And we are extending a guard with GE WT strategy. And
that's pretty much it, we might just run the super and, and we need to export that class so we
can use it in our application. So instead of writing out guard, like that, with those strings,
we can just write that class. And let's export it and import it in our user controller. jadibooti guard. And if we run, if we check
the runtime it is executing correctly. And we can even get the information correctly. Oh 401.
Unauthorized and we can we can. So so this is similar to what will happen in our application, we
get a access token for a specific amount of time. And once it is expired, you cannot use the
protected route anymore. So we need to get a new one. So let's go ahead and create a request
email flat ARCHIE MILLER comm and pass word 123 We get the new access token. And then we come
back to the to the header here and replace it with a new one. And if we send the request, it works
correctly. So now we have fixed that. And let's go ahead and fix this. This is a decorator.
So you know, all those things are decorators, we have made a custom guard that extends the
auth guard provided by naturally as passport. But we can also make a custom decorator that will
go in the request object and get that user object and return it back to us. So now we can go in the
old folder because it's kind of part of the OT, ot functionality, right, we can create
another folder called decorator. And again, export the burn with a burn export.
And let's create a custom what we call a puram decorator. And I don't remember the syntax
by heart. And obviously that's why as programmers, we have documentation, so use it when you can
you're not supposed to know everything by heart. And in the custom decorators documentation, we
have params decorators, and that basically mean that you can put them in the parameters of your
endpoints. And there are a lot of them already defined, you see that you have the request,
we have already seen their session as well. You have IP headers query body that we have
used, right. And they say that you can create a custom decorator, for instance, to get the user
object on the request object. And the way to do it is like that. So they just copy it. And I'll
explain in a minute what it does. So let's go and create our decorator, let's call it get user.
decorator, the Ts, and let's call it get user. And that's it. It is creating the params
decorator that we can use like that. Get User right, and it in fact, it only knows it. And what it does is it can take a data that you
pass to it, for instance, that would be data. But by default, it's unknown. So you don't you
don't need to pass it. And the second argument it's actually gets, it actually gets the execution
context of the request. So it's important to understand that NES GS is an abstraction right?
It uses something under the hood. It can use Express it can use any other protocols. Here,
we say that the this execution context switch to HTTP because we're using HTTP, but sometimes
you might use for instance WebSockets, right. Or you can use RPC for something like like micro
services. In our case, we use HTTP And then we say get requests, there are other things that you can
do, you can get the response as well. But here, we want to get the request. So it's going
to get the request object of the Express Library, right. And then we can do something with
it. So this request will actually be the same as Express request. If that makes sense,
right, this request is going to be that, and then it's going to return whatever we want
from it. So the decorator will put in a variable here, whatever we return to it, and is
going to be a user. And in our case, since we have used the strategy, in our case, the
user object will be the user object from Prisma. Right, that's all we need to do for the custom
decorator, let's export it from the Baron export. File, and in the user, we want to use the Baron
export pattern is going to export it from the decorator file. And here, we don't need to put
any data for now we're going to leave it blank. And here user is going to be of type user. And
that will come from the Prisma client. So in the beginning of the video, I said that Prisma
generates TypeScript typings for us, so which is very cool. So now, we can say that this variable
is of type user that was defined in our prisoner schema. And now we can just return the user and
it makes it all clean, and that decorator, you can reuse it pretty much everywhere. Now, we can
also optimize that logic a bit by moving the guard on the controllers level. So that just means
that everything that is in the user controller, while it will require you to be to have a token,
it will require you to provide an identity of who you are, which makes sense. That way. We if we
have another endpoint here, for instance, patch. Edit User, well, we want me to copy the guard here
as well, we can just put it on the global level. So let's go ahead and run our requests again, and
everything is working. So let's say that in that in this case, you don't really want to pass the
whole user object, you just want to pass the ID. So you can say I just want the ID and then it's
going to be user ID and it's going to be a number. So you can say something like that. And then
in your logic, you can get the ID of the user, if you want to do something like that, it's quite
easy. You come back to the Get User decorator. And you say, well, if if data if there's something
in the data, then you return request user. data, and data will be a string. And it could be
optional. So we if we don't provide it is going to return the whole user object, if
we provided it's going to return the the a field from the from the user objects.
So we can we can actually try that out here. And here, we say we want to
get the email of the user is going to be a string. And we can before
returning the user console log that email. Let's run the request. And if we come back here,
or there's something that's not working a required parameter cannot follow an optional parameter. Let
me go back to the get user. And here, okay, I see. So instead of doing like that, and this is true,
a optional parameter can only be put at the end here, the old order kind of metrics. So we
will say String or undefined. So that way, the data could be potentially undefined. Let's
go back and throw the request. And we have a log of our email and the same time we get our
users so everything is working correctly. Let's clean that up. And we don't need to get the email
anymore. So thanks to those tools, decorators and guards. We have created quite a clean logic
for our application. You see everything is quite organized, and everything has its place.
There are a lot of decorators that nest GS has and it allows basically you to save Have some code
and have something? Well, I would say economic from a from a visual point of view. For instance,
there's another decorator that I use quite a lot. And that I haven't showed yet. It's HTTP code. So
usually, when you create a resource on the server, you send back a 201 HTTP code. So if we go ahead
here and create a new user, Vlad to the status code will be 201. Right? So that just means that
the server has created something in a database, that's kind of a standard, you can of course,
make it return to Oh, so for that is going to be HTTP code. That's the decorator. And inside the
decorator, we're going to return a status. So for instance, it could be 200. Gnaeus, also
has enums. So instead of returning numbers, which is error prone, you can return
enums, HTTP code, HTTP status. And you have accepted found and it basically means
different codes. So code 200 means okay. So and if we put the mouse here, we see that
200 is equal, okay. And there's a lot of other code that is that are used in the development.
So you see, here's a whole list here not found, for instance, that is very known for or for 401,
authorize that is thrown by the strategy when the bearer token is not correct and forbidden, for
instance, for free, when we throw a forbidden exception. So we can do that. Obviously,
when we create a user, now it's going to be two Oh, but by default, a post request will
return to r1. So we don't really need to change that. However, when we sign in, we don't
really create a new resource. So I like to return the code 200. So we can use it 200 Here.
So instead of like signing in with a 201, it can be used to sign in and get it to oh, that's
quite cool. So post requests return usually 201, GET request will return 200. So we don't need
to specify a because decorator here, we have implemented quite some features already. And you
see that our application starts to grow, will soon implement the user routes. For instance, the
patch user route will also have bookmark routes, and application will start to grow, it's going to
be very tedious to actually go in insomnia, and run all those kind of tests manually. Imagine you
have something like 100 endpoints, and you need to test several use cases, for instance, like, Oh,
I've signed up with a profile of admin and the admin should be on boarded. And then it should
do that. And then I need to sign up with you, oh, my God, it's pretty much impossible to test
manually. So that's why we use automated testing. There are three kind of main levels of testing
you have unit testing that you probably have heard about, unit testing will usually take any
any functions, for instance, is going to take the sign up function is going to mock any
dependency that the signup function is using, for instance, argument or connection to
the database. And it's going to try to make sure that this function is executing and
calls the right the right things and everything is working correctly on that unit level. However,
that takes a lot of time. And to make it right, you need to actually practice a lot. There's a
lot of methodologies, how to do unit testing. And it can be really nice when you have a project
where a lot of people work on and a product that will respond through years and years of
development. Unit tests are a good investment in terms of your time. However, in most of the
cases, what we want to have really is end to end testing and integration testing. So we have unit
testing just above, we have integration testing, integration testing, will kind of take several
modules. And that's why NES GS is so cool, because, by default, it separates our application
into modules. So for instance, I want to check the odd functionality on it. Well, I can load the odd
module and the Prisma module, and maybe the Config Module. And I can test only all those three
together. And when I'm happy with the result, I can just test something else user user well
for the user will need to have a user. So it needs to use the auth module. So we're going
to use the auth module user model prisoner module, but not the bookmark module. And we can can
test all those together, integration testing, you define some segments of your app and you
test them together. I like to test it with the test database, so on of having a dev DB that I
use for like manual testing and manual feedback, I will also have a test DB here in our Docker
compose that I'm going to spawn and destroy every time I do my tests. So you have integration
testing, and then you have end to end testing and end to end testing kind of verifies a very high
level user journey of your app. So okay, a user signs up signs in, he requests his profile, he
does that. So it mainly verifies how the user will interact with your app. While integration testing,
we can really test a lot of things, for instance, well, is the same token working correctly, is it
the same token with signing is working correctly are the exception throw correctly. So this is more
for integration testing. Since testing takes a lot of time, usually, it's estimated that testing
takes at least the same amount of time that you have spent on creating your app. So if you have
spent something like two weeks building your app, you'll probably spend at least two weeks testing
it. There are various methodologies for testing, there's test driven development where you
write your app as you test. Obviously, we have not done that here. And it can solve
some problems as well. But in our example, here, in our small CRUD application, what we can do
is that we can use end to end testing. So end to end testing will allow us to kind of showcase the
use of our app, and prove that it kind of works, we don't need to go into details and
make sure that everything works together. If you're interested by that, you'll probably be
using integration testing, I have a video about that on my channel. So if you are interested about
that, specifically, and how to do it with Prisma, well be my guest, there's a one hour video on the
subject. But in our example, we are going to use end to end testing. And I'm going to use a library
that has been suggested to me by one of the core developer of NES GS. And I have to say, it's quite
cool. It allows you to save a lot of time. And it's called Pakhtun. By default, NES GS uses super
test for the end to end test. But In that example, we are going to use Pakhtun je s. So without
further ado, let's get started and create our end to end tests. So we don't have to jump between our
code and insomnia over and over again. Okay, so let's start with that amazing endeavor. And first
of all, let's install package on yarn, add pack. So pack to me is an amazing library for testing
your API's. It is really nice to work with. And it works very well with Graph QL. And
it has a lot of features that we're going to use straight away. And to install
it, all we need to do is to use AES to install it with factum. So before we start, our test will need to consider
a couple of things. First of all, we will have to set up a test database for our end to end tests.
Because we want to keep the dev DB actually separate from the test dB, so we don't delete
our database every time. So that's one thing. And the second thing is that we'll have to set
up the database set up the Prisma service to be cleaned up every time we run our tests. But before
doing that, let's clean up our app. And to inspect file. We can just go here and delete everything.
So how NES GS works is that it's going to compile a module. And we're going to take the whole global
module, the app module is going to compile that and we can create a test module out of it. And on
that test module, we can call requests like we do with insomnia. So let's go ahead and first,
create a describe blocks. So it's going to be app content. And inside that app, and when we
will just run a test, a to do test should pass just to see that if our script is
running, and we are going to kill our server because we don't need the server anymore. We are
going to kind of use what we call test driven development. We are going to write tests and write
logic and make sure that our tests are passing. So The way to run that file is through tests end
to end here, there's a script already created for us by nursery's. So yarn test end to end. And
let's see if that at least works. And we see that we have a past test, nothing really happened. But
that's fine, because we haven't defined our tests yet. So let's go back to our test and create
the Prisma module. So before all the tests, what we're going to do, and it's
going to be in a sync function, it's kind of a hook that is provided by jest,
we're using jest as our main testing framework, both describes come from jest. And we're
going to use Pakhtun for for the requests to send the request to our server and to analyze
the the answers to responses from the requests. So before doing that, we need to create a module
module ref await test, the test is going to come from from NES testing. NES GS
test think there will be a test. Test create testing module. And here we import.
And that's basically a, a, a module as we have here in the app module. So we create a module
that will import that app module inside it. That's the easiest way to do it. app module for
the app module come from the from from here. And we are going to say compile. And
that's pretty much it for end to end test, we just import everything because everything
needs to work together. And what we need to do, then is to well, let's see
that passes to begin with. Right? Amazing. Now, what we can do is to avoid
everytime that every time that we want to run the test. To avoid that we have to go here and
do yarn test and 20, we can set the test to be kind of automated. So the run automatically
as soon as they detected change in our code. So we go back to package json and say just watch.
One important thing that we need to say as well is to disallow the cache of the test in case we
don't run into problems where some resources might be cached. So that's fixed as well, let's
go ahead and run it and see if that works. So now it should be running continuously. And if
we save, nothing would happen. But if we run a new test, it should automatically recompile it.
So it will detect if we have any new tests in our application. And if I press A is going to run all
the tests again, that's pretty much just usual, just I would say flow, you press W to get more
options. But for now, that's all we need to do. And let's delete that test and, and see if we
can connect to our test database right now. And let's add a couple of things. Basically, what
we are doing here is that we are simulating the the server. So anything that we have defined
in the main Ts, we also need to define here. In our testing module here we use Global pipes.
If we don't include those, the detail validation in our tests will not work. So we need to also
include those just before here. So now that we have the module compiled, we need to run a win to
create a NES GS application testing application. So we can say app is equal module, dot ref. Create
module ref, create nest application, right, so he's going to try to emulate an app. Here, it just
compiles a module that can be useful when you run integration testing. But since we really want
to run an end to end testing, with that, also app we can abstract it in name in the describe
here, I nest application and app will be this. And now we can include the pipes. Otherwise,
the validation pipe will not work. It needs to be as close as possible to our real server. And at
the end we say await up in it. So this is where we kind of start the server and when We are done.
When all the tests are done, what we do is that we do app close. So we close the app. And we see
that now it is still working, everything is good. This is our starting logic. This is our teardown
logic, we can close that. And now let's think about the database. Well, by default, NES GS,
in our case, will connect to our dev database, we need to have a dedicated test database for our
test and a dedicated database for everything else. So how are we going to do it? First of all, let's
create the dev DB in our in our Docker Compose. So just copy here and called sorry, test
dB. Test DB is going to run on port five, and 5435. So it should not be running on the
same ports or otherwise you will have a problem. And the rest can pretty much stay the same. We can
now go back to the package that Jason and we see that we have a we have the scripts for the dev dB,
right? Well, we need to have the same scripts for test dB. So let's copy that and write
it here test to be your third. So and and just copy all the scripts here. And
those will be for test for is not test deploy dB. Test, remove and DB test up. So for the
test DVD, it's going to be just test a B test, the B prisoner deploy. And here
in the to start is going to be test, remove yarn DB test up, and er prisoner test,
deploy. Okay. One important thing though, is that Prisma does not manage the the environment
variables by default. So here is going to just load the environment, the database URL from the
environment file that it detects. So I need to be a bit more specific here. This means that
prisoner will try to find that variable from the environment variables. And if it doesn't find
it, it will try to get it from the NV file. So one thing to work with Prisma with
different environment variables, because you can just create another.in the
development that indata testing, that would not work. Well with that the cleanest way I found to
manage environment variables with Prisma is to use a NPM package called dough 10. CLI, so let's go
ahead and implement it. Yarn add the 10th. CLI. So what is going to allow us to do is
to inject the dot env file of our choice in our kind of scripts. So by default, if we just
stay on the environment variables is going to get this environment variable. So if we don't do
anything, everything is going to be loaded immediately from this one, right. However, if we,
if we want to use another environment variable, such as in the the test, it's a bit more tricky
to force or all our code to use this one, because Prisma will automatically only get the
dot ends, it doesn't know about the end the test, and it cannot know about it. The Config Module
from the API tutorial can load that specific one. For instance, it has a variable called end file
path, and you can specify where the environment variable ease, but we will have to create a lot
of filler code and it's going to be quite ugly. So it's better to use the 10. CLI. It keeps our code
clean. And we don't need to change anything here, we just need to include that library here. So
let's go ahead and do that. So if we are using the dot env development, we don't need to use
the 10th CLI because by default is going to be using this one and this one will override any
any other environment variables file. However, if we use the test ones, we need to inject
this one first. So it's very easy to be honest, you do you just do the n the E and you just
provide the path to the end file environment file and dot txt And then you just execute the rest.
This is this is specific to Docker. So Docker does need to be aware of the environment
variables. However, when we run our test, we also need to make sure that the NESs GS is
loading the right, the right environment variable, not this one, but this one. And for that we also
need to include here. So we include that end, hyphen, e dot and the test on the test end to
end. And on the deployment of our migrations, right, and let's come back to a dot and
if the test and just they just clean that here, and implement that new string, but instead
of the Port 5434, we're going to have the Port 5435, which is the right port. Now to test it all,
and this is the moment of truth. Let's go ahead and kill it and do yarn test, end to end. Also, an
important thing is that we need to create a hook for test and twin. So when we run the end to
end test, it needs to run the DB test, restart. So it's going to clean the database, the test
database, and it's going to push the migrations on that test database. And it's indicated in
the dot n dot test. And to define a hook in package that Jason scripts, so we just say
pre. And we put the name of the script here. So if we execute that, this is the hook that's
going to be executed before. And we do yarn, DB test, restart. And just to make
sure that everything is correct, the B test restart. Okay, let's try
that out and see if that works. So we see that the hook is working. And everything
seems to be correctly working now to make sure that the migrations have been pushed into our
test dB. So let me just open a new terminal session, and I just do docker ps. And we have
now two databases, one that has been created just just now. And it is the database
on port 5435. It is our test database. And this is the old database on port 5434.
So what we want to do is to make sure that this database has the migration, all we need to do
is to use Prisma. To do but remember that Prisma is also using this the 10th file. So what we
can just do is to force Prisma studio to use this one with the same technique that we have
used here. So that N E F test, let's copy that. And it's MDX because we are running it
from the terminal. And we do Prisma studio. And that should automatically connect us to
to the right database. Now we see that the studio is blank. However, if we connect
to the to the other one with dot length, will be connecting to the dev database. And here
we have some fields that we have been testing with insomnia. So everything is working correctly,
we can clean Prisma studio, we won't need it for our tests. And let's come back to our testing
logic. If you have any questions regarding that process, feel free to drop them in the comment
section below. I will try to answer as soon as possible. But all the code here will be available
in the GitHub repository in the description of this video. So if you are stuck somewhere, just
check there. And I'm going to clean here because everything works correctly. And yeah, let's start
our testing process. So now we're connected to the test DB via this environment file. Everything
is okay. Let's go back in our in our testing module. So what do we need to do? First of
all, first of all, before running our tests, we need to make sure that the database has been
cleaned. And of course, every time we run our end to end tests, the whole database is actually
restarted, so cleaned and restarted. But when we run our tests in the same kinda just session, we
also need to clean the database. We don't need to restart the Docker container because that
takes time that takes like three, four or five seconds. And we don't want to wait all that time.
Right? So what we want to do is that we want to tell Prisma to clean everything that is in our
database every time we run the tests, right? So because we have a just end to session that is
watching. So that is keep going, it doesn't stop. We need to do it manually. So it's
quite easy to be honest to do it. But let's first examine our schema to make
sure that we are doing the things right. So what should happen here? Well, every time we
do we run our tests, we need to delete the users, and the bookmarks. And sometimes there could be
a problem where for instance, of the bookmark is linked to a user, right? But what happens if
you delete the user first? Well, if you delete the user, first, the bookmarks suddenly might
be without a user. And that could be that could be problematic, because that can create errors,
to make sure that we don't have any errors. And that elements are deleted in the right order, we
can either tell Prisma to delete first bookmarks, and then the users. But that can be a bit
daunting, because imagine you have something like 20 models, so your teardown logic of your
database can be quite long. Or you can do something like that you can say, on Delete. And we
just say cascade, that means when the element that is it is the kind of the parent of that when
the parent model or that bookmark is deleted, the bookmark should be deleted as well. To
implement that we need to run migrations, I'm not going to do that, I'm going to go with
the other option, where I'm going to make sure that the bookmark is deleted before the user,
but know that it is possible. And in some cases, it is really good to have a on delete cascade,
and it's unless you really want to conserve the data in your database. So for instance, you
can in an app have categories and posts. Let's say that the user decides to delete a category,
well, you can say what happens with the posts in that category? Are they kind of
set to are they just do we keep them? Or do we delete them as well, in our case, if a
user has been deleted, the bookmarks should kind of be deleted as well, I don't see why would we
keep the bookmark so undelete is set on cascade. But in some application, you might want to keep
the user data, you know, you never know. But know that it is possible to have those kind of hooks
on prisoner, but I'm not going to go with it. Because I don't want to run the migrations,
I'm going to just show the other way. The other way is to go into Prisma. Service. And
we need to implement a new function that's called Clean dB. And what does clean dB? Well, we need
to tell it what it does. And we need to delete the bookmarks and the users. So we can do return
this prisoner. Sorry, actually, user, it works like that, because this is actually prisoner. So
this is the Prisma, client, user, delete many. So what do we need to have here, we need to
delete all the users. And we need to delete all the bookmarks. And of course, since we need to do
it in order, it needs to be first bookmarking. And you're right, except that Prisma kind of optimizes
our, our request and could delete the user before the bookmark. So to avoid that we can use
transaction a transaction is when we tell Prisma, to make sure that the things are
done in the specific order. So this transaction, and we just provide an array and
elements will be executed one by one. So we first need to delete the bookmarks, and then we delete
the user. So we don't have a case where a bookmark can be without a user. And we just need to return
that. And that's it. That's all we need to do. This is our teardown logic that we need to run
before our end to end test. And let's come back to the end to end test in before all hook,
we need to make sure that in that logic, the database is reset. So how do we get the database?
Well, it's actually quite simple in Nash, yes, because it uses dependency injection. So you
can do app prisoner. Let's just declare our Prisma service here. And now we can say,
we can say prisoner is equal to app get, and we can get any provider that we want. It's
called Prisma. Service. And it's going to just get that Prisma service and we can put that into the
variable and we can call that variable like that await Prisma clean dB. So it's quite cool to do
tests with Nest Yes, because yeah, it's just a nice experience. And we can come back here. And we
see that property, clean DB does not exist on type prisoner service. That can happen sometimes
when you write code, and you didn't allow Nigeria to recompile the files. So in
our case, we, I think we need to just restart the server. And that should work. So if you are doing major changes to
the underlying logic to the services, you might actually need to restart the end to end
test because it needs to compile the files into into JavaScript, right. And that's it. And let's
go ahead and now create our end to end logic, we can close that collapse this, we don't need to
touch it anymore. And we don't need to touch it either. Because as soon as we close the app, the
prisoner connection to the database is also close. So we are quite happy. Now let's create our tests
of describe what do we need else we will have user and we will have bookmarks. So you notice that I actually write the tests
first, before we even have a for instance, bookmark logic is because that will allow us to
kind of have a structure already and think about our application before we even build our code.
So what should be in the oath? Well, it should be sign up. And there should be signing and that's pretty much all the race, then we
should be able to get the user get current user or more like get me then we should be able to edit
user. And for the bookmarks, we should be able to get bookmarks create bookmark we
should be able to get bookmarks. We should be able to get bookmark by ID maybe
edit bookmark and delete bookmark because that way we will have our CRUD application now next year's
complaints because we haven't created a single test. Let's go ahead and just create a
to do I think it will be happy with that should sign up. Let's see that
works. Okay, that works. So we have should sign up we
also have should sign in. And let's start by that. So this is kind of
our testing structure. Now we're going to run our tests right our tests actually
and for that we are going to use Pakhtun so to run the tests was packed on we first need to
import it with import all as Pakhtun from factum. So after that we have imported Pakhtun. We can
now use it in our application. But factum is a request making library, it needs an API, it
needs the server to make requests. And so far, our servers, our NSGs app, it just has initiated
the nest application context, we also need to start a server by providing a weight dot listen.
And we can provide the port at which we want the app to listen. And now we can do the request
so we can go into our first route a to do and we can provide a callback function to that test.
And what do we call we call return Pakhtun spec. And then we provide the the request type is a post
request to OD two to HTTP localhost 3333 Odd sign up and then we need to provide the body as well
with body and the body will be the to the odd DTO and it will take an email which will be
flat@gmail.com and the password which will be one To free. And we will provide that
email here. And we shall expect a status of 201 Because we're creating a resources. So
let's get back and see, okay, perfect. That test took 42 milliseconds. And that has succeeded. If
we want to see what's inside the request body, we can just call inspect, at the end of the
promise chain. And the body here has been locked, along with the code and the
headers. So you see that we have the headers that is powered by Express, the status
code is 201. And we get our access token. Now, this is a bit verbose because every time we're
going to call the the API, we need to write that this is called also the base URL. So what we can
do is that we can abstract that away from this post request. So we don't need to repeat it in a
subsequent request. So we can do Pakhtoon request, said base URL, and we can just provide that base
URL. And instead of writing the localhost here, we can just write out, sign up, that is much
cleaner, much shorter. And that does the same effect. Let's get rid of the inspect to see if
our test is passing. And everything is nice. So it just took a bit more time, but it
just independent of our of our logic. Okay, now, it's amazing DTO, we are going to
reuse it in our whole Old application, kind of odd flow. So we can just move it above. And let's
also test if the sign in route is working. So we are going to delete it to do here, provide a
callback to be to test and return out sign in. Since it's a end to end test, it will first
execute this code. So we will have a user and then it's going to execute that code. And
obviously it should be too low. And let's see how it works. Amazing, everything works together.
So we have tested that we can create a user and that we can sign in usually when you write a
test, you also need to test the use cases where your test will will fail. Alright. So for
instance, before writing should sign up, we should actually write it should throw, throw
throw an exception, if email empty. So we can also write all that to test if that logic will,
will flow. So we can just copy that. Put it here and say that we expect a a code of 400. So
we're going to write 400 status code to be expected because we expect the error to be thrown
by the validation pipe, which returns a 400 error, bad user input. And we also need to provide a
DTO without the email. So what we're going to do is that we're just going to provide just the
password 123 Actually detail that password. So we reuse that, that detail. And let's see if our test
is passing and we see that the test is passing next we can also check the other way around.
So should throw if password password tempting. So instead of having the password here,
it's going to be email and the to that email and should also throw 400. Right. And we
can also test both just in case, throw should throw if not that you nobody provided.
And I'm just going to do it without the body. And we can do pretty much the same with
the with the sign in. So we can copy that and test it with a sign in here. Except
that it's going to be signing everywhere. Let's go ahead and check our tests. And everything
is passing. And that's pretty much the process of kind of end to end testing. It doesn't really
test how our modules how our functions interact with. So we might return the right status, but
maybe some something in the database is not is not saved in the correct format and that we
are not going to be able to verify that with end to end testing. end to end testing is not the is
not designed for that end to end testing is just there to make sure if the app is working end
to end. So if kind of all the functionalities are working more or less as expected, if you
need to go in more details, you're going to actually create integration testing. And
that's out of the scope of this tutorial. Now, a very interesting part is that we need to
check the user information, right. But for that, we need to send the bearer token. So Pakhtun
provides a very neat functionality that allows us to store a variable in the packet and
execution context. So we don't need to create a variable somewhere, for instance,
we don't need to do something like that access token, and then assign it somewhere
else based on the of what our API returns, what we can do is that we can use the store
API of pakhtuns. So here in should sign in, let's inspect the body response that's going to
be returned. So we have the body and we have the access token. So we can actually save that value
in the memory of Pakistan. So let's go ahead and use that functionality. And it's basically a
stores function stores back request and response data. And we can choose where what we want to
store. So for instance, is going to be user at user access token is kind of like assigning a
variable. And the path to that variable will be access token. So it's going to get the access
token from the body, the access token variable from the body, which is in the Jason and is going
to put that into that variable. And later on, we can reuse that variable in our requests.
So let's, let's do it here. Get me it should get current user return packet on the just copy that
here. And we are going to put users need and it should not have a
body. However, it should have a a an authentication headers. And if we check
the code, right now, let's run that. We see that get current user, we have a status of 404. less
interesting. Why is it? What is going on with it? So users? Has that controller and me, users me,
so why is it not working? That's very interesting. 404 cannot post it's a post request, of
course, it expects a get request. So get. And now it says that it's it's 401
unauthorized because we don't have the access token. Right. So let's get go ahead
and pass the access token. In the same way we have passed them in insomnia. Remember here
in headers we have. We have authorization bearer space and the access token. Let's go
ahead and do that here. Headers, with headers, and we can provide the object key value. So the
key is authorization. And the value is bearer. And to inject that variable that we have saved here,
there's a special syntax in Pakistan. And it is this thing a bit like what we have in in template
strings with JavaScript, except that you have an S here for store. And you can put the name of the
variable here. And normally, we should get a a correct answer. And we can even inspect
that to make sure that we get the correct one. And you see that we got the user object that was
expected. In a nutshell, that's all integration testing is most of the time you will just test
if the status code is correct. And at that point, we have tested all our routes. And what is left
is to actually write the logic for edit user and the write the logic for create bookmarks, get
bookmarks, get bookmarks by ID, edit bookmark, actually by ID, and delete bookmark by
ID. So this is what we're going to do in the next part. In the next section, we
are almost there, we have almost finished our CRUD application. So what we need to
do now is to create the logic for the patch request, and the crud requests in the bookmark
module. Let's first start with the patch request. And we are going to need the service here, we
can leave our end to end in the background. And we can go ahead and create our, our service.
So nest G service, user know spec, it's going to create a service for us that we can now use, let's
put there and we need to create a function here Edit User in the same fashion that we have done
with the auth service. So edit user is going to be probably in a sync function is going to take
several fields is going to take a user ID. And that's pretty much it. And it
also needs to inject prisoner praise Max service. And that's yeah, that's
about it. So once it receives the user ID, it also needs to receive a DTO. And it's going
to be Edit User DTO. Let's create that DTO. So Edit User DTO, the Ts. And it's a class, Edit
User DTO. And what can we edit? Well, we could edit the email, we could edit the first name
and the last name, so email, drink, first name, string, and last name, think and all those
fields can be optional, because we can just edit the email without editing the first name,
or the first name without the email, etc. Right. So we can also provide the class validator
with email. This is going to be a string, string is string. And all those
are going to be is optional. It's optional is optional and mean to export that,
Edit User DTO and exported here through the parent export pattern from Edit User DTO, let's clean
those files and come back to user controller. The patch request, edit user will receive
a body of type edit user detail. And it's going to also get the user and just the user ID.
So Id, user ID and it's going to be a number. And he's going to call the function in the
User Service edit user. And it also needs to have video edit user DTO. And let's just call
that so on that side on the controller side, this is done return this. We have of course
not imported the the user service here, we also need to import it through dependency
injection, private user service, user service. And now we can use that user service here return
this user service added user user ID and detail on on the control side, it is now done. Let's go back
to the user that service and write our logic here. So what we need to do is to update the user with
the DTO. We know that the DTO will either have an email first name or last name. So we can trust
the DTO. Because remember, we have this function, we have this pipe with a widely that's true. So
we are sure that we are going to receive at least something at least something in in in our in
our API request. And we can say user is equal await this Prisma user update where ID is user ID
and the data that we're going to update, we just can just destructure the DTO. So if the video
has something is going to be structured here, and before returning the user, we can delete the
user dot hash and return the user. That's our edit logic. And before continuing any further, let's go
back to our tests and run the test for that edit, edit functionality. So we come back to our tests.
We have one Is that we have user get me. And we have edit user, right? So we go here and just
copy the test here and right should edit user is going to be users. And it's
going to be a patch request. And the headers are correct. And we need also
the body, the body will have it to the to edit user DTO. And let's just edit the
first name of Lada mirror, and just the email. Lat at code with blood, that
and let's push that detail to the body with embody the T O and let's see if we can
get something interesting. So first, let's see this test executes. So here it goes.
should edit user, it is executed. Interesting. And let's maybe inspect that. Where's the answer here
is this. This is the response. So we get the user with the updated email and with the updated
first name, which is quite cool. So that works, we can also make our tests a bit more defined.
For instance, we can say that, Oh, we expect that the body contains a field where we expected
the body contains the first name Vladimir, and an email of that, well, we can just say expect
body contains. And we can provide a value. So it's going to be detailed that first name. And
we can do the same with the to that email. And let's see if our code executes. Cool. Our code
is now working obviously, you can test it out, you can say expect body contains let's say I
know false value. And that should throw an error because false value is not part of the body
value false value not found in response body. So that's a handy way of checking if some fields
are there, without going too much into details. Okay, now let's go ahead and create the routes for
the bookmark. So for the bookmark, we haven't done anything yet. We will need to have a controller
and a service. Let's go ahead and create those. So nest G controller, bookmark,
no spec. And same for the service. Miracle we can clean that out. And here what
do we need? We need to have a couple of routes. Okay, bookmarks, maybe put it here as bookmarks.
We also need to use some guards. So govt guards need to have an access token in order to use those
routes. So that is done at all missing imports. Have we forgotten something else? No, I think
we have adult we can close user controller and just work with bookmarks controller right
now. So we have a route to get all all the all the bookmarks, so it's going to be get get bookmarks. We also have get bookmark
by ID. We have edit bookmark by ID. And maybe delete bookmark by ID. Let's check
our test. If we haven't forgotten something, create all create bookmarks as well. So we get
bookmarks. Get bookmark by ID, edit bookmark by ID and delete bookmark by it and we also need
to have a route for creation. So create bookmark that's it we need to have a positive decorator
for create bookmark, we need to have a get decorator for get bookmark by ID by ID.
Let's move it above so gets are in one place. We also have added bookmark
by ID so it's going to be patch and delete is going to be
deleted. So we have the whole crud crud thing. Let's also import the service because
all our business logic will be in the service. Private book mark So bookmark service, book mark,
service. And let's copy all those functions in, in the bookmark service. And
just clean the decorators here here we go very well, now all of them will
have to use get user decorator, get user. And it will have to use user ID. Number.
Sorry, it's actually goes into here. And we need to fetch the ID. Same goes for here. Same goes for here. And same goes for there. And
the reason why I have to have these ready is that I can check if a bookmark belong belongs to a
certain user or not. So when we are deleting a bookmark, we need to make sure that we are
not deleting the bookmark of someone else. And when we get the bookmarks, we need to make
sure that we are getting the bookmark of that user. That's why we in that, so the Git bookmark
will not need to have a DTO. This will not need to have a DTO, either, but it will have to have a
parameter. So if we want to get a bookmarks or get book marks by ID forces, two, or five, well,
this ID is called the params, we can get it with another decorator. Here around that also comes
from less common, and it's going to get the ID and the Name of that variable will be equal to
the variable that we defined here. And we need to put two dots like that. And similar to what
Express does express also have those parameters. And there's going to be book mark
ID is going to be a number. And by default, this is going to be a string,
but we can convert it to a number with parse, and pipe, the Create bookmark will have to have
a detail. So let's go ahead and create that DTO. Index Ts and create book mark that detail
the TS export class create a book mark DTO. And what will the Create bookmarklet to have?
Well, let's check out our schema. So the bookmark has a title description, which can be empty, no
link, okay, title, description and link. Let's go ahead. And let's add that title is a string
description is a string and it can be optional. And link a string as well. And
we can use this drink validation is drink is drink. And this can be is
optional. And this is is not empty. Here we go. And we can export
that create video from the folder. Now we can close it all open the
bookmarks and add the DTO here as body the DTO will map to create bookmark DTO and edit bookmark DTO will also have something
similar. And I'm going to show you a cool, neat way to reuse our code. And here we have
made an error it should be capital. And it should be capitalized here because it's a class.
And that should be fixed. Above. That's nice. And the next thing we said that we want to have
edit bookmark by ID by ID Okay, edit bookmark. Detail the Ts. So what we can do is to just
copy what we have here added bookmark the deal. And everything could be optional. That's
the only difference here and here. This is this is for TypeScript. No this is for
the validation to know so you don't get confused. We can change title description and link and
we also need to export that detail from our DTO folder. And then go back to here and the patch
request will be edit. Bookmark DT Oh, nice. And we just need to have the one last route, delete
bookmark by ID. Well, it also will take the, the ID here. And we can just reuse the same
logic that we have in get bookmark by ID. Here. Here we go, we have everything that we need
on the controller side. Now let's jump into the service side. Let's jump into bookmark
service. So what do we need to get to bookmark, we need to have user ID, which is a number and
that's about it, right? We don't need to have anything else yet just to user ID, then
we need to get the bookmark by ID, well, we still need to get the user ID. But we also
need to have a bookmark, it should be a number and create bookmark, we need to have the user ID.
But we need to have a DTO of create bookmark DTO, since it's going to be an object,
same for edit, bookmark DTO dot edit, bookmark DTO. And delete bookmark by ID
is going to be user ID as well. And bookmark it. Like, like here, and that's pretty much
all our parameters. Now we can pass our R values here to the service, return this
bookmark service, get bookmarks, user ID, we also need to do the same here,
get bookmark by ID, so just need to change it here. And you also have the bookmark
ID that goes there. And for create bookmark, purchase similar except that the
second argument will be d t Oh. And here as well, it's going to be
the same DTO but it's going to be create bookmark edit bookmark by ID. And
here we we actually have forgotten something. Since it's added a bookmark by ID, you say
the idea of the bookmark at which you want to be edited and you provide the body so
we have forgotten also to put a ID here. And let's just copy it here and
provide the bookmark, bookmark ID and then the DTO. And of course, we need to
go and modify the signature of that function. And delete the bookmark by ID is going to
be your turn this bookmark service delete bookmark by that by ID user ID and bookmark it.
Whew, that's, that was a bit tedious. But we have all the logic pretty much programmed from the from
the controller's point of view. Now let's go ahead and jump into the service to to see how we
can make that work. And we're going to do it one by one. So when you write code, it's
also nice to actually test it straightaway. So you get an immediate feedback on how your code
performs here is going to be very wide because we are running entwine tests. But usually you would
first go with integration tests. But in our case, since the application is quite simple,
that's going to do it. We rerun the test database to make sure that everything is
updated correctly, all the files are generated as they should be. We have our first tests that
are being run. And let's go ahead and add the logic for the second one. So the first thing
is to create bookmarks. Okay, maybe we can, we can first get the bookmarks, get empty
bookmarks first, get empty bookmarks. So because we want to kind of simulate the user experience
on our app, if we imagine what the user would be with end to end tests, let's actually close
that. Close this To make it a bit more readable. So we have the signup functionality, the user
signs up, then he signs in, then the user will get himself. And then he can edit himself. So we
can simulate what the user can do on the platform, then the user will go on a dashboard or something,
and he will get empty bookmarks. Because he haven't created. He hasn't created any bookmarks
yet. So let's go ahead and simulate that. Let's copy this code and simulate
it here. It should get bookmarks. Return Pakhtoon is going to be
bookmarks, it's going to be get requests, the headers can stay the body, we don't need
any, the status should be 200. And let's inspect the result. And we see that the body is actually
empty, nothing is there. And this is normal, because we haven't written any any logic yet.
So we can come back to our bookmark service and write our logic. So let's import
Prisma. Because it's not important yet. Prisma Prisma service. So get bookmarks. We need
to return this Krisna. Sorry, I forgot private, this. This Prisma. Bookmark, return file,
find many where, where what where user ID is equal to user ID, we can just leave it like
that. So it's going to return all bookmarks that are linked to us to the user that is doing
the request. Let's see what it does return. And here, now we have an empty array. And because
by default, when Prisma will run, find many, if it's not going to find anything is going to return
an array, because you are expecting to find a lot of elements, right? So what we could say is that
inspect status, expect body. And here we can say what the body will expect. And it should be an
array, an empty array. Let's see if that works. Then we can go ahead and continue with our logic. And we can write the code for create a bookmark.
So same thing return, it should create a bookmark return Pakhtoon spec post bookmarks with headers
correct. And then we need to provide a DTO. So let's go ahead and provide
a DTO here at that level. Create bookmark the deal. Okay,
the tie the title should be it should be first bookmark. And the link will be
Let's actually put a link to one of the videos of Free Code Camp. I personally liked the
video, that guy who teaches keep your needs are really well done, man. And I'm
going to save this URL in my bookmarks. Here we go. And we put the DTO into our with body. It should expect the status or two or one because
it's a post request. And let's see what it does. Well, obviously it will do nothing, because
because we have not programmed the logic yet. So let's go ahead and program the logic here this
create bookmark. So in the same fashion, we can make it a sink and create our bookmark
const bookmark is equal await this prisoner bookmark create data. And what do we put
here? First of all, the bookmark is created by a user. So we can say user ID is equal user ID.
And then we can just destructor the detail. And then we can return the bookmark to see what it
does. And let's see if our code executes. I'm going to check here and inspect the, the,
the response, let's inspect the response. And we see that we have, we have created the
first bookmark, it is linked to user ID nine, which is the current ID and that is the
second bookmark. So if we do it again, we see that the ID is three is because
while we're cleaning the database, we're not cleaning everything, we just clean
the users table and the bookmarks table, there's another table that keeps track of the
indexes. And that is not clean. And this is normal, that is going to be incremented. But
that's okay. It's not a problem in our case. And anyway, you should not in your test relate
to hard coded IDs, like here. So now we have created a bookmark. And we can go here into
get bookmark and re execute the same code. So let's go ahead here should get
bookmarks. And we can just copy it here. And let's see that code executes. Okay, cool. We had to get bookmarks and wish we
had that. And we can inspect that. And we'll probably see that we have an array.
And in that array, there is our newly created bookmark. So now we should have the get bookmarks
and expect the status 200. Yes. But now we know that the body should be an array, and it should
be one element inside it. So what we can do to make a very simple test is that expect Jason
length to be one. So we can expect the array of bookmarks should have at least one element. So
that's that we can then proceed and write the logic for getting the bookmark by ID. So the way
to do it is to simply should get bookmark by ID, which will copy the tests that we have already.
So if we want to get a specific bookmark by ID, we can write it like that. But we don't know
the it right? We weren't sure about it. Because as I've just said, we don't reset the ID is the
that has been sequentially generated by Postgres, for elements. So we need to keep track of the
bookmark ID that we have created somewhere. So let's go ahead and where do we create it
should create bookmark, this is where we need to use the stores API, like we have used for
user access token. So stores is going to be book mark ID is going to put the ID field of
the bookmark inside it. So to use parameters with Pakhtun, when to use their own kind
of way of doing so we need to the ad here and with params with path params. And it's going
to be the key is going to be ID and the value is going to be the value that we have saved in
our in our store. So let's see if that works. Very well. But now let's write the logic.
Because we haven't written the logic yet. And we have the function here, get bookmark by ID
is going to be pretty much the same. Apart from we are going to use Find first. And we need to
get the bookmarklet that belongs to a certain ID. And which ID is also a a bookmark ID provided
here. And let's go ahead here. Let's execute our, our logic. And we see that
now we have the right answer. And we can go back here and do some
basic assertions we can say expect body contains and it's going to be what is it
going to be the value, the value is going to be this number which will which is
which is going to be the bookmark ID. Okay, let's see that works. Perfect. Let's now
finish it with edit bookmark by ID and delete bookmark by it. Before we go any further.
Let's just examine if everything is correct, get bookmarks. By user ID, that's fine. Get
bookmark by ID. Yes, that's okay. Create bookmark. We pass the user ID and the DTO that's fine. And
edit bookmark by ID and this is where we need to do a bit more work. So first of all, we need to be
get the bookmark by ID and then we need to check if user owns the bookmark. And then only we
are going to do the modifications. So the first part is quite simple bookmark is equal
await this prisoner bookmark, find unique, where ID is called bookmark
it. And then if not bookmark, or so the bookmark add specific ID does
not exist or if the user ID at the bookmark is not equal to the user ID. That means
that is a bookmark that we try to edit does not own to the current user, then we throw
an exception, throw a new, forbidden exception, access to resource denied. That's it, and
the function should be an async function. And we can clean that out, we don't need those
parentheses here, because we only have one throw exception. And again, it's a god condition.
So it's going to stop the execution of our code below. So if the bookmark belongs to the user,
then only we can modify the the user, then only we can modify the bookmarks. So we will return
the modified bookmark this Prisma bookmark update, where ID is called bookmark ID. And the data will
be DT Oh, okay, just destructure the DTO here, and we can come back to our tests and just copy
our test here. should edit bookmark by ID should. Edit bookmark is going to be a patch bookmarks
by ID year. And we need to take a detail. So the detail is going to be here and the
to edit bookmark to and we're just going to add the description. And the description was
actually the we're going to add the title and and the description. So let's just copy this and we pass that DTO to the body with body. Here, we expect the status of 200. And let's
first execute it and inspect that to make sure that we had the right things. Awesome, we had
right things and we can just expect the title and description to the to build those values.
Let's I see that we have a whitespace here. Let's expect body contains DTO dot
title. And same for detailed description. Let's see our test. Awesome. That works. And now let's run our last test. So usually when
you delete a resource status that is return is a two or four. Let's see if we can we can actually
do that. So first of all should should delete delete book, delete bookmark. So it's going to
bed delete with path params IDs with headers, correct. We don't need any body. We
don't. And let's see if that works. Okay, one thing that I like to use when I delete
resources, I like to have the code to have four. So we're going to modify a bit HTTP code, like
we have seen before HTTP status, no content which is which stands for code to have four. And
if we go back to the test and run them again, we now have an error. We can
of course go ahead and just modify that and we can return back to
our bookmark service and feel the last logic here. And we don't want to return anything.
So it's going to be a sync function. While here, we also need to check if the user owns the
bookmark. So we can just copy, copy it here. And if the user owns a bookmark, then
we can go ahead and delete it. So await this prisoner bookmark delete,
where ID is equal bookmark ID. And that's pretty much it that will
delete our bookmark. Let's come back here. And just after the Delete, let's make another
API request should get empty bookmark. And what we can do is that we can just
copy the should get bookmarks here. And and expect the Jason length to be to
be zero. Let's come back and run our tests. And yeah, amazing. We now have a entwine tested
crud API with authentication. So I hope that you enjoyed this tutorial that you have
learned something out of it. Of course, there's a lot of stuff that we can still do. We
can work on security. When we deploy our API, we can run it in the cluster mode through pm
two. And we can do a lot of amazing things with that library called Ness GS. Furthermore, to
improve the code coverage of this app, we can add integration testing and unit testing. But so
far, my mission here is accomplished. I have taught you how to use NES Yes, and how to make
a solid use of its architecture. My name is Vladimir, I hope you liked this tutorial,
and see you for more ad code with flutter
Thanks. I'll be checking it out after work.
Great tutorial!