Transcripts
1. Class Introduction: Welcome to the
ultimate noJS course. In this course, you will
learn all about nodejs from basic to advance in simple and easy to
understand language. At the end of this course, you will start building fast, scalable and secure
pend applications without getting confused. Let me tell you
some major features covered in this course. So you will learn
database connection. Pagination and
infinance scrolling, user authentication,
as well as sign in with Google and sign in
with Facebook features, sending emails from our backend, uploading multiple images, handling and logging errors, apply full payment
integration using paper, and also real time
chatting features using socket sending
one to one messages, showing typing indicators,
handling message status, is it sent delivered or seen, and also group chat features. Now for learning these
advanced concepts, we need a strong base. So first, we will
start with learning nodejs basics like
core concepts of node, built in modules, Express, which is the most popular and
most used nod Js framework, some JavaScript
repressor concepts like callbacks, promises,
missing await. Also, we will see
Mongo DB and mangoes, and if you understand
basic concepts clearly, then you can implement
any advanced topics of node jazz very easily. Also, I want to clear one thing. In this course, we are
not going to create front end because it is not
the scope of this course, but we will taste some features with front end so you will get clear understanding of full workflow of these features. I will give you front end
ready code for tasting, so you have to just
run that file and you can taste some features.
It is really fun. So if you know a little
bit about node jazz, or you are confused in concepts, if you don't know
anything about no Jazz, then this course is for you. During this course, we will build three
applications back end. First, to do application for
learning basic concepts, and then two major
projects ecommerce application and social
media application back end, for learning advanced
node Jazz concepts. Now you might ask who am I? I am a software
engineer and also teach programming in easy to explain language using
my YouTube channel, God bless you, and with
my online courses. Overall, in this course, you will get 200 plus
SD video lessons, real world implementation, bunch of exercise,
so you can learn no Js with practical
implementation, some tips and tricks,
and much much more. After completing this course, you will write No JS code with confidence and using
best techniques, so quickly enroll and become
no Js o to No Jazz hero. CU in this course.
2. Section 01 - What is NodeJS: NodeJS is the most
popular for developers, but it is not a
programming language or it is not a framework. Then what is really nod
Js? Let's see that. Nojs is simply a runtime
environment for running JavaScript code outside of the browser. What
do I mean by that? So as we know, we use
JavaScript code for changing the data on the web page or
implement some logic, but we can't run Ja Script
outside of the browser. Have to link our JavaScript
file with our SDML file, and then we can run
it on our browser. But with nodejs, we can run JavaScript code outside
of the browser. Nodejs provides a
runtime environment for running JavaScript code. Developers mostly use Nojz
to build backend services called API or we can says application
programming interface. Now you might ask
what is an API? Basically, it is a way for two programs communicate
with each other. Me explain you
that with example. So here is a restaurant. We are sitting on the table n, and we want to order some food. In this case, what we will do? We cannot directly go to the kitchen and order
our food to chef. Instead of that, we
will call a waiter. Waiter, will take our order and then give it to the kitchen. After that, kitchen
start making process of our order and give
food to the waiter, and then Waiter will deliver
the food to our table. So here, Water is like
a messenger who takes our request and pass that
request to our destination. And then Waiter will
get reply message from that destination
and bring back to us. So in real world, this
table n on which we are sitting is our client
application or front end. We want to get some data about food which is available
on the back end. We will call Waiter, which is an API and send requests to it. Now, API will transfer that request to
server or database, which is our kitchen, and that server or database
will share the response, which is the data we want. And API deliver the response
data to our client. Now you understand
what is an API. API is a way for two programs communicate
with each other. With NodeJS, we can
build real world APIs. Also, we can build superfast real time
applications with node JS. These services give power to our client applications like web applications and mobile
applications very useful. Do you know, Netflix,
Paper, Uber, Lin DIN, and many more famous
companies use No Jazz? Yes, because Noe Jaz makes their services super
fast and efficient. Also, P done a great
research on Noe Jaz. I will tell you that
in just a minute. So to summarize, Node Jazz
is a runtime environment, and it is ideal
choice for building highly scalable data related
and real time applications. Now you might ask, there
are many frameworks and languages like Java and asp.net, which can also build
wegen services. What is special about Node Jazz? Why should we learn node jazz? Why it is so popular? First of all, with no jazz, we can build super
fast and highly scalable backend
services with low cost. Also, people did a great
experiment with no jazz. In that experiment, people create its application
back end using no jazz and also build the same application
with Java plus spring. And do you know the result
of that experiment? It is really shocking. They found node
application built twice as fast than Java plus spring application and
with fewer people. In Java plus spring, they have five
developers, then in node, they only need two developers, and still those two developers build application
faster in nodejs. Also, node application
takes 40% fewer files. Also, in Java, they have
5,000 lines of code, but in nodejs, they have
only 1,500 lines of code, which is three x less. So node application
is super faster, 35% faster response time
than Java application, and node application can serve almost two X requests per
second than Java application. This result shock
everyone and people immediately migrated many
services from Java to node Jz. Is really cool, right? Second, with node jazz, we get cleaner and more
consistent code base because with less code, we can do many complex things. Also, it is great
for prototyping, which means node Jazz
is fast and easy to use when building the first
version of an application. First version is
called as prototype. Also, as we know, in node jazz, we will use JavaScript. So if you are friend
end developer, you can easily learn node Jazz without learning a new
programming language. And if you learn both
frontend and backend, then you can switch
your job role as full stack developer and improve your career
and get more salary. And one more benefit of Nojs is Nojz has large ecosystem
of open source libraries, which means there
are predefined code for doing particular things. So we can use
someone else's code and implement their features
in our application. So overall, learning
nodejs in 2025 or in future will definitely benefit you and your
programming career.
3. History of NodeJS: In the previous lesson,
we see No Jazz is a runtime environment
for running JavaScript code outside
of the browser. But let's see how Noe Jazz invented with interesting story. Before 2009, JavaScript was like a superhero trapped inside
a browser like Chrome, Firefox, and many more browsers. In all these browsers, there are JavaScript engine. When we run our JavaScript
code inside the browser, browser pass that code
to JavaScript engine. JavaScript engine
process that code and convert it
into machine code, which our computer
can understand. For example, Microsoft Taj
use Chakra Engine, Firefox, use Spider Monkey engine,
and Google Chrome, use Fiat Engine, which is the most powerful JavaScript
engine among all browsers. Because of these different
JavaScript engines, sometimes JavaScript code works differently in
different browsers. When we use JavaScript
inside browsers only, then there are many limitations. We can't build servers or do Bend task like
handling database, user authentication, or APIs. Now, at the time a developer, Yandll comes into the picture. He thinks, what if we
take Vat engine and let JavaScript run on servers
instead of just browsers. He takes the Vet engine, which is the fastest
Js engine and record that engine
in CplusPlus code. Can run Jascip code in
our machine and call that software nodejs and
that's SOO nodejs invented. Nod Js is a runtime
environment for running JavaScript code because it
has V JA Script engine. This environment is little different than we
have in our browser. Like in the browser Ja script, we have document object for
dealing with SDML document. Also we have Window object. These objects we don't get
into node Jazz and also we don't want to access
document and Window objects because in node jazz, we are creating
back end services, and document and Window objects
are for front end task. So why we need document and
window in the back end? With the node js, we can do
many more things like we can access the file system
for read and write files. We can connect with database
and store data in database, and also we can build fast
APIs and much more things. Now, one question you might
ask how nodejs is super fast, how no Jz works, and we will see that
in the next lesson.
4. How NodeJS Works: So we have seen that
nodejs is used for super fast and highly scalable
backend applications. But how nod Js is super fast. So the reason no Js is
super fast is because of its non blocking or we can say asynchronous nature of node js. Now you might ask
what is asynchronous? Let me explain you
with simple example. Imagine here is a restaurant. We have one kitchen here and
we have multiple tables. Now first customer
wants to give order. So here, our waiter, go to the table one, get order. Let's say one pizza. And then our waiter give that order to the
chef in the kitchen. Now while sef making pizza, our waiter will look
at other tables. Let's say Table two wants
to give another order, then waiter take that order and also give it in the kitchen. Our single waiter can
handle many tables. He don't need to wait for
chef to complete one order, and then he can serve another order. He
don't need to do that. This is called as non
blocking or asynchronous way, and this is how node
application works. This single weiter is working as single thread of node JS, whose task is to handle request. So this single thread can use to handle multiple requests
without blocking. Now at the other side of
this asynchronous way, we have also synchronous
or blocking way. Let's understand that also
with the same example. So here, customer Vn
wants to give order. Our waiter take order from
table one and give it to the kitchen and our self start preparing for
that order like pizza. Now, as we know, pizza
can take time to prepare. At that time, instead of
handling other tables, our waiter wait there in the kitchen for
complete that order. Suppose it takes 10 minutes, so our waiter will wait there for 10 minutes
in the kitchen. After that, deliver that
pizza to table one. Now, at that time, customers
on other tables have to sit because our waiter is
blocked for 10 minutes. This is called as blocking
or synchronous way. Most of the older back
end frameworks like w.net or Rails works in
synchronous or blocking way. So when someone send
requests on the server, server allocate one thread
to handle that request. Our example, it is weiter. Now imagine we have request for getting
the list of users. This task needs database
operation which can take time. Now, if at that time we
get another request, then to handle that new request, we need to also give them
a new thread or weiter. Now imagine on our server, there are 1,000 requests. Should we create thread
for all those requests? If yes, then we have to increase the number
of server hardwares, which is really costly and slow. That's why applications
which are built on asp.net or rails need more number of servers
and more maintenance. Now, at that time, node
comes in the picture, and as we have seen, node use asynchronous
or non blocking way. Of course, we can
make asp.net or Rails application works like non blocking or
asynchronous application. But for that, we need
to do extra work. On the other side, node
applications are works by default asynchronous
or non blocking way. So it is really fast
and easy to manage. When we get multiple
request on node server, node has single thread. That single thread can
handle one request. Now, if that task takes time, find something from database, then our single thread add
that task in the list called event Q and move to another request and start
handling that request. This event Que will notify node when they get data
from the database, and then our node will send
that data to the request. So in this way,
our single thread can serve multiple users, and that's why nodejs
is a great option for data intensive or network
related applications. Can serve more clients
without adding more servers, and that's why node applications are also highly scalable. Now the question
is, should we use node Jazz for all
types of applications? And the answer is no. We should not use node for
CPU intensive applications. Now, what is the CiPi intensive? CPU intensive means
applications which have tasks related to CPU like video editing or photo
editing applications. In these types of application, we have a lot of
calculation that can use CPU few options, which does the file
system or network. And as we know, node is a single threaded
application when it performs CPI related operations when other clients has to wait
to complete that task. Here, node will lose its
advantage of being asynchronous, and that's why we should not use node for SIPEintensive
application. Can use node for input output, network related, or real time
data related applications. Just remember we should not use node for CiPi intensive
applications. Otherwise, node can use for mostly all kinds
of application. So we understand how nod Jazz is faster than other
back end frameworks. Don't worry about the theories. As you build projects
in node Jaz, you will understand
all the very easily. So let's start learning
node Jaz practically.
5. Installing Node JS in System: Let's see how to install
NodeJS in our system. First of all, we will check if Nojs is already in
our system or not. So open up Command
Prompt in Windows, and if you are
using Mac or Linux, then open up your
terminal and write node, dash dash version,
and hit Enter. See here I get this
kind of error message, node is not recognized as an internal or
external command. This means node is not
installed in my system. If you get here nodejs version, which means node is already
installed in your system, but I highly recommend
you to install the latest table
version of node JS. So head over to browser
and opennjs.org. On this website, we get a
download Nodjs LTS version, which means latest table version which node officially
recommend to use. At the time I'm
recording this course, latestable version is 22.14 0.0. But in the future, there might be updated
version available. So simply click on this version, and download will start. One question you might ask, is this course relevant for
the future node versions? And the answer is yes. This course will
stay relevant for any node JS version
because in this course, we are going to strongly focus on the fundamentals of node JS, and you can use that fundamentals
in any node JS version. So you don't need to worry
about no JS updates. If any major updates occur, then I will update this
course according to that. Now our setup is
ready to install. So let's open it up. Let me put here Click on next, accept the terms and
condition and click on next. From here, you change
the installation path, but in my suggestion,
leave it as it is. Again, click on next
and next and install. And it's done. Now, let's check Nojs is
installed successfully or not. So back to command
prompt or terminal and write nodes version,
and hit Enter. See, now I get here nod
Gs version 22.14 0.0. So we successfully install
node in our system. Another thing we need in
this course is code editor. Personal favorite code editor is Visual Studio code or VS code, which is one of the
best code editor. Of course, you can use
any other code editors, but not many editors has that
power which VS code has. And also, in this course, I will share my tips
and tricks for VS code. So if you don't have VS code, then you can go to
code.visualstudio.com, download VS code and install it. It is really simple.
Now in the next lesson, we will write our
first node JS code.
6. Writing first node code: Let's write our
first node JS code. Open a folder in which you
want to create a project. Here, I open Project
folder, and in this folder, I create a new
folder, let's say, first project and simply open
this project in VS code. Good. This VS code looks a little bit smaller for this
course. So let me zoom it. Usually, I don't
zoom in this much, but with this Zoom, you can
see what I'm doing clearly. Perfect. Now let's
create a new file here. Let's say index dot js. You can use any name. It's totally up to you. Now inside this file, we can write our normal
JavaScript code, which we are used to write. So let's simply create a variable called greeting
equals to good morning. And after that, we
simply consult that log. Hello comma and simply
adhere greeting variable. Save this file.
Now you might ask, how can we run this code
without our browser? Because previously, we are running JavaScript
using browser. So to run this code using no
chairs, we need terminal. And use our system terminal and open our project folder
in that terminal, or we can use VS code terminal. I always use VSCode terminal
because it is easy to open, go to the terminal menu
and select new terminal, or you can press
Control plus Pecti. It will open here terminal. Now to run this
index dot js file, we simply write node, space, and our file name, which is index dot js. Your file name is apt Gs, then you have to write here nodspace app dot
js and hit Enter. See, here we get
our Console line. So congratulations. You write
your first nodejs code. Now to hide this terminal, we can again press
Control plus Bectig. Now here we can also add
function and call that function, same as we are doing
in regular JavaScript. So after console dot
log in the new line, we can create a
function print message. And in the Cully brackets, we simply console dot
log. Have a nice day. And after this function, we simply call this
print message function, so we can get this
console message, save this file, and to
again run this file, what do we write
in the terminal? Write, we write node
space index dot js. See here we get this
both Console Loui. So we can use here
set timeout function, set interval
function, et cetera. We can add almost
all JavaScript code in the node application. Now you might ask which type of JavaScript code we can't add
here in the node JS project. So in NodeJS project, we can add all types
of JavaScript code, except writing doom
manipulation code like document dot Get element or window dot
location, et cetera, because as we know,
whatever code we write in node application, will run on server
and on server, how can we access document
and Window objects? Let me show you practically
what will happen if we use document in this
index dot JS file. I comment out all this code
using Control plus slash or Command plus slash and simply right here,
console dot log. Print here document object. Save this file, and
in the terminal, we again run node index dot js, or we can simply press a perro. It will bring previous
command for us. See, here we get error which says document is not defined, and it is A showing us the line. So we can't use here
doom manipulation code. Another thing is
we also can't use here browser specific
events like Alert. So at the place of this console
we add Alert, say hello. Save this file, and let's
run again this file. See here we get Alert is
not defined because again, these are browser
related events. Server can't show
Alert in the browser. It is the front end
who can show Alert. So to sum up in index Js file or any other
files in node project, we write JavaScript code for server we can use
various modules, handle SDDPRquest, access our system files,
connect to database, build real time applications, and many more things which we will learn step by
step in this course. Don't worry, you will master
node after this course. Just you have to code
along with me and try to write code after you
understand it properly. Not just copy and paste. You have to understand
why we write that specific code and
then implement it. It is really simple.
7. Make VS Code Cool [OPTIONAL]: In the previous lesson,
if you wonder how my code gets reformatted
when I save the file. For that, I'm using one of the best and most popular VS code extension called pretty. Install this extension. Now we have to do
little settings for installation of Pretty E. So
in the installation section, scroll down to the default
formatter section. And here, copy these two lines of code without Cully brackets. Now, open up settings from
the bottom gear icon, go to settings, and at
the top right corner, from here, open settings,
touches and file, and at the last line, add comma and then in the
new line, past those lines. See this file. Now
back to settings and search format on save and make sure it is
checked and done. Also, many students ask me which theme and fonts
I use for my VS code. So currently, I am using
my favorite theme, which is Au Mirage Warder. You can download that
from the extension panel. This theme is not very
bright or not very dark, so it's good for our
eyes and it looks good. The name of the font which
I'm using is Monisa. Which is the paid font, you can download this font
whichever way you want. I can't say anything further. Install them in your system. And then from the
VS code setting, search here font family and add your font name at the very
beginning, and that's it. You can use any theme
and fonts you like. It's totally up to you. And if you have a great combo, then you can add your
VS code screenshots in the Q&A section. I love to see that,
and that's it. We are ready to go. So let's
dive into this node JS code.
8. Section 02 - What are modules: Welcome to the second section of the ultimate node JS course. In this section, we will
learn everything we need to know about modules
like what are modules? How can we create
our own modules? Also, we have some built in modules like path,
operating system, file system, DTP module
for creating server, and much much more. Let's start with
what are modules. But before that, let me
give you one situation. In the previous section, we created this index dot JS file. Now imagine this is the
Began project of Netflix, which is the really big project. This project has many features
like database connection, user authentication,
payment gateway, also many other APIs. Now imagine we add this all feature code in
single index dot G file. How will you manage
this? Or if someone told you to add another
feature in the same project, then imagine how confusing
and difficult it will be. What is the solution
here? Think about it. Here, we can separately create these features in
different files and then simply input them in
the main index NodeJS file. These different small files are called as modules in node JS. If I had issue in
database connection, I can go to the database
connection module and resolve or improve that
code independently. To sum up, module is a piece of code that perform
a specific task. Example, imagine you
are building a car. A car has many
parts like engine, wheels, sheets, et cetera. Each part is built separately, and also it can be replaced or repaired without
affecting the entire car. Now in node JS, modules
work similarly. Each module represents a different part of
our application, and these modules can work together to form a
complete application. And that's why modules are very important part of node JS. Now we can divide
modules in three types. First one is local modules. These are the modules
which we will create for our own application like DB module for
database connection, payment module for handling
payments, et cetera. Second one is core modules. These are the built in modules
which we get with node JS. They are already available
in all node applications. For example, FS for file system, STTP for creating STTP
server or making STTPRquest. OS for operating system related functionality,
and many more. Don't worry, I will explain you our core modules in the upcoming
lessons of this section. Now the third one is
third party modules. These are the modules which are created and published
by other developers. If we want to use them, then we have to manually install those modules in our project. For example, express dot
js is third party module, which helps us to
build API fast. Another module is Mongoose
for Mongo Div et cetera. These are third party modules
built by someone else, and we can also use
them in our project. Don't worry about
all these things, we will go step by step
and learn each of them. So to quickly summarize, module is a piece of code
that perform a specific task. We can store them
in separate files, and then we can use them
in any other files. Now in the next lesson, we will create our own module.
9. Creating own Module: Let's build our
own custom module. So here in our application, we create a new file called
math operations dot js. In this module, we will add some basic math
operation and then reuse it in our main
index dot js file. So first of all, let's
create a new function called AD and pass here two
parameters called A and B. And inside this function, we simply written addition of these two
parameters, A plus B. You might ask, how can we use this function inside
our index dot js file? Because we all know when we define function inside one file, we can only use it in that file, not outside of the file. So to use this ad
function in other file, we have to export this function from this math
operations module, and then we can import
it in index dot js file. But before that, let
me show you something. So in nodejs all files have one object called in
that module object, we have many
properties which gives information about that
particular module. Let me show you
that practically. After this function,
we simply write console dot log and print
here module object. Now we have to run this
Mth operations module. So open terminal and write node, math, and press tab. It will autocomplete
the file name. See, here we get this module object with
a bunch of properties. First one is ID, which is the unique
ID for module. Next, we have path, which is the full
path of our project. After that, we have export and
it is set to empty object. You guess correctly, if we
want to export add function, then we have to add their
function in this export object. After that, we have
current file name with full path and bunch
of other properties. Now, how can we add add function inside
this export object? It is really simple. So here we write module
dot exports dot add, which is the property name
equals to add function. Make sure we don't call
here add function. We just add here function name. I know you think this
is a little confusing, but trust me, it is not. Let me show you
that. So simply move this console dot log below
this module dot exports. Save this file, and
in the terminal, we again run this file. You can see in the
exports object, we have add property and
it is set to add function. So if you confuse by
the same property name, then we can also change here property name like add numbers. Save this file, and let's
again run this file. See, here we get ad numbers property
name to add function. Now we can also export more than one functions
from the module. So here we create one more
function called sub track. Again, we need here
two parameters, A and B, and inside
this function, we simply return A minus B. Now, can you tell me how
to export this function? Right, we simply write here module dot exports dot subtract equals to our
substract function. See if the changes
and take a look. Let's again run this file. See, here we get this new property substract
to our abstract function. Also, it's not necessary that we can only export functions
from our module. We can also export variables
or objects or array, basically anything we want. Suppose here at the top, we create a variable
called name. Equals to let's say code
plus U and another variable, lucky number equals
to, let's say seven. Now, if we want to export
only the name variable, then we can only add here module dot exports dot
name equals to name. We don't need to export
the second variable if we are not going to use
it in any other file. Only export variables
or functions which we need to use somewhere
else in our application. Now here is one thing. These three lines of
code looks a little bit ugly because we are repeating
module dot exports. There any shortcut
way to write this? Yes, let me show you. So here we can write
something like this. Module dot exports equals to, and here we pass object and add all properties which we want to export in this single object. So we write, add, cool, function, add
another property, substack colon function, substack and we can set
name to variable name. This code and these three
lines of code works the same because here we are
setting properties one by one. But here we directly
set them in object. Let's remove these three
lines. We don't need it. Save this file. Let's
clear terminal using CLS, and then run the same file and see here we get the same
export object as before. So if we know when our property name and
passing value name is same, then we can remove this
column and variable name. So this add means
add column add. Same as we remove this column substract
and also column name. So there are multiple ways
to write the same code. Save this and let's
run it one more time. See here we again get
the same export object. Now we have properties in
the export object and we can access this export object in any other file
in this project, and we will see that
in the next lesson.
10. Accessing module in other module: In the previous
lesson, we created our math operons module and
export three properties. Let me also remove this console
line. We don't need it. Now let's see how we can access these properties in
the index dot JS file or any other file. Let's remove this previous code. We don't want it. Now to
import any module in our file, we have required
function in node Js. We call this function here and inside this
function in codes, we will enter our module path. So as we can see these both
files are in the same folder, so we can write here
dot forward slash, which represent current folder. And here we write our file name, math operations dot js. Or we can also remove
this dot js extension because if we don't pass
extension with our module name, then by default, nodejs will
take dot js as extension. What if we have this math
operations file in sub folder? Then we have to write
here folder name first, and then we add slash file name. And if we have this Mth operations
module in parent folder, then here at the
place of single dot, we will use double dot. Don't worry with practice,
you will learn this. For now, just write dot
forward slash math operations. This required function
return exports object from this module which we get here interminal.
Let me show you. Here, we store this value in variable called math operations. You can take any other name, but mostly, we use the
same name as that module. So we don't need to
remember another name, so we don't need to
remember another name. Here we simply consil
dot log math operations, save the changes, and now we can run this
index dot js file. Node index dot js. See, here we get the object with those three properties
which we added in the exports object
in Mth Operations. Here at the place of
simple math operations, we can do methoperons dot AD. And here we pass arguments, let's say 20 and 30. Save these and let's
run this file again. See, here we get the
addition of two numbers. So we successfully use one
function from other module. You can see how simple it is to create module and use
it in other files. Just we have to export
from that module, and then with require, we can use that exports
in any other file. So we can use other
properties like methoperons dot Substack
or methoperons dot Name. But we can see when we need to use any of
these properties, we need to write this
math Operations dot. So we can use here
Javascript topic called object destructuring. Probably you know about this because it is
pure Javascript topic. But let's quickly see this. So here we write our object and we want to extract its
properties as variables. So at the place of
methoperons dot AD, we just have to write EdD. So at the beginning,
we write Cs now here, we have to use curly brackets
equals to our object name, which is math operations. Now, can you think what we have to write in this curly brackets? We have to write here
properties name, which we want to extract from
this object as variable. We write here add,
which is our function, substract, which is
another function, name, which is our variable. Now, at the place of this
math operations dot ad, we can simply use this add and
we can also add here name. Save this file and let's
run it in our terminal. See, here we get
50 and our name. Also, at the place of doing object restructuring
in another line, some experienced developers like to do in the same required line. So they simply got
this object from here and simply paste it at the place of this
math operations name. So we don't need
this extra line, save this file, and
let's run it again. See, here we get
the same result. If you get confused in this
objective structuring way, then you can simply use Mth
operations dot ad method. It's totally fine. At
the end of the day, our code should work.
11. Exercise for own Module: Now it is time for
little exercise, so you can revise modules. So here you have to create a new module in your
application called Logger. And inside this, you have
to export two functions. One function will return current date with
this expression, and another function will return current year with
this expression. And then you have to console this result in the
index dot JS file, same as we do in math
operations module, and our output should
look like this, give it a try and then
watch the solution. So I hope you complete this exercise or at least you
try to solve this exercise. Now let's see the solution. First of all, we create a new
file called logger dot js. Now here, we have to
add two functions. So the first function
is current date, and in the GLY bracket, we simply return new date
dot two time string. Now let's duplicate
this function, select it and press
Shift plus alter plus down arrow or Shift
plus Option plus down arrow. Let's change the
function name to current year and at the
place of two time string, we write get full year. Now, do you remember how
we export these functions? We use module dot exports, and here we simply set it
to object and inside it, we write current date to current date and current year
to current year function. Or we can simplify them by
removing the same name. Great. So we export
these functions. Now we just need to input these functions in our
index dot JS file. So at the top, we add required function and simply
pass here our module path, which is dot forward
slash because this module is also in the
same folder and logger. Now we can store its export value in
variable called logger. Or as we did in previous lesson, we can destructure it like this. So add here calibrakets
and see here we get current date and
current year properties. Now, after this console, we add another console and simply call here both
functions one by one, current date and current year. Save the changes, and let's run this file
in the terminal. It's a node index chairs, and see here we get our output. So you can see how
simple and easy it is to create our own modules and
access it in other modules.
12. Using Path Module: So till now, we have seen how
to work with local modules. Now let's see some core
modules or we can say modules which are already
available in the node JS. So head over to nodjs.org, and from the header,
open its documentation. Make sure here we select
other version and select the Ts version because
it is the stable version. Here we get all
information about node JS, like simple object, console. Also, we have many topics
explained and how to use them. We have some core modules
like file system. Inside this, they explain all
its methods and properties. Also, we have STP module, OS module, path module, and much much more. Here we will see some
important core modules which we need to
create our back end. If you like to
learn all of these, then you can read
this documentation. But in my suggestion, I will
cover all important modules. Let's start with Path module. Path Module is used to work with file and
directory paths. For example, when we want to store uploaded image
on our server, then by using this path module, we can give that image proper path for saving
them in specific folder. So if you want to merge two paths or other
path related things, then you can use this path
module. Let me show you that. Also, you can see here, they show us how can we access this module
in our application. Same as we are accessing our local modules in
the required function. But at the place of file path, we write our module name. So in our application, I simply remove this code. We don't need it now here
we write require function, and in the codes, we write node column and our core
module name, which is path. In previous node version, we just write the
core module name without node column prefix. Also, this will work
in current version. I use this previous syntax, you can use any syntax, it's totally up to you. Now, same as before, this require function
written exports object from this path module. We have to store them
in variable path. Now you might ask why
we don't destructure here this object as
we did previously? Yes, we can do that same here, but we don't know
its property yet. So it's better to not
destructure here. Now, let me show you some
useful methods of path module. Suppose we want to see some information
about specific file. So here we have path
dot parse method, and in this parse function, how to pass the full
path of our file. But how can we get
full path of our file? So in node js previously we seen all files have
module object, which we use as
module dot exports. Also, all files have
another two variables. One is underscore
underscore file name, which returns the full path of the current file and underscore underscore Dname which is
directory path of current file. Let me show you these two first. So for now, I comment out this path pase method and
quickly consol dot log, underscore underscore
Dname console dot log, Underscrendscre, file name. Save the changes, and
let's run this file. See, here we get the full path
of the current directory. Directory means folder, and below that for
Underscore desceFlename, we have full path
with file name. Now back to our file, we remove this both Console and remove comment
from here using Control plus slash
or Command plus slash and simply in
this parse function, pass Underscredsc file name. We get some details about this file path and
to print its result, we simply wrap it
with console dot log. Good, save the changes, and let's run this file. See, here we get this object. First, we get root property, which is the root path
of the current path. Here in Windows, we get
C colon, backward slash. If you are Mac or Linux user, then you will get
here forward slash. Next, we have DR, which is directory
path after that, we have base, which is the
last part of this path, which is index dot Js. After that, we have EXT
for extension and last, we get name of the last
part, which is index. This parch method is used to get details about
the specific path. Now let's see one
more useful method of path module which is join. Suppose in our database, we want to store the
path at where we store our uploaded image
like profile picture. Understoe that
profile pictures in folder called uploads
in our project. Here we can use path
dot join Method. Here at the first, we need
the path of current project, and how can we get
this folder path? Should we use underscore
underscore file name? No, we have to use
underscore undisco dirname. At the second argument, we will pass our folder name
which is uploads. We will get a path of our
project folder and we join Uploads folder at the end of this project path.
See what we get. We remove this console
and store this path in variable called profile Path and console dot log
this profile path. Save this file and
let's run this file. See, here we get full path. This first part is
underscore underscore, dear name, and we join
Uploads folder at the end. I know this is a
little confusing, but don't worry when we use
these modules in our project, this will all make sense. Now, here you might ask, can we join two parts manually? Why we need to use
your path module. So as we know, for a file path, Windows use backward slash, and Mac and Linux user
use forward slash. So we get different results
in different system. If we manually write path
with backward slash, then in Mac we can't
find our path. And also, currently, our
project is available locally, and that's why we
know it's path. But in real world, we deploy
our backend application somewhere on the Internet and we don't know the
path for that server. So this path module is very useful in this
type of situation.
13. Getting Operating system details: Sometimes we need some details of our server operating system like which operating
system it is using or how much memory
they have, et cetera. For that, we have another
core module called OS, which means operating system. So we remove this previous code, and can you tell me how can we access this
operating system? Right, we can use it by require function and simply
pass here node, column OS. We can use only OS. Now we can store this
in variable called OS. Suppose here we want to run a specific code for Windows
system and for Mac, we want to run something else. Here we can write if
condition s dot platform, this will return
the platform name on which this code is running. If st platform equals to win 32, if this is true, then we
will run code for Windows. For now, we just write console dot Log,
hello, Windowsser. After that, we want to run
some code for Mac system. We add s and checks dot
platform equals to Dawn. This is the platform
name for MacOS. Inside this, we run console
dot log, hello MCUser. At last, we simply pass ls
and Consol dot log Hellouser. Check this is working or not. See the changes and take a look. See, here we get Hello Windows
user, so it is working. Now in OS module, we have many more properties. I don't want to bore
you by explaining all properties because that
will take a lot of time. But let me explain you two more useful properties of OS module. First one, we can
get the total memory of the operating system
using this OS module. So Os dot total MM for memory, and we can simply print
them using console dot log. Also we have another property called free MM for
getting the free memory. So we duplicate this line
using Shift plus alter, plus down arrow or
Shift plus option, plus down arrow and change this total MM
function with freemM. Save the changes
and take a look. See, here we get
memory in bites. I have eight GB total
memory and free memory is almost two GB. You can see this is the
importance of node js. Before node js, by using
simple JavaScript, we can't get this
type of details. So that's how OS module is used for getting information
of operating system.
14. File System Module: There is another
popular core module which we use for
interacting with files. For example, with file
system or FS module, we can write the
file, read the file, update the file, and also we
can delete a specific file. So here in the node
documentation, we open file system module. We can see it has so many
methods and properties. Let me show you how
we can use FS Module. Let's remove this previous
code. Don't worry. I will add all this code in separate file so you
can look at it later. Here we want to
access core module, require code Fs, and we store
that in variable called Fs. Now we simply write Fs dot and see here we get
the list of all methods. But what is this? All these
methods have two variations. One is synchronous
or blocking methods, and another is without synkyword which means asynchronous
or non blocking. Now, as we know, node
is popular because of its asynchronous
or non blocking way. We have to always use
asynchronous methods in node js. Node give us synchronous
method for simplicity. Let me show you one by one both. Here, we want to get the list of files which we have
in current folder. For that, we have method
called read directory sync, and here we pass the path of directory which
we want to read. Period slash which is
the current directory, and we store this data
in variable called data. And at the end, we
simply log this data. Save the changes, and let's
see what we get here. See, here we get the list of all files inside this folder. Now let's also see how we
can use Asynrns method. So as dot red directory, and same as before, at
the first position, we pass our directory path, which is period slash. Now in almost all async method, have to pass second argument, which is callback function, which is the function execute when this asynchronous
work will complete. And also, we can see
that in suggestions. Now, there are two possibilities for all asynchronous work. We get an error or we
successfully complete that work. I know this is a little
bit confusing if you are dealing with
asynchronous work for time, but don't worry in the
upcoming sections, I have complete section for
asynchronous JavaScript. You will learn all
these in depth. Now in this function
at the first position, we get error, and at the second parameter,
we get our data. If we get error, then we get null in this data. And if we get our data, then we get null in this error. So for handling this scenario, we can pass her condition. If error is available, then we will log the error. As we will console dot log
our data. Simple as that. Now let's comment
out this console for synchronous method. Save the changes
and take a look. See, here we get
again the same data. Now, if you want to verify, we get error or not, then we can change this
path to something else, save this and run
this file again. See, here we get error, no such file or directory. So now you understand how
these acing methods works. Also, don't worry
about this complexity because we hardly use these
core modules in our project. Currently, I'm showing
you just modules, and this file module is little
advanced for this stage. So don't worry about
this. You will master it when we use them
in our projects.
15. Creating Server using HTTP module: Let's see one of the most important core
module of node JS, which is SDTP module. SDDP module allow us to create a SDDP server and also for
handling different SDDPRquest. As we know, NodeJS is used to create backend for our
client application, but currently, we only access this backend
in our terminal. What if our front end wants
to access our back end? So to interact with web in node, we have SDDP module. Let me show you rectically
how we can create server. So same as before, access STDP module using
required function and pass here SDDP and store that in
variable called SDDB. Now, can you tell me what is
this STDP module written? I will return SDDB method. Now, this STDP has
one method called Create server which is used to create server for our
backend application. Now what we will pass inside
this create server function? Simply, here we pass
callback function. When someone sends
requests to our server, this callback function will run. Let me ask you
something. What is the main work of our server? So when user sends
request to the server, server has to return response
according to data request. So here in our server function, we need the information
about user request, which API he or she
request for, et cetera. So here, in this
function argument, we get request object, which is the user
request details. Now here we have
information about request. So our server do its
process and suppose we want to return response
as hello world message. For sending response here
in the function argument, we get here response object
after this request object. Also, many developers
like to write short name, Rg for request and
Rs for response. Now to return Hello
world message, we write response
dot R. And in codes, we pass our message Hello world. In real world, we will
return data from here, for now we are just writing message when someone sends
request on the server. And here, we have to also tell that this
response is end here. So we write response dot N. This will make sure our
response process is ended here. Now here we create our server, but we need to start this
server on some port. If you no react, our
application will run on Local host 5173 if
it is created by y. Now, same as that we have to start this server on some port. Let's see how we can do this. Here this stp dot
Create server method, return our server object, we store it in variable
called server. And after this at the bottom, we write server dot LISN. And in this function, we will pass our port on which we want to
start this server. Many developers like to run
server on 5,000 or 3,000. But you can give any port, make sure that port is
not used in your system. Here, I like to use 3,000. In real world, when
we deploy our server, then this port will replaced
by our Bend domain name like catwis.com or tasrag
dotben.com, et cetera. Also, in this function, we can pass another
callback function. This function will run when our server
start successfully. Here we simply write consol dot log server start
listening on port 3,000. Save the changes and let's run this code because without
this code running, how server will create
node index dot js. See, here we get server start
listening on port 3,000. If you're using Mac and
you use port 5,000, then you might get error for
5,000 port is already in use because in Mac ARP receiver surveys
running on this port. Can use another port like 5,001, 5,002, or you can
even use 3,000. There is no rule for port
number as long as the port is already in use by another
service on our system. Now to check this, we need to
send requests on this port. So open a browser and
simply write here URL, Local host Column 3,000
or your port number. See, here we get our
message, Hello World. So we successfully create our server and also
start this server. So whenever someone sends
requests on the server, this function will run and print this message
simple as that.
16. How to handle different routes: Now currently we are sending requests on the
port route route, which means home route. But in real world, user
can send requests to local host Column 3,000 slash
About or slash products. So we have to also handle these different
routes on our server. And for handling
different routes, first, we need information for which
route user sends request. And for that have
here request object. We write here
condition I request dot URL equals to in
codes, forward slash. This simple forward slash
represent the root route. In the CL brackets, we can simply move this response dot write
function withholding alter or option, and up arrow. After that, we want to handle, let's say about route. So here we add si request dot URL equals to in codes about
and if it is true, then we simply return another
message response dot right. This is about route. Like this, we can add as
many requests as we want. And after all routes,
we will pass, which means user pass routes, which is not handled here. And that's why here we return simple response dot
right, route not found. Also make sure this
response dot function called at the very end
of this server function. Save the changes and
back to our browser. If we change our
URL to slash about, can you tell me what we
will get here? Let's see. See, here we get the
same hello world message because in our terminal, still our old code is running in which we
didn't handle routes. To run this new code, we have to again run
our application. In terminal first, we
will stop the application by pressing Control plus C
in Windows and Mac both. After that, we again
run node index dot js. Good, we get here, servers start listening on port, and in browser if we
refresh our page, see, now we get here message. This is about route. And if we try to request
any other routes, then here we get
route not found. That's how we handle
different API routes using SGDP module. Now imagine we have 20 different API routes
and to handle them, we have to write
18 more sf block. And if all those routes has
hundreds signs of code, then managing our own server
will become so messy. So in the real world, we will
not use this SDDP module. Or that we have very popular
third party module called Express and Express has many more benefits
than this SDTP module. Also, using Express, we can divide our routes
in different files, so we don't get confused
when we have to make any updates or we want to
add new functionalities. I explain all these modules so you get little
understanding how node is working and you get comfortable with some
JavaScript syntax. I hope you enjoy this section, see you in the next section.
17. Section 03 - NPM Introduction: Come to the third section of
the ultimate node JS scores. In this fun little section, we will see all about
node package manager or in short NPM. So what is node package manager? Node package manager is
a tool which help us to manage packages or libraries
for our node project. In simple words, NPM is like a big warehouse
for developers. In this warehouse, developers store their JavaScript code so other developers can
search that code and download the reusable
code in their project. Let me show you that.
So head over to NPM. Js.com. Here we can search
our package or library name. Package or library means
a piece of reusable code. For example, in
previous section, I told you we will use Express Library for
creating STD server. See, here we get many
results for this search. Simply, we open this
Express package. See at the left side, we get basic documentation, how to use it, et cetera, and at the right side, we get information about the package. First of all, at the top, we get installation command, which you'll see in
upcoming lesson. Next, we have Github
repository link so we can see the code and also we get link
of their official website. Here we can see this is the monthly downloads
of this package. By this, we can see the
popularity of this package. Also, we get version information
and many other things. So on NPM, we get packages for almost all functionality which we want to add in
our application. And the amazing thing is this all packages we
can use for free. So there are premium packages, but it's rare we
purchase package. Actually, I didn't purchased
any package till now. So in this section, we will see commands for how to install
different packages. Also uninstalled packages,
install developer dependency, update some packages, et cetera. So by this one little section, you will get comfortable with NPM commands and you don't
get confused about them. They are extremely
simple and easy. So let's start this section.
18. Creating Package: So as we see in previous lesson, we use as many packages as
we want in our project. Now when we install any package, that package has a bunch
of files and folder, which we will download
in our project and store it in folder
called Node Modules. Now just imagine we install ten or 20 packages
in our application. All those files will store
in this node modules folder. When we want to
share our project or we want to upload
our project on Github, then we don't upload this node modules folder because it will increase the project size and also it will increase
the number of files. So we don't upload
node modules folder. Now you might ask if we don't share this node modules
folder to someone, how they know which packages
they have to install. To solve this issue
in our node project, we will create one file
called package dot JCN. In this file, nodes store all the main information
about our project. So whenever we start working
on any node project, first and foremost
thing we do is we create packaged Gs and
file for our project. Let's see how we can
create package Gn file. So here I create a
new folder called NPM Dash Commands and open
that folder in vis code. Good. Now here, first of all, we have to create package
GSN file for that, we open our terminal
using Control Plus Batak, and here we write NPM, init, and hit Enter. See, this utility will walk you through creating a
package sn file. At the bottom, it asks
for our project name. If you want to change it, then you can write
a new name here, but make sure you use small letters and
also without space. I am happy with
this current name, so I simply hit Enter. After that, it will
ask for version name. Again, we don't want to change this default value,
so we hit Enter. Now here, we can write our
application description. For now, I just skip this. Next, we have entry point, which is the default file
name of our project. So when we deploy
our application, that platform will know which is the main file of
our application. After that, we
have test command, Git repository, then keyword,
author, and license. Hit Enter for all of this and see here we
get this object which is going to add
in the package to JCNFle it is asking,
is this okay? If you're okay with
these details, then we can simply write here, y or yes. And done. See, here we get package
JsNFle in our project folder. If you open that file, see, here we get this object with application details
and in future, we can also change
these details. Now, if you pay
little attention, when we are giving answer
to package GSN file, we didn't actually
change anything and still we have to go through
by all those questions. Is there any shortcut
trick for this? The answer is yes, we have shortcut com
creating packages and file. Here, let's delete this
current package Gs and file. In the terminal, previously, we write NPM in it. With that, we need to
answer all those questions. But if we want to
skip those questions, then we write here NPM
in it Y for all, yes. And done. See, we get again package DsnFle
in just a second. And also object is same. To sum up, when we start
any new nodejs project, first of all, we need to create package Gn file with
NPM init command.
19. Installing package in project: In the previous
lesson, I told you this package GSN file has application information
with the package list, but we can't see any
package name here. It's because in our
this application, we didn't install
any packages yet. So let's install some packages. For that, we need to run one command in the
terminal. Is really simple. Just we have to
write NPM install, or we can use here shorthand, which is just I space. Here, we write our package
name, let's say Express. Also, you will get
this command at the page of that
package on NPM website. You can copy from this also, and here we hit Enter. See in our projects folder, we get node modules
folder in which our all third party packages saves their files and folder. And if we open that, see, here we get many
folders and many files. Don't worry about that
because in real world, we never ever open this folder. Either we create that folder, we delete that folder, but we never open that
folder, don't worry. After that, we also get the
package log dot JsNFle. Packageog dot JsNFle is used to log dependencies to a
specific version number. In other words, the
package log dot JsNFle makes sure every developers and also the
deployment system uses the same version of
package to avoid problems. Also, don't worry about that. We hardly touch this file. And if we see in the package dot JcNFle here at the bottom, we get new property
called dependencies, and in that, we get all our dependencies
with its version. Dependencies are what
our projects depends on. Without these packages, our
application can't work, and that's why we
called it dependency. This is the version of
our Express package. If we install another package, then that package will addhe in this dependency
list with its version. Now this version is
the latest version which developers
deploy on NPM website. But sometimes developers by mistakally deploy bogged
version or new version, introduce some new syntax. In that case, we
can also install older version of
any NPM package. So here at the right side, we have version Stab. See, here we can see the full
history of this package. We can install any
of these packages. Suppose we want to install
this 4.18 0.1 version. So we click on that version. It will open its homepage
for that specific version. And we can see here
version is changed, and also installation
command is changed. They add AdSign and after that, they write package
version, simple as that. So back to VS code here, our current version is this. Now, we copy this command
from this site and simply paste it in our project
terminal and hit Enter. C version changed. So that's how we
install packages with NPM I package name command. Also, here we can add
multiple packages names. And if we want to install specific verson
then we write NPM, package name at the red
version number, and that's it. That's how simple to install
package in node project. And after installing
the packages, we can start using them in all JavaScript files
of this project.
20. Uninstalling packages: Now let's see how to
uninstall package. But before that, let's install another package called Mongo DB. Tell me which command we use. We use NPM install or I, and then we write our
package name Mongo DB. You are doing real great. In this package dot JCNFle, you can see another
package in the dependency. Let's try to uninstall
this package. For that, we need to write NPM, uninstall, or we have also shorthand for that, which is UN. Then we write our package
name which is Mongo DV. This command does two things. First, it will remove that particular
package files and folders from the
node modules folder. Second, it will
update config files, which is package JSN
and packslogt Json. By that, in the future, our application don't install
unnecessary packages.
21. Install package as Developer Dependency: Now sometimes in our project, we want to install package just to use in the
development environment, not in the production. For example, we have some testing packages which used to test our
code implementation. We don't need testing packages in our production environment. It will unnecessary
increase the server size. In the node Jazz, we can install packages at the developer
dependency or in short, dave dependency, which means that package will only add in our development
environment, not in the production. So in our application,
let's install one testing package
as Do dependency. For that, we write NPM. Here we write our package
name, which is Mocha. This is the normal command
for installing package. Now for installing
package as Do dependency, we have to add option here, d s Dev and hit Enter. Now let's verify this. Pun package dot
JCNFle at the bottom, we can see dev dependencies, and in that, we have our
package with its version. We can install any packages
as dev dependencies, we have to use Dev at the
end of install command. But keep in mind it will only add in the development
environment. Also, if we want to uninstall
dv dependency packages, then we use the same command which we see in the
previous lesson, NPM Uninstall or
UN package name, which is Mocha. And done.
22. Outdated packages and update them: Let's see some useful
commands of NPM. Sometimes in our application, we install some
packages we might get outdated and NPM has
its newer version. For example, in this section, we install previous version
of the Express package. Now, how can we identify
which packages has updates? For that, we have one command
in NPM, which is outdated. We write NPM outdated
and hit Enter. And see, here we get the
list. We have package name. Is current version, wanted means latest version of the package that satisfy the version range. Latest version, which is the latest table version
of this package, location, which is the
location of this package, which is in the non
module express folder. At last, dependent on which is the project or dependency that
is depend on this package. By this, we can quickly see which packages is
outdated in our project. Now currently we get
only one package, but imagine we are working on old big project and here we
get many outdated packages. Now to demonstrate that, let me install another package
with older version. We write NPM install
Mongo Debi at the rate 4.14 0.0. Hit Enter. Good. Now we again run
NPM outdated command. See, here we get
our both packages. Now you might ask why wanted version and latest
version are different? What is that meaning?
As I told you, wanted column sources, which is the latest version of the package that satisfy
the version range. Currently, we have version
4.14 0.0 in our project. It's version range only
version four related. For example, 4.15 0.1, 4.16 0.0, 4.17 0.1, et cetera. This type of version
is the version range for 4.14 0.0, not 6.15. But you might ask, what is the problem to
upgrade our version to 6.15 0.0 lets
version? We can do that. But when developers change
version number like version four to version
five or version six, then they might done some
updates which can give you error for old code according
to version 4.14 0.0. That's why we always update our package to this
wanted version, not by latest version. By that, we will not
break our application. Now we want to update all these outdated
packages in single go. We have another command for
updating outdated packages. The command is NPM
update. That's it. It will update all packages to its latest version within
the specific range, but it will not update package dot Json and
packago Json files. See, in the package GSN, we still has our old version
called Express package. Now, until we update
package dot JSN or packaglot JCNFle there is no point to install
latest version. It's necessary, we need to
update package dogs and file. For that, we use another
package uploaded on NPM, which is NPM check updates. We write NPM, I and here we add G for globally install
this package in our system. In the other node applications, we don't need to
install this package. It will globally
available in our system. Here we write our package name, which is NPM check Updates. Make sure you write the same name with
dash, not underscore. Also, if you're using MG, then at the start of
this global command, you have to add pseudo prefix. Otherwise, you will get
error and after that, enter your system password. Now we can simply run
NPM check updates. See, it checks the
package dot JCNFle and says our Express and
Mongo DB has updates. Here it is showing
this latest version, but we want to update our
package with wanted version. So we have to write here NCU, which is the shorthand
of NPM check updates. T for Target. We target minor, which
are the wanted versions. This will return
only major versions. See, here we get the wanted
version for both packages. Also at the bottom, it suggests run NCT minor to
upgrade package J and file. We run NPM check updates, or we can write NT, minor and hit Enter. C, it changed the version
in the packages and file. Again, it suggests to run NPM install to
install new versions. We write NPM install or NPM
I and hit Enter and done. Our all packages are updated, that's how we identify
outdated packages and how to update packages to
its latest specific range. So our application don't
break by new package version.
23. Remove unused packages from project: Sometimes in our application, we might install
so many packages at the beginning of our project, but some packages we
really don't use, so we can remove
those unused packages because it will take unnecessary
space in the production. So forgetting the list
of unused packages, we need another package
called DP check, which is the dependency check. So we write NPM IG depth check. Again, we are installing this
package for global level. In all application,
we can use it. Also, if you are Mcuser, make sure you add sudo
at the beginning of global package command and
enter your system password. Now you can simply
run here dep check, and it will return the list of unused dependency and also
unused dab dependency. Currently, we didn't use
any dependency or packages. That's why we get all third
party packages as unused. By using NPM uninstall
or UN Express Mongo DB, we can uninstall all packages. Great. Now if we
again dap check, then we don't get here
anything, lovely. That's how we can remove unused packages from
node application. That's all about node
package manager or NPM. If you want to quickly revise what you learn in this section, you will get summary PDF at
the end of each section, so you can download it and
recap what you learned. Now, from the next section, we will start building our
first real No Js project.
24. Section 04 - API vs REST API: Welcome to the fourth section of the ultimate node JS course. In this section, we will start working on
our first project. So we start with basics, What is RS API, set up our server
using Express JS, then we will create different
types of STB request, get, post, put, and delete. Also, we see data validation
and much, much more. So let's start this section. Now as we know, API stands for application
programming interface, and it is the way for two programs communicate
with each other. Remember our restaurant example, let's see another
real world example. Imagine user wants to
register on our website, so he or she fill the
form and then submit it. The moment they submit, we call API for
register a new user, and in the back end,
we create a new user. Like these, we can create many APIs for logging
the existing user, get all products list, add products to cart, delete products from
cart, et cetera. In simple words, API is
used to transfer data between front end to
backend and back end to front end. We
already know that. Now you might ask
what is rest API. Rest API stands for representational
state transfer API. Rest API is a specific way
of building our simple APIs. Don't worry about its
name. It's very simple. Rest API is the same API, but we have to follow some
rules to build that API. If we follow some rules, that simple API
becomes rest API. Now let's see some rules
which we have to follow. Make rest API. One rule is we have to define separate API URL for
each piece of data. For example, suppose
we are creating API for user related
operations like register user, getting the information of
single user, et cetera. In this case, our API should be our domain name and then
slash user for register. And if we want to get
information about single user, then we create API URL
like slash users 123. Here 123 is a user unique ID. Same as if we create API
related to products, then our URL should be
like this slash products, slash products, slash 123, slash products, slash
Add to cart, et cetera. So by simply looking
at this API URL, we get the basic idea
related to our APIs, and by that, we can easily do changes and our
application stay clean. Another rule is different
types of actions like reading, adding, updating
or deleting data. Should use specific
SGDP methods. Now you might ask what
are SGDP methods. So as we know, SDDP stands for hypertext
transfer protocol, and it allows our front
end to request and receive web pages and other resources
from servers using API. So only because of
SGDP we are able to send and receive data
from backend using API. Now, this SDDP has
five main methods. Get, post, put, patch, and delt. Let me explain you
this one by one. First one is the Get method. We use Get method when we want to only get data
from the server. For example, we want to
get all users details, or we want to get
all products details or we want to get
single product detail. In this case, we define
our API with GT method. For understanding these methods, we will use analogy of library. Get method is like asking a librarian to show you a
book or multiple books. Don't worry about
implementation. We will see all methods step
by step in this section. Second, we have post method. We use post method when
we want to post or send some data from front end to create a new
data in our server. For example, for
register a new user, we have to send user data from the front end and that will create a new data in our server. Think it's like we're
giving new book to librarian for at the
book in library. Next we have put method. We use Put method when
we want to update an existing data on the
server with new data. For example, if you change your profile information
on a website and save it, then a put request is sent to update your profile
with the new details. Think it's like giving librarian an updated version of a book
to replace the old one. Next, we have page method. We use page method
when we want to update a piece of small
data, not entire data. Example, if we want to update just our email address in our profile without
changing anything else, then a page request can be used. Think it's like we
are giving librarian just the updated pages of a book to update rather than
replace the whole book. Imagine, here we have user
object in our server. Want to update, complete
this user object, then which method we will use, we use put method. And if we want to update just
a password of this user, then which method we will use, we will use patch method. You are doing really great. Put method is used for
whole data update, and patch method is used for little update in
the existing data. Last method is delete. And you guess correctly, we will use delete method for delete data from the server. For example, when we delete a post or a comment
on social media, then a delete request is sent to remove it
from the server. And for that, I don't think we need library analogy, right? And also, it is not practical. Think it's like asking libraries remove a
book from library. You can see SGDB methods
are very simple. We will use these
five SGDP methods to define our different
types of API. These are just few
rules we currently see, but don't worry, we'll learn all these rules
as we build APIs. Now you might ask why
we need rest API. Why we need to follow
rules for building API? By following any rules, we can make things organized, and that's true for RS API also. Rules will make our
API more organized. Also, by following
REST API rules, we create a simple and
easy to use API because they use specific SDDP methods for doing specific
types of work. Also, with rules, we can create a clean and
maintainable API, which any developers can understand and start
working on them. For now, don't
worry about rules. As I said, you will learn these rules while
you create APIs. Celeste start winning our
first node JS project. I'm very excited and
I know you are too.
25. Planning API List for Project 01: Now before we start any project, it's better to roughly
plan about Ted project. So as we know, our
first project is about managing the task and here
how its front end looks like. Also, this is the first
project of my React JS course, and we will build
this project backend as our first backend project. Don't worry, we are not going to create here front end because our main focus is on the NodeJS and building the best backend
for our applications. From the front end, we
just need to call APIs. Now you might ask,
how can we plan NodeJS project? It
is very simple. This is the method
I used for planning my Bend project,
even big projects. Instead of building project first and then doing
major modifications, it's better to spend
a little time on planning before we
start our project. For Ben project, if we
create the list of APIs, that's very useful because
Bend is almost all about APIs. For this project, first, we need the list of all todos, one API for getting all todos. After that, we need to create API for adding a new
task, and after that, we can update the
single task details, one API for updating the task, and at the end, we need
to delete single to do, another API for
delete specific task. Currently, we know we
need these four APIs, and we will create two, three
extra APIs for practice. If while building project, we need to create more APIs, then we will create more APIs. There is nothing
wrong about that. This is just a rough plan
for reduce the confusion.
26. Setup a new application: Now let's create a fresh
application for our project. So open the folder in which
you want to create project, and I name it as task track and simply open
this folder in the VS code. This is the name of our
project. Is it sounds good? I hope it is. If you
have better name, then you can also use it. It's totally up to you. Now what we have to do for
creating no JS applications? Do you remember we just did that in our previous section. Yes. Simply, we open our terminal and initialize our project using NPM in it for As we
will use y here. See, we get pgsNFle with
some default configure. Here, we can see our main
file is index dot js, which is the file runs when
we start our application. Let's create index dot
js file, and that's it. Here in this file, we will write code
for our back end.
27. Build server using Express: So in the previous section, we create our server
using STTP module. The problem with the STTP
module is we have to define our different API endpoints in these I and LSI statements, which is not very clear and also not easy to maintain
for big projects. So that's why we use very popular package or
module called express dot js. Express Dogs is minimal and flexible for creating
better node applications. In simple words, by
using express dot js, we can simplify our routes. We can easily handle STIP
request and response, middlewares, and
much more things. Let me show you something. Here is the Express
package on NPMJS website. You can see its weekly downloads
are almost 37.2 million, which is crazy and almost 90% of NodJS projects developers
use Express package. It is very important
Master Express. First of all, let's install
this module in our project. Open up terminal and write
and pm Install Express. At the time I'm
recording this course, its latest version is 5.1 0.0. You want to get the same
experience as I'm getting, then you can write here
NPM install Express aerate 5.1 0.0 and hit Enter. Don't worry about Express. It is really simple package. D. Now we want to use this Express module in
this index js file. Can you tell me
what we have to do? Right, we will input the express module
using require function, express, and this required
function returns a function. We simply store it in
variable called express. Now, to create an
express application, we call this express
function here, which returns a
object and we store it in variable called
application or app. You can give it any other name, but probably all
developers called it app. Let's go with app. Now in this app object, we get many useful methods. For example, we
have here app dot get app dot post, app dot pot. App dot patch, app
dot DLD, et cetera. Do you remember where we
see these five methods? They are SDDP methods for creating different
types of API. If we want to define our
API for SDDPGt method, then we use app dot GAT. If we want to define
SDDP delete method, then we use app dot DLD. Same as we can create post, put and patch request. The way to define all these
five methods are the same. Still, we will see all methods in this
section one by one. For now, let's start
with GAD request. In this GAT method, at the first parameter, we have to write our
API endpoint name. Suppose we want to define
G API for URL, SGDP, Column double slash, local host, Column 3,000 slash Tudo. Now what is our endpoint in this URL?Tudos is our API endpoint. That's what we have to write
here at the first parameter. Make sure we addheFward
slash first. Otherwise, this will
not work properly. Now, what if we want to
define get API for URL, SDDP column double slash
localised column 3,000, which is the root
URL of our website. At that time, we can
adhere only forward slash, which represents root end point. Now we have to define
what will happen when our front end sends
requests to the end point. Do you remember,
let me show you. Here, we pass callback
function, and in this, we get two objects,
request and response. This request has
all details about URL request and with
this response object, we can send response
related details. We already seen this
in STP module lesson. Now what we want to send when someone sends
request on this API. For now, we simply return a text response dot SEND
Task Track project. When someone send Get
request on this endpoint, this function will
run and then it simply returns this text in
response, simple as set. By these two lines, our
Express server is created, but we have to run or listen
this server on some port. If we don't listen our server, then our API will not work. Here at the end, we write app dot L ISE and simply here we pass our port number at
the second parameter, we again pass Callback function, which will run when our server started
listening on this port. Here we can simply consol dot log server is running or listening
on this port 3,000. Save the changes and let's
run our index dot js file. Write node, index dot js. C Server is listening
on the port 3,000. Nice. Now back to browser and run Local host Column 3,000, which is the root URL and see here we get Task
track project text, which we send from our server. You can see Express makes
our code really simple. We can compare our
current code with the previous SGDP module code. Express makes it really simple. Also, this is our first API. That's why we written
only text in response. Now, as we move forward
in our project, real thrill will start.
28. Exercise for Creating Express Server: It is time for little exercise. I want you to remove all code from the index dot JS file and again create Express server and create API for
root and point. By this exercise, you will get familiar of making
Express server. Literally, it will
take only 1 minute. Try to complete this exercise and then watch the solution. Okay. I hope you solve the exercise or
you tried to solve that. Now let's see the solution. First of all, we need
Express module in our file. Require Express and store it
in variable called Express. This express is a function, so we call it here and it will return our server object
or application object. So let's store it in
variable called app. Here we create our Express
server using these two lines, but we have to listen our server only then we can run our API. So at the end, we simply add app dot Lisen and at the first parameter,
what do we will pass? Right, we pass port,
which is 3,000. And at the second parameter, we pass callback function
and inside it simply console dot log server is
running on port 3,000. One thing is, I listen
server before adding API because many times I
forgot to listen server, and it will waste a lot of time. I always listen the server as soon as I create the server. Now, in between, we can
define our API anything. So for Get request, we add app dot GAT. At first, we add
our API and point, and then at the
second parameter, we ad callback function, which has two parameters,
request and response. Inside this callback,
we simply send text using response dot SNG. This is Task track project. Save the changes, and
here we have to stop our application from terminal because it is still
running old code. We again run node
index dot js file. See, server is listening, and in our browser, we get the
updated text, very simple.
29. Creating API for Getting all todos list: Now let's create API for
getting all todos list. Can you tell me which SDP
method we have to use? Right, for getting the
data from the server, we use Get DP method. So here we write app dot GT. And let's say we want to
give this API name todos. So we pass here slash
todos, and after that, at the second parameter, we pass callback function, which has two parameters
request and response. See, for all APIs, this structure will
stay the same. Only this SDP method change, this endpoint change and logic inside this call
web function will change. That's how Express JS makes
our project simple and clean. Now, currently, we are not
going to work with a database because we don't want to add extra complexity for
our first project. We will learn all these
things step by step. So don't worry about database. Now here, we have to
send all todos array. At the top, I define
one dummidata called todos array and simply
add some todos in this. Here, every todo is object
with four properties. ID, let's cite it to one task, which is a text of task. Let's create all APIs
for project one. Tags, which are the
tags related to task, which is an array, not just comma JavaScript. And status to do. These properties, ID, task, tags, status is defined
by noches developer, which means you what
you want to call it, how many properties you need, these things you have
to decide based on which data we want to store
and which data we don't want. Now let's duplicate this object two more times using Sift plus alter plus down arrow or Sift plus Option
plus down arrow. We change this ID to two task to create EPI for list
of all to do tags, no Js and status to doing. At last, we simply change ID to three task to plan project one, text JavaScript and
status to done. Want to simply return this
array as response when someone sends Gut request to API
which slash todos and Point. What we write here, we, we write response dot SN and
simply pass our todos array. Save the changes, and let's verify this API is
working or not. Open terminal, close the
current running server using Control plus C, and again run this file. Now in the browser at the
place of root website, we simply call slash
Todos and Point. See, here we get our array. That's how simple to send data from the back end using G API.
30. Setup nodemon auto restart: Currently, when we do some
change in our project, we have to close our
previous running server and restart our application, which is really annoying. For automatically
restart our application, we have one package
called nodemon. It is really useful. Open up terminal and write
NPM install or IG nodemon. Here we use DSG for installing this Norman
package globally. Otherwise, we have to install nor moon in every node project. Also, if you're using Mac, then for installing
global package, you have to write sudo
at the beginning of the command and then it will
ask for system password. Now, how can we run file using
node one? Let me show you. In the terminal, previously, we are running file using
node, index dot js. Now for nod M, we write
nodemon index dot js. Simple as that. See, here
we get our node M version. Or restarting, we can enter RS. Also, it is telling us
watching Path period, which means root
of this project. After that, it is watching
extensions like Js, MGs, Cgs, JSON, et cetera. An file which have this
type of extensions, Nod moon is constantly
watching those files, and if something
changes in those files, it will restart our application. At the end, see, nodemon is also running
Command node index dot js. By using nodemon, we don't have to restart
our application. It will automatically
restart when we change something in those files
with those extensions.
31. Environment Variables: Now currently we are manually setting the pod of our server. But in the real world,
that pod is set by environment in which our
application is running. Specifically, it's managed by the hosting provider or development platforms
like render, Heroku, AWS, et cetera. When we deploy our application,
on these platforms, our 3,000 port might be already
used by another server. At the time, if we
hard coded our port, then our server will
not run on that port, and it will give us
error in the deployment. So what is the solution here? It is really simple. We
will check one condition. If in our application
environment, port variable is defined, then we use that port variable, else, we use our
hard coded port. Let me show you it
is really simple. So here, before we
listen our server, we create a variable called
port equals to now here, how can we check our server has port in its environment or not? So for that, we have one
object called process dot ENV. This ENV is for environment. In this environment, we get all variables which are set by hosting platforms and
port for Port variable. So if any hosting platform
want to set different port, then they will store them this process dot Env
dot port variable. That's common convention. Also, all environment
variable name is all capital letters, and that's why we also give this variable name
as all capital port. So just by looking at it, we know it can be
environment variable. If this process dot nw dot port is available, then no issue. But if it is not
available or set, then we have to pass our
hard coded port value. We adde or operator, which is par symbol two times, it is the key above
enter or return. Here we pass our
port, which is 3,000. Now let's pass
this port variable at the place of this
hard coded port, and also we will change
this console message. Let's make this string
as template string using acti because
in template strings, we can easily access
variable and change this 3,000 with dollar
coli brackets, pot. If this environment
port is available, then we use it or if
it is false or null, then we use this 3,000 port. Simple as that. Currently,
in our application, environment Port is not set. That's why our application will run continuously on port 3,000. Don't worry if you are a little confused about this
process dot ENV, we will see it in details
in the upcoming projects. Let me give you one shortcut for setting up node application. First, required Express,
create express application, create this port variable using this expression and listening the express application
on the port. These four things will stay the same in all node applications. Yes, we can add more
functionality in other lines, but nothing will change
in these four lines.
32. Route Parameters & Query Parameters: Previously, we created API for getting the list of all
to do in our application. But what if we need to get
the information about only single to do like to do
with ID one or ID three. For that, we need to
pass ID in APi URL. Our APIURL look like this. Todos ON, which means we want to fetch one to do
information whose ID is one. Type of variable
which we pass in our URL is called
a route parameter. It is very important for providing the
specific information about API request like ID,
status, date, anything. Let me show you how we
can set this type of API URL and access
the route parameters. We add new GAT API endpoint
to slash todo slash. Now here for defining
route parameter, we use colon and give our
route variable name like ID. This ID is our route parameter. We can call it
anything like todo ID, but ID is short and sweet, so we go with ID, and that's it. That's how we can define route
parameter in our API URL. Let's access this route
parameter in our API logic. Again, we adhere
callback function with two parameters,
request and response. Now, inside this function,
what do you think? In which object, we
get information about our route parameter,
request or response. Eight. We get information about route parameter
in request. So request dot PAMs. In this perms object, we will get all
route parameters, and we can access it by dot ID. See, here we get also
auto suggestions. Now let's simply send this ID in our response dot SND
save the changes, and back to our browser. Head over to local host, Column 3,000 todo slash one S, here we get ID one. We are successfully getting the route parameter.
Now here is one thing. We can also pass multiple
route parameters in our API. For example, we can
pass task status todo. Now in the back end, we add here route parameter.
How can we do that? Right. We add slash Clan status, and how can we access
route parameter? Right. We use request
dot Perms dot status. Let me show you whole
route Perms object. Save the changes,
and back to browser, we pass here status value
after our ID value. Make sure the order is the
same as we define in our API, which means first
ID and then status. See here we get the
params object with those two properties
which we define in API. Now you might have curiosity. What if we don't
pass here status? Let's also see that. Remove
the status value and see, here we get cannot
get to do one, which means our server didn't
found ABI with this URL. If we define route
parameters in our API, then make sure we will pass all route parameters
in right order. There is another way
to pass data in URL, which is by using
query parameters. If you're using
websites, then in URL, you might seen question mark and then pass some types
of variables like shot equals to date and order equals to a C for
ascending, et cetera. These are the query
parameters which is used for passing data in URL. Now you might ask,
what is the difference between route parameters
and query parameters? Those are used to
pass data in URL, but route parameters are used
to pass data which is most required where query parameters
are used to pass data, which are additional
or optional. In simple words, in our API, if front end didn't
pass route parameters, then our API will give us error. But if we didn't pass
query parameters, then our API will
not give us error. It is an additional details. For example, if we didn't
pass todo ID or status, then it will give us
error that API not found. But if we don't pass these variables after
the question mark, then we don't get error. Now let me show you
how we can access the query parameters in our API. For that, we don't need to change anything in the API URL. And directly access them
in our callback function. For query parameter, we have
another object in request, which is request dot
query, and that's it. We don't need to do
anything in our endpoint. Save the changes
and take a look. See, here we get
the query object. Request dot params is used
for route parameters, and request dot query is
used for query parameters. We will use them a lot when
we build complex APIs. They are very useful.
33. Get Single Todo by ID: Now let's see how we can
return single to do by its ID. For that, what we use route
parameter or query parameter, we will use route parameter because that information
is most required. So we remove this class status from our endpoint,
we don't need it. In the Colvey function, we need to return
the single to do, which ID is the same as
our route parameter ID. First of all, let's store the route parameter
in separate variable. So Cs to do ID equals to, how can we access
route parameters? We request dot params ID. Now we have to find
a single to do from our todos array which ID
is same as this to do ID. That we use one JavaScript
array method, which is find. Todos array dot find, and inside this fine method, we pass callback function, and here in the parameter, we get the single
to do object a So this T is this single
object in the Tds array. And we return here condition if t ID equals to our todo ID. If you don't know
this fine method, then let me explain you in short because it is pure
JavaScript concept. So this fine method will check our Tudos array, and first, it will pick the first object of the array in this T variable, then we check the condition. T dot ID is same
as where to do ID. If it is true, then this fine method will
return that single object. So we store invariable
cons to do. A, if this condition
is not satisfied, then it will set T as next object and then again
check the condition. And that's how we
can find single to do using this fine method. Let's sent this to do in
response dot SN method. Save the changes
and take a look. Let's remove these query
parameters and so status value, and here we don't get anything. Why? We pass ID one, and in our todo array, we also have to do with ID
one. Then what is wrong? Let's try to console
the single todo. Gave the changes and refresh
the page on browser. Now in our VSCO terminal, where we are running our server, we get our consoles. See, here we get undefined. So we cannot find todo from our todos array, and
that's the error. May this error occur because we are not comparing
the IDs properly. Let's also try to console this todo ID type
using type of do ID. This will return the
type of to do ID, save the changes and
refresh the page. Back to VS code and see, here we get undefined
for single to do, and we get type
string what to do ID. That's why we are not getting
single to do because here our todo ID is integer and we are comparing
it with the string. So we have to convert this
Td ID string into integer. So we can wrap this
request dot perms dot ID with parse integer. Pass integer function will convert our string into integer. Save the ings and take a look. Refresh the page and see, here we get the single to
do object with ID one. If we pass here ID three, then we get here to do with
ID three, so it's working. Good. Now you might ask
in the route parameter, we are passing one as integer. But why in the back
get it as a string. So the truth is whatever
we pass in the URL, it is passed as string. So if we want to pass
something as integer, then we have to convert
it in the back end. We can't pass integer value
using route parameter, and it is also true
for query parameters. So always remember
when you define route parameters or
query parameters, then you have to
convert its value using parse integer or
parse float method. I intentionally create this
error to show you what might happen we create API and Xs
ID in the route parameter.
34. POST API for Adding new todo: Now let's create post API for
adding a new to do object. As we know for creating
new data on server, we use post API. Also in post request, front end will send
data to the server. For example, here we create post API for adding a new todo. So our front end will send details about the
todos like task, tags, and status in the
body of that request. Out this information, how can we add new todo in our todos array? Don't worry, let me show
you this practically. So here we define post
API using app dot post, and at the first argument, we pass our endpoint. Let's pass slash todos
at the second argument, we pass callback function with two parameters,
request and response. Now you might ask these
two endpoints are same. How can we use these
APIs individually? So here we can see this
API used Get method, and the second API
is used post method. When front end send API request on this
endpoint with G method, this function will
run and if frontend send API request on the same
endpoint with post method, then this function will run. We will see how to send post
request in just a minute. Now, as we know from front end, we send data in the
request body and how can we get information related
to request in this function? We can use this
request parameter. In this request, we will get all information about
that particular request. Here we have request, and in this request, we have property called body. This body has all data which front end sends
with the request, which is our todo object, we store it in
variable called todo. And after that, simply
consol dot log this todo, and also response
dot send this to do. Now, let's taste, we are getting data in our todo
variable or not. Previously, we are sending
simple Get request from the Browser URL because by default browser send
Get request to the URL, but we cannot send other
SDDPRquest using Browser URL. For that, we need
front end code we can use API tasting
software like Postman, or we can also use one VS code extension
called Thunder client. From this three option, installing VSCode extension
is much easier setup. So in this course, most probably we will use
Tender client and Postman both because we're tasting Advanced API,
we need Postman. But don't worry, both
interface are same. Main thing is we should able to taste our API, simple as that. Head over to extension
final from here, and search Thunder
client and install. Now in our panel list, this tender client icon is
added. So let's open it. And here we can taste our
APIs for our project. So first of all, we
enter our API URL, which is SDDP, column double
forward slash Local host, Column 3,000 slash Tu Dos. And which method we need,
we need post method. So we select post from here, and now we have to pass Todo
Object in our request body. So for that, we select here
body and in the JSON option, here we will pass
our data in object. First field we need is task. We have to pass field name in double codes and also we have to pass value
in double codes. This is new task. Also, if you can't see
these fills properly, then close this penal by using Control plus B or
Command plus B. After that, we need another
field which is tags, and we pass here array, and in that we pass two
values SGML, and CSS. Last fill status and value, let's say, to do. Now
here's one thing. This fill name,
which is this task, tax status, these names are
set by the backend developer. Whatever name Bend
developer defined, front end developer should send data with the
same field name. Otherwise, how in
the back end we fetch data from the
body of the request. For sending this post request, we click on the Send button. See, we didn't get anything, and if we check our terminal, then also we get undefined. Why we are not getting the data in our request
dot body object, what we are doing wrong. When front end
sends post request, it sends data in the body of
the request in JCN format, and that's what we also did
in our testing request. By default, Express
server does not know how to automatically read and
understand that JSN data. For Express, we need a translator which will
convert that JSN data in a simple Javascript object because we cannot directly
work with JSN data. We need to convert it
in object or array, which our Javascript
can understand. For that, we have one
middleware in Express, which will work as a translator. So after the todos a variable, we add app dot U, and in that, we
call our middleware express dot JSN and that's it. Make sure you add this
middleware before our APIs, and also you have to call this express dot
JS and middleware. Otherwise, it will not work. Currently, without this
express dot GSN middleware, we are getting request
dot body as undefined. Now let's save the changes
and again, send post request. See, now we get our
request dot body, same as we send this object. So this is the importance of
express dot JsN middleware. Now we have to just add this
new todo in our todos array. So we use here
simple array method, todos array dot push. This will add new data at the
last position of the array. And here in the push method, we pass object because all
our todos is in object, and we have to follow the same object structure
for the new to do. So first of all,
we need ID and how can we get the we didn't
pass it from the front end. So for ID, we can do
something like this. We will get the last to do
ID and increase it by one. So to get the last
to do of the list, we write todos array in square packet todos
array dot Length, which is currently three. But we know array
index start with zero. So we have to do here
length minus one. So this Tds array
in square bracket, Tds array dot length minus
one is our last to do object, and we want to access its ID. So we add here ID and
simply increase it by one. So if our arrow length is three, then three minus one, which is two, which is the
index of the last element. And then we access its ID
and increase it by one, which is four, simple as that. Next, we have task filled, and we pass value as to do which we get from
request dot body, and for accessing
task ad dot task. Next, we have text, and we pass value todo dot text. Last, we have status,
which is what? We to do dot status. Now I think this
might confuse you. We define this Nut
object separately. C Nu todo equals
to past this here. And in the push method, we simply add this new to do. I think this looks more clear. Also, one thing I
want to tell you in any API at the end of
the callback function, we must at response dot sg. Otherwise, on our front end, our request keeps running, and that's make our
overall speed down. So make sure in every
API, we return something. You might ask, what should we return from the post request? From the post request, we can return
recently added data in our server at database. So our front end gets all the information like ID
and use them as they want. So here we simply return this new to do, save the changes, and take a look,
send the request again and see now we are
getting data with ID four. If we fetch all to do
from the Get request, C, we get four tu dos. So we successfully
create post request and add new Tudo in
our Tudos array. Let's quickly recap what
we learned in this lesson. So we use post request for creating new data on the server, and we get that new data from the body of the post
request sent by front end. For tasting that, we use this tender client
extension and send our data in JCNFmat which is the common
format for sending data. Now in the back end,
we need to convert that data into simple
Javascript format, and that's why we use Express D JCNMddleware and
add it before our APIs. Without this Express
dot JCNmddleware, we are getting request
dot body as undefined. After that, we add the new data in separate
object with it's all filled and then simply push it in the Tudou's
array and at the end, return the new added data
from response dot SN. Simple as that. Now
in the next lesson, we will improve
this post request.
35. Validating user Data: Currently, our front end is sending whatever data
they want to send. But what if someone don't send the required data like Tas
text or text array or status. Without them, we can't add incomplete data in our storage. It's bad for our application. So in that condition, we have to add data
validation in our API. So at the beginning of our API, we will check front end is passing all the
necessary data or not. If it is not passing
proper data, then we stop the request immediately with
proper error message. Let me show you what I means. Here, after we are getting the data from the
request dot body, we add simple condition. If todo dot task
is not available, then we return error from here. So in the Cali brackets, we write response dot
send task is required. Now here is one thing. Even if we write here
response dot send, our remaining code
will also work. To stop running the
code from here, we have to add here return. Without this return, our
remaining code will anyway run. Don't forget to add return. Now let's duplicate this I block two more times using Sift plus alter plus down arrow or Sift plus option
plus down arrow. Now we simply change
the condition for text to do dot text, and change the error message. Texts are required. And at last, to do dot status,
status is required. Also, here we can check more
conditions like text should be array or task value should more than three
characters, et cetera. For now, we don't
want that complexity, so let's taste this
implementation. Save the changes,
open the post taste, and we simply remove
this task fill. Send the request and see, here we are getting
this message. Task is required. Beautiful. Now front end, just need to display
this error message on the form or anywhere
they want to display. By this way, we can manually validate our input fields data. But these are only three fills. Imagine we have seven to eight
fields and for each field, we want to validate data. Then we have to write this if condition seven to eight times. Is there any other
way to do that? Yes. Have special library for
validating the user data. First one is joy. This is one of the
most popular and robust data validation
library in node JS, and we will also use joy for data validation
in this course. Also, there are another library like yap and validator dot js. You can use any of this library. I personally like joy, and we will use joy
in the next project. Currently, our main focus
is on creating crowd APIs, which means create, read,
update, delete APIs.
36. Passing status code: Now when we return an
error from the server, it's better we also pass the error code with
that error message. By that error or status code, our front end gets
information about the success or failure
of the SDDPRquest. You might already
know about this, or you might seen this error. For example, when we
don't found a web page, you know some website
display 404 not found page. This 404 is the error or
status code for not found. See most common error or
status code for SDDPRquest. First one is 200, which means all o. This is the default status code which sent by our
express application. In our request also, you can see here we are
getting status 2000. Next, we have 201 code, which means created
successfully. If we create a new
data on our server, then we can return that
data which status code 201. You tell in which HTTP method, we can return status
code to 01, write? In the post request, if our
data is successfully created. Next, we have status 400, which means better request
done by front end. These include some missing or invalid or unauthorized
access requests. And yes, for data validation, we will return error message
with this 400 status code. Next, we have 404, which means not found. Suppose we are sending get request for getting the
single to do with ID ten. Now, that is not
available in our data. So at that time, we can send response with 404 status code. Another important
status code is 500, which means internal
server error. Suppose we get some error in creating the new
to do on this server. Then at that time,
we will return response with status code 500. Are many status code, but for now, these
are most useful. Don't worry with practice, you will remember all
those status code. Now, for passing the status
code with our response, we can add here multiple cursor. So press Alt or Option and simply click where
we want multiple cursor. And here we add dot status, and in this Status code, we want to pass
for invalid data. We pass 400 and that's it. Press SCAP for remove multiple cursor and that we can pass data code
with our request. Also, if we successfully
create data, then we return response
with Stas 201. Save the chan cheese
and take a look. Send the request again and see, here we get error status code, 400 bad request. Lovely.
37. res.send & res.json: Till now, we are sending the response using
response dot send method. But this response dot Sen method is used for general purpose, which means in this
response dot SN method, we can send any type of
content like plaintext, SDML or we can also
pass JSON data. Now when we create API in node, most of the time we
want to return data in the JSNFmat because it is
much easier to handle. Here in the response, we have another method
called response dot JSON. This will automatically converts our data in the JCNFmat and also it will set header content type to
application slash JSON. Don't worry if you don't know
about header content type, we will see that in
the next section. We will use response dot
send for simple responses, whether text, SDML or objects, and we will use response dot JSN when we want to ensure that the response is in JSN format and properly formatted
for JSN clients. Both works almost the same. But for JSN data, response dot GSN is
more convenient. In the first GAD request, we want to send a plain text. We use here response dot SN. After that, we want
to send to do array, so we can use ar
response dot JSN next, also, we want to send object,
then what we will use. We use response dot JSN let's
also remove this console. We don't need consoles
in our server. It is just for testing. Next, here we are
returning error. First, we change the
send with JSN JSON, and JSN it's better we
pass that error in object. Wrap this string in object and simply addhe message
property and colon. Same we do for this error and
also for this last error. And when we successfully
creating a new to do, then we are returning
the new to do object. So we also change
the sand with JSON. Make sure you don't pass plain text in the
response JN method. It will give you error. Save the changes, and in
the previous testing, go to the header section. Here we get a bunch of details, but for now, we need
this content type. See, it is set to text STML. Now let's send another
invalid post request. See, here we get object which we send with
message property, and now if we check
our content type, see it is set to JSON. Use response dot send
for plain text or DML and use response dot
JSN for sending GSN data.
38. Update single Todo with PUT Request: Now let's create an API for
updating our single todo. Suppose we want
to update text of task or we want to
update the status. So here we want to update little details
about our current data, or we can also update
all the information. So it's better we use
here put request. Also, you can use your page
method. It's totally fine. So app dot pot 0.2 slash Todos. Now here we need the todo
ID which we want to update. We add here route
parameter, calm ID. After that, we add callback function with
request and response. Now, first of all, let's
get the ID parameter. CsiD equals to request
dot params ID. We know that this ID is string, so we wrap it with
parse integer function. Our first task is
we have to find which element of the
array has this ID. And then by that element index, we can change other
values very easily. So for finding the index, we use todos array dot
find index method. Here, we get single to do
object in this and then we pass condition T ID should
equals to our ID parameter. This expression returns
the index of the element, so we store it in variable
called to do index. What if user pass ID, which is not available
in our array? At the time, this todo index
value will become minus one because this fine
index method returns minus one if you don't
found it to do index. Here we pass if condition to do index is equals to minus one. Then we return response
dot status 404, which is not found and then
dot Json and we send object with property message and simply return here
message to do not found. Make sure you add
here this return. Great. Suppose we
found a todo index, then we have to update
that element fields with our new data passed
with API request. So at the top, first
we get data from request dot body and store
that object in todo variable. Now, instead of defining
three different variables, we can use here
object destructuring. At the place of variable name, we can simply add
CLI brackets and write our property names which passed in the request dot body. First, we get task, then text and then
status. And that's it. This single line of code works the same as these
three lines of code. Now in the update,
not every time we change task or we
only change datas. We can change any of
these three properties, and our front end should pass only those properties
in the body of the request which
they want to change. So here we can do
something like this. If task is available, then we updated array to do index dot task
is equal to task. So if this task is available
in the body request, only then line will
update the task property. Let's duplicate this I
condition two more times. Let's replace this task
with tags, tags, and tags. Also, this one status, status, here is the
status, and that's it. Also, here we are
missing one thing. Can you tell me, we have to return response at the
end of this function. Response dot Json and here we send our
updated to do object. Todos array in square
packet, todo index. Save the changes, and let's
taste this implementation. So go to Thunder Client
and create a new request, write our endpoint, which
is SDDP Local host, Column 3,000 Todos slash. Here we pass todo
ID. Let's say one. Change this Get method to put method and let's pass
data in the request body. Object with property task and value to this is
the updated task. And simply send the request. See, here we're getting
the updated data. And if we pass here ID
ten and send the request, then we get here error
message not found. You can see how simple
it is to create a PI. So if you want to
save your test, then press Control pluss or
Command pluss then from here, we can also rename our
test request Update todo.
39. Exercise Delete Specific Todo: Now it is time for
interesting exercise. You have to create API for deleting the specific
to do using its ID. This is a lot easy exercise. Exercise will help you to learn fast and also boost
your confidence. Even if you can't complete
the whole exercise, at least try to solve
it because that's how you know on which part
you have to work more. Give it a try and then
what's the solution. So I hope you solve this exercise or at
least try to solve it. Now let's quickly
see the solution. So here we define
a new API using app dot Tilt because we
are deleting data here, endpoints to do slash Callan ID, callback request, response,
and arrow function. First of all, we get ID
from the route parameter. So Const ID equals to
request dot params dot ID, and simply wrap it with
parse integer function. This parse integer practice
is very useful because when I'm learning node Jaz this
one mistake I repeat a lot. Make sure you don't
repeat this mistake. Here also, first, we have to find the index of
the todo object, which has this ID. So from the Put method, we copy this variable line, and also we copy this
condition for not found and piss them
in our delete method. Now, after that, we
have to simply remove that specific todo
from our todos array. So for that, we use todos
array dot splice method. In this, we have to
pass two arguments. First, which index
we want to remove, which is this to do index. Second argument is
how many elements we want to remove
from this index. Suppose we have index value two, and here we pass three. This splice method will remove three elements with
the index two, and also it will remove the element with
index three and four. In our case, we just want to
remove this single element, we pass here one,
and at the end, we simply return
response dot Json and here we pass
success message, object with message property and value two deleted successfully. Save the changes, and let's also taste
this delete request. Create a new request, change the URL to STP, Column double forward
slash local host, Column 3,000 slash Studo Let's delete the
task with ID one. Select delete method. Here, we don't want
to pass anything in our request body,
send this request. See, here we get
success message, which means our task one is removed from
our to dos array. Now if we again send the
request with the same ID, see, here we get not found. Y. You can see creating APIs
is not that much difficult. And in this section,
we created get, post, put, delete,
all crud APIs. So I hope you
understand how to use Express and create raised
APIs with Express. Now in the next
section, we will learn some advanced
concepts of Express, see you in the next section.
40. Section 05 - Introduction of Middleware: Welcome to the fifth section of the ultimate NodeJS course. This section is all about advanced express
and node concepts. We start with middleware, multiple types of middleware, how to work with
different environment like development or production, template engines, and the professional fuller
structure of node application. Let's start with middleware.
What is middleware? In express, middleware is a
function that either call the next middleware
function or send a response to end
the current request. In simple words, whichever
function who call the next middleware function or send a response to
end the request, that function is
called as middleware. Now let me ask you one question. Think about this callback
function which we pass here. Can we call this
function as middleware? Yes, because this function is sending a response to
end this Gad request. When our front end send
requests to any API endpoint, that request pass
through tunnel or pipeline in which all
our middleware functions are placed in order. This pipeline is called as
request processing pipeline. Suppose here we define
middleware one, middleware two, and last middleware three will send response to
end the request. Now when front end
sends API request, first, this middleware
one function will run. After that, that will pass our request to NST
middleware two, and after completing
the execution, middleware two will pass this request to
middleware three, which will send the response. Our server will
maintain this order. If we get error in
middleware one, then other two
middlewares will not run. Simple as that. Here are some common tasks
for middleware. First one, it is used to
log the request details. Also, it is used to check user who send the request
is logged in or not. This is the most common and
best use case of middleware. Don't worry, we will see that in the user authentication section. Now, last common
task for middleware is passing the incoming
data. We already done. Remember, this press dot JCN
is a middleware function, which is passing the
incoming data into JSON format and then pass our request to next
middleware function, which is this main function. Now you might ask why we
need these middlewares? Can we write all code
in the single function? For that, imagine
we are creating Bend for social
media application. Have one API which is used
to create a new post. Now we want to set
condition for this request. Only log in users can
create a new post. If user is not logged in, we will return an error, please log in with your account. Now we have another API which is used to add likes
in the single post. In this API, we also want the same condition which
is user must log in. Here, we have to again
add that same code. Now instead of repeating this locked in
verification code, we can define it in one
middleware function and simply add that middleware
for all the secured APIs. By this way, we don't
need to repeat our code. A using middleware, we divide
our code in small pieces, and that will make our code
clean and more readable. Also, by middleware, we can stop unwanted users request to
access the protected routes. To quick recap, middleware is a function that either called the next middleware function send a response to
the current request. We can see middleware is very useful for any
node application. So in the next lesson, we will see how to build middlewares.
41. Creating Custom Middleware: Now let's create our
own custom middleware. It is really
exciting and simple. So here in our application, we already added express
dot Json Middleware using app dot U. Now, after that, we add
another AbdTUuse method, and in that, we will add
our middleware function. So this ABDTuse
method is used to add middleware globally to our
request processing pipeline. Now inside this use method, we pass callback function. This Colbec function
has three parameters, request response, and next. Now you might ask, we know
request and response. But what is the next parameter? This next parameter is used to call next
middleware function. Now, inside this callback
function, we write our logic. Let's say we want to console dot log our request
methods and endpoints. In the template string,
we add dollar C brackets, request dot method, dollar
cbakets request dot URL. Now here is one thing. In
every custom middlewares, which we define,
at the last part, we have to call
this next function. Otherwise, our
request will stuck in this middleware and user
don't get the response. Let me show you
that practically. Save the changes and back
to our tunder client. Let me open this request
and send the request. Oh, sorry, I forgot to run this application
using nodemon. Make sure your application is also running in the terminal. Nodemon index dot js, good. Now we send this request. See it is processing. And if we open our terminal, C, here we get the console
of that request, which proves that our
middleware function is called. So our current request processing
pipeline is like this. First, we have expressed
adjacent middleware, then we have custom middleware, and then we have the
last middleware function which returns the response. When we don't call here
next middleware function, our request we stuck here in this middleware and it
shows us processing state, which makes our whole
application slow. That's why always remember
in every custom middleware, it's necessary to call next middleware function
or we return the response. Here we call next
middleware function, save the changes
and take a look. Now, if we again send
the request, see, here we get the response
and in the console, we also get the request log. That's how simple to define our custom
middleware function. I know this is very basic
middleware function, but in the future, we will create custom
middleware which checks our user is
logged in or not. Also, when we define our middleware using
app dot use method, that middleware will
apply for all API calls, just like this express
dot json middleware.
42. Built in Middleware: Express, we have very few
built in middlewares. We already use one
of the built in middleware which is
express dot JSON. This middleware pass our request body data
into JSN format. Without using this middleware, we can't get data into
request Dot body. Do you remember we got
nothing in request Dot body. Now another useful built in
middleware is URL encoded. This is the similar built in middleware like
express dot JSON. Express dot JSN
Middleware used to pass JSNdata and press dot
URL encoded middleware used to pass URL encoded data. This URL encoded data
format is usually used when data is sent through simple SML forms using
the post method. In simple words, these
both middlewares are used to extract
data from the request. WheneRquest has JCNData,
then Express use JCN Middleware and
when our request has data in URL encoded format, then Express use this
URL encoded middleware. Middleware functions
simplify the process of accessing request data, so we don't need to manually
parse it simple as that. Now let's see how to add URL encoded middleware in our application. It
is really simple. We add here app.us
and inside this, we call Express dot
URL encoded function. Save the changes and let's see this middleware
is working or not. We go to the post request. Previously, we are sending
data using this JSON format. Now here we have another
option which is form encoded. Here we pass data in key value pair like task
and here we add our text. This is new encoded task. After that, we
want to pass text, which is array and in that
we pass SDML and CSS. And at last, we want to pass
status, which is to do. Now let's simply
send this request. See, here we get new data
created with status 201, our URL encoded is working.
But wait a minute. Here we get this weird
syntax of this array. Actually, it is not array. Our server store this
array as string. See, it is coded
in double codes, which means it is string. Why this happen.
When we want to pass array or object in
form encoded format, we have to add additional
setting in our middleware. Here in the URL
encoded middleware, add object with single property extended to true. Save this. And in the post request, instead of pass this
array in single field, we have to pass it
in different fills. Like our array name is tags, so we write tags, and here we add square brackets, which indicate it is an array, and then we pass values
in multiple fills. First we write DML. After that, we add another
key value pair, again, tags, square packet, and we
write another value CSS. At last, we pass tags, square packet, and JavaScript. Now you understand
why developers stop using this form
encoded format. JSNFmat is much easier
to pass and taste. See, now we get our text array. Also, let's see if we remove
extended property from the middleware still it is working or not. I
just want to see. Oh, it is not working. See, here we are getting
texts are required, which means we are
not getting texts. In previous node versions,
this might work. But as we are using her
latest node version, it is not working, so
it's better to make it extended to true,
simple as that. You might have question
which middleware should we add in our
node JS project? Express dot JCN or
Express URL encoded. In modern world, 90% developers are using JSNFmat for data. JSNMddleware is common. But if you don't know
in which format, your front end will
send request data, JSN or form encoded, then adding both middlewares
is beneficial for you.
43. Sharing Static Files from Server: Let's see another built
in middleware which is used to send static
files from the server. If you don't know static files, then static files
are simply assets which are front end
needs like SDML files, CSS files, JavaScript files, text files, images,
paws, PDF, et cetera. They are called static
because our server doesn't modify or process those files before sending them
to the client. Example, we have the logo of
our company in the server. We will send that logo file to our front end and front end
will display on website. Now you might think we have static file on server.
How can we share it? Don't worry, it
is really simple. Let me show you that.
Here we use app dot ug. Now for sharing the static
files from the server, we have to use another built in middleware called
press dot static. Inside this function, we have to pass the folder name
which we want to share. Most commonly, developers
called it public. You can call it
whatever you like, but public is a
common convention. You might ask, we don't have this public folder
in our project, so we have to create
it called public. Make sure you write the same name as you
give your folder, and also make sure that folder is available in
root of the project, not into any other folder. Now for demo we will add here
any type of file or image. So here I have this image. I download this and add
it in my public folder. And let's rename this file as Fire Watch or anything
you want to call it. Good. Now, as we set our
public folder as static, we can access all files which are available
inside this folder. So open a browser, and here we write our base URL, local host column 3,000 slash. And after that, we can write our file name which
we want to access, firewach dot P. If your image file
extension is PNG or JPG, then you have to write
the same file extension. Here, I don't get the
file. What is wrong? Oh, we forgot to
save these changes. Now again, try to
access this file. See, here we get our image. So we will add all
our static files into public folder and we can
access them directly here. Our front end will use this URL to show images,
pawns, or anything. Also, in our application, we can define one or more
than one static folder. Like some developers like
to addhe Assets folder, so we can duplicate
this middleware and simply replace folder
name by assets. Just make sure that folder
is available in the root. Now many developers like to add prefix for static file path. Currently we are accessing our static files directly
after local host column 3,000. By adding prefix, it
will look like this. Local host column 3,000, slash Static slash
firewatcht web P, or local host column 3,000 OTRs firewatch web P, et cetera. Let me show you how
we can do that. For that, we just addhee our prefix before our
express static middleware. ATR if the changes if
we refresh our page, see, we don't get
here static file. We have to add Autar in the URL, and now we get our static files. That's how we can share
static files from the server. Now let me give you
a little exercise. Add one static file in the
folder called assets and simply access that file in the browser with prefix profile. I know you can do that, so I'm not showing
you the solution. It is really simple.
44. Useful Third party middleware: Let's see some useful
third party middleware. We call them third
party middleware because it is created
by third party. First one is Morgan. This middleware is most
popular third party middleware for logging the SDDPRquest. Now when I was learning node
has, I have this question. Why developers wants to
log the SDP request? What they will get from simply
logging the SDP request? I know you have
the same question. When we log SDP
request using Morgan, we can keep tracking of which
API is calling more time. Which API is taking
more time to response, how many API calls get filled, debug the weak API, and many more benefits. Usually, by logging
the SDP request, developers can monitor, debug
and improve their APIs. Now let's see how we can log the SDP request using Morgan. Here is a Morgan package. C, it has a 4 million monthly
downloads, which is insane. And as we know, we use this command for
installing Morgan package. So simply copy this and stop our application
with Control plus C and paste it in our terminal. Good. Now let's run
again our application. Now, in our application, we created this custom
log middleware. We don't need it, so we
can comment out this code. Now to use Morgan, first, we have to import it
in our application. So at the top, consed Morgan
equals to require Morgan. And at the bottom,
we add app dot U, and inside this, we call
this Morgan function. Inside this Morgan function, we can pass predefined
format of logging request. For example, we have Dev for development combined
for more details, common, tiny, et cetera. If you want to see
all the details, then you can check out
this package page. Here, you get all
information about formats. Now, in our code, we simply
add here double codes. See, here we get suggestions. Combined, common Dov short, tiny for now, let's go for Dev Save the changes
and take a look. Now hit any API in the terminal. C, here we get the
request details. First, we get the SDDP method, then endpoint, then status, response time, and
size of the response. By this data, we can
improve our APIs. If we again send the request, see, we get new log. So that's how Morgan
will help us. Now the next third party
middleware is helmet. Is another popular middleware, and we all know what helmet
do in our regular life. It protect our head
from accidents, and that's what helmet
middleware also do. It is an excellent
option to enhance the security of our app
with minimal configuration. And also, it is commonly used
in production environment. Many node application
use this middleware. So we search here helmet. Here we get this package. Let's install this package. Open terminal, stop
the application, paste the command here. And if you want to install the
same version as I'm using, then you can add here version
as well and hit Enter. Hood. Let's run our
application again. Now to use this package, first, we import it at the top. So Const helmet, equals
two require helmet. And at the bottom, before
our other middleware, we add app dot g and simply call here helmet
Middleware. And that's it. This will automatically add secure headers which will
secure our Express application. Also, I want to clear one thing. When we add middleware
in our application, Express add them in the same order which
we add in our code. For example, currently Express, add helmet middleware first in the request
processing pipeline, then JSON, then URA encoded, then static, and then Morgan. The order is also
important for middleware.
45. How to Code according Environment : Now let's see how we can code according to our
application environment. For example, currently
our application is in development process, and when we deploy
our application, it will be in the
production environment. Now, when our application
is in development stage, only then we want to enable Morgan middleware
for our application. So first of all, we need to
know the current environment. And yes, we know that the
same way we try to know port. So we can simply consult
dot log process ENV, and here we access
dollar Cully Brackets, progress dot env dot
node underscore ENV. This node underscore ENV
is the variable name, same as this port. Let's see what we get here. Save the changes, and here
we get undefined. Why? Because initially our environment
is not STS development. Now, in Express, we have another way to know the
current environment, which is by using app dot gat. And inside this gut, we have
to pass ENV for environment. This app dot gt ENV will
return the same result as this process dot nw dot
node underscore environment. But when we don't set up node underscore
environment, at that time, this app dot gt ENV will by
default return development, which is great, right? Let me show you. So
console dot log app ENV. Dollar curly brackets,
app dot Get ENV. Save the Gengs and see, here we get development. But still, our process
dot ENV is undefined. Now we want to only add this Morgan middleware if we are in the
development environment. So we can code like this. Here we add I condition, app dot Get ENV. You can also use process dot NV, but then you have to change
condition according to that. Now equals to development. It is true, only then
we add this middleware. We move these inside
this If blog. Now to indicate, we add here another console dot
log, Morgan added. Good. Save the genes, and in the terminal, see, here we get Morgan added. Now let's change
our environment to production to see Morgan
is adding or not. First of all, we stop our
application from running. For setting the
environment variable, we write, dollar ENV, column, node, underscore ENV equals to in
codes, we pass production. If you're Mac or Linux user, then you have to
write here export, space, node, underscore ENV
is equal to production. Make sure you write the correct environment
variable name and value. Hit Enter. Now let's run our application
again nodemon index dot js. See, here we don't
get Morgan added. That's how we change our
application environment and code according to that. Let's again change the
environment to development. So dollar ENV, colon node, underscore EN V is
equal to development. Or for Mac or Linux, export, node, underscore ENV
is equal to development. And if we check our
application again, see, here we get Morgan added.
46. env file & dotenv package: Till now we are setting
environment variables using dollar ENV or export
command in the terminal. But there is another
simple way to define those variables
using dot ENV file. In this NV file, we can simply add our all
environment variables like port is equal to 3,000 or node underscore
ENV to development. Or we can also define here
the URL of our database. And many developers use
this approach to define variables instead of defining
them in the terminal. This ENV file keeps
our applications sensitive information like
credentials out of our code, which is a good
security practice. Now, this ENV file is
a simple text file. Can we load its variable
into our node application, so we can use them and
code according to that. To use these environment
variables in node application, we have package called dot ENV. Don't worry by just
one line of code, we can use these
environment variables. Let's install this package. NPM install dot ENV. Good. Now, in our index
dot sile at the top, we have to just add one
line require dot NV, and then here we call method
dot config, and that's it. We don't need to do
any other thing. And also, the good thing about this approach
is we can access them same using process dot nw dot porD and process dot nw
dot node underscore ENV. Let's run this application. Good. See, currently we are in the development
environment, and that's why we
get Morgan added. Now, to show you, I
change the port to 8,000. Don't use the port 5,000 in MAG because it is already
running in your system, and we change ENV to production. Save this, and now we have to manually restart our
application because Norman only restart
our application when files with these
extensions will change. See, our port is changed to
8,000, but what is this? Our ENV is the same as
before, which is development. But in the ENV file, we set it to the
production. Can you guess? It is because in
the last lesson, we hard coded our
node underscore ENV value using dollar
ENV or export keyword. So our application,
remember those settings. So always remember
when we have dot ENV file and we also set our environment variable
using terminal, our node application gives more priority to
the terminal value. To solve this issue,
we can simply remove that node
underscore ENV value. So write, remove it, path, ENV, colon,
node, underscore ENV. Or if you are Mac or Linux user, then write unset, space, node, underscore
ENV, and hit Enter. Now restart the application. See, here we get the production. And if in the ENV file, we change ENV to development, save the changes, restart our
application using nod moon. See, here we get the development because
we get Morgan added.
47. Different Settings for different ENV: Currently we are
manually changing our environment variables
because they are just two. But in the real world, we might have more than two
environment variables. Like we have different
database URL in the development and different
URL in the production. Similarly, we might have secret key for
authentication, et cetera. Now when we have these many
environment variables, it's difficult to change
the variables manually. Now, how can we solve this? To solve this problem,
we can create two separate ENV files
dot nw dot development and dot nw dot Production. You can even create
dot nw dot Testing. In these both files, we will define our different
environment variables. After that, in our index dot js, we can simply put condition if our node environment
is development, then we load dot nw
dot development file. And if our environment
is tasting, then we load this dot
NV dot tasting file. Simple as that. Let me
show you this practically. Here we create a new file
called dot nw dot Development. This file, we define various
environment variables. First one is port
is equal to 3,000. K is equal to do
underscores secret. We can also add
node underscore ENV is equal to development,
and that's it. Now let's create one more file called dot nw dot production. And inside this, we add Pd
is equal to 8,000 K is equal to pro qi and at last node and a square E and
V is equal to production. Save this, and now
in the index dot s, we just need to add condition. So at the top, here, we first create a variable
called ENV file equals to, and here for using
ternary operators, we pass condition if process dot nw dot
node underscore NV is equal to production. If it is true, then our ENV file should dot nv dot production. We set dot nw dot
development file as our environment file. Now, simply in this
config method, we have to pass object with
property path to ENV file. Very simple condition. And here to show
another NV variable, here we use consol dot log, process dot nw dot. Let's check it is
working or not. Currently, we are
getting Morgan added. Now let's stop our application. Dollar ENV, colon, node, underscore ENV is equal
to codes production. If you are Mac or Linux user, then you have to use export, node, underscore, EN V
is equal to production. Now let's start our application. See, here we are in the production environment
because Morgan is not added, and here we get Pro key
and our port is also changed to 8,000 so that by
using different ENV files, we can define different
environment variables.
48. Template Engines in Node Application: Let's see template
engine in node. This topic is a little old and not commonly used in
the modern world. So let's just understand
what is template engine. Basically, template
engine is a tool that allows us to create
dynamic SDML pages. So instead of manually
writing STML for each page, we can create a template, and by using engine, we can automatically insert
data into that template. Then send the generated SGML
file to the client browser. Suppose we want to create an old style blog website where all blogs have
the same layout, but only their
content is different. Now we don't want
to manually create an SGML file for
every blog post. That would be time
consuming and inefficient. Instead, we can use
a template engine to create a single template
for our blog layout, and the template engine
will dynamically display the blog content based on the data we provide
simple as that. There are many template
engines in no Jaz, but most popular is PUG
and another is EJS. This both works the same. Only their syntax is different. Let me show you about PUG. So in our terminal, we write NPM install PUGOr if you
like to install same verson, then write at the red
three dot zero dot three. Good. Now in our
index dot JS file, we have to set the view engine, which is the template
engine to PUG. So we write here app dot set. Here, we want to set engine, so we write view engine. And at the second parameter, we simply pass PUG. So this line we set PRG as
our view or template engine. Now we have to create a
template our SDML page in POG. So in our application, we
create a folder called views. Make sure you write the
same name as views. And inside these, we can create a file called template dot POG. You can take any file name. It really doesn't matter. Here, we have to write
code in Perg syntax, which is very
similar to our SDML. Here we start with HTML. Notice that here I'm not using
angle brackets for tags. Inside of the SDML, we can have head, so we write it like this tree structure. Inside of the head,
we can add title here we want to get value
of title dynamically. We write our tag name equals, and here we write
our variable name which we pass at the run time. Title. Don't worry, I
will show you everything. Now, after the head, we
know we can add body tag, so we move in same parallel
line as head and addre body. In PRG, we don't have
to close the tag. Now in the body, we can addre H one tag equals to
heading and after that, we add paragraph
equals to content. Simple template for blog. You can see this is very clean
syntax than regular SDML. Many developers like it, but many doesn't like it. Save this file and let's
see how we can render SDML page and pass these
variables dynamically. Previously, we define an API for simple root route in which we simply return response dot Send. This is task track project. Now at the place of simple text, let's render our Perg template. Here at the place of
response dot Send, we write response dot Render. And this function
takes two arguments. First one is the file
or template name which we want to render. In this case, it is template, and the second argument
is the object with the variables and its value which we define
in that template. So first of all, title to, let's say, blog one. After that, we have heading
to template engine, and content to ease pug
really good, I don't know. Save the changes,
and in the browser, we go to local
host Column 3,000. Connection error might
we forgot to start application node
one index dot js. Oh, currently it is in the
production environment. Let's set it in
development environment. So dollar ENV can node underscore ENV is equal
to encodes development. Or if you are Mac or Linux user, then you can use export, node underscore ENV is
equal to development. Make sure you don't
pass here codes, and then we start
the application. See, it is listening in 3,000
and if we refresh our page, see, here we get
the Perg template. Title is blog one. Heading and content is
also same as we pass. So that's how we can
render an SDML file on the server and then send
it to the client browser. I don't think modern
companies are using this way because most of
all companies use Rag, angular or view for front end.
49. Cleaning up the Code Application Structure: Currently, if we see
our index dot JS file, it feels we really
messed up our code. Everything looks very cluttered
and not maintainable. So let's structure
our node application, which is used in the real world. So currently, we added all to do related APIs in the
single index dot JS file. Imagine in another e
commerce application, we have another set
of API for users. We have another set of APIs
for card related features. At the time, we can't add all those APIs in the
same index dot js file. The solution is we can create a separate folder called routes, and in that, we will add
files for each set of APIs. We create a new
folder called routes. This is common practice
for application structure. Now here, we create a new
file called todos dot js, in which we will define all
API routes related to todos. First of all, let's cut all routes from the
index dot JS file. And paste it in the
todos dot js file. Now you might ask in the
index dot js file at the top, we have app instance
and using that, we define different API routes. But here, in the
todos dot JS file, we don't have that app instance. So how can we
define routes here? You might say we can
recreate the app instance, but that is not possible.
It will not work. So in the express, we have another instance called
router by which we can define API and
points just like we are APIs using app instance. Here, we first input express const Express is equal
to require Express. Now in this express, we have another
method called Router, which will return
the router instance, and that's why we store it
in variable called router. Now, let's simply replace this app instance by
this router instance. So select this app and press Control plus D
or Command plus D, which will select
all app instance and replace it with router. Good. Now we have defined our routes in
the different file. But how our index dot
js file will know, these routes we want to
add in our application. For that, we have to export
this router instance from this file and we add it in the index dot js
file. Simple as that. So do you remember how can we export variables
from the node file? Right, we use module dot
exports is equal to router. Save this file, and in
the index dot js file, let's remove these blocks. We don't need them,
and we add here app dot g for adding those
routes in our application. Here at the first position, we add router prefix, same as this static file. Most of the time, developers
like to add here slash API. After that, at the
second argument, we pass our router instance
from the todos dot js file. So at the top, we import that router
using require function. And here we pass our file path, which is slash out todos. Now, as we know, this
require function will return router instance, so we can store it in
variable called todos routes. And at the bottom, here, we pass Tudous routes. And done. Also, I found we need this Tudous array
in the Tudos file. So we cut it from here and
paste it in our Tudous file. Save the changes,
and let's check our application is
working well or not. Remember, for all to do routes, we need to add prefix API before endpoints.
Send the request. See, it is working. Now, one last change I want
to do is in our TudsRoute, we can see we are adding Tudos before every
route endpoint. So we can commonly
pass that slash todos at the prefix in the
index dogs file. Save these. Now in
todos dot js file, we can remove slash
todos from the endpoint. Also, we remove todos
for all API endpoints. Save this and we are good to go. We can also remove
these unwanted lines. We don't need them. See, now our index dot js file looks clean and
more maintainable. We will follow this
application structure for the rest of our course. So here our Section
five is over. You can quickly see the
summary PDF and recap the whole section in two to 3 minutes and I will
see you in the next section.
50. Section 06 - Asynchronous vs Synchronous: Welcome to the six section of
the ultimate not JS course. This is the repressor
section for some AdvancEvAscript topics like synchronous
versus asynchronous, callback function, callback hell promises, and async await. A lot of developers are
confused in these topics, so it's better we understand it. So if you are
already confused in these topics and you
want to master node js, then in upcoming sections, you will face a lot of issues. It's better to
learn these topics, which we can call as
asynchronous JavaScript before we dive really deep
into node applications. In all node applications, we use these topics a lot. Let's learn these topics. Are simple and easy to learn. Also, if you are already
confident on these topics, then you can skip this section. It's totally up to you. Now before we start learning concepts of asynchronous
JavaScript, let's understand difference between asynchronous
and synchronous. It will help us to
understand better. What is synchronous?
Synchronous programming means our code run line by line. Each line must be completed before we
move on the next line. In simple words,
synchronous programming is like following a
recipe step by step. Each step must be completed before moving
on to the next one. If one step takes a long time, we have to wait until it's
finished the execution, and then we can continue. For example, we are preparing
a cake for birthday party. Now there are step by step
process for let's say, first we mix ingredients, then we put it in
oven for baking, and finally, we
decorate the cake. Here, we have to follow
the steps line by line. We can't directly start decorating the cake
without baking. We have to go in step one, step two, and step three. That's the synchronous example. Synchronous code is also
called as blocking code. Let me show you the
blocking behavior or blocking code example
in JavaScript. So to practice these topics, we create a new folder in
the projects folder called asynchronous JavaScript and open this folder in the VS code. Now inside this folder, we create a simple JavaScript
file called index dot js. Good. Now here we write
three console lines. So console dot log. Step one, start. Another console dot log. Step two, and last,
consol dot log. Step three, and
let's run this code. So in terminal, we
write node, index dogs. See, we are getting the
output line by line. First, we get step one, then step two, and
then step three. Now you might say, we can't see the blocking behavior
of this code. How can we see that practically? Currently, we are getting
this output without blocking because not a single line
taking more time to execute, every line execute immediately. Let's add little
block in this code. Here, after Console one, we create a function
called fetch data. In this function, we will pretend we are fetching
data from the database. As we might know, fetching
data from the database can take time like two to 3
seconds or more than that. For now, we just add some
delay using empty four loop, which can take some
time to execute. Here, let I is equal to zero, I less than one and nine
times zero and I plus plus. Simply, we don't want to do
anything in this fall loop, so open close curly brackets. Now, this line can take one or 2 seconds to
execute and after that, we move our console step two up and right here, data fetched. Here before our step three, we call this fah data function. Can you guess what we
get in the output? Let's see. Save the changes, and let's run this file. See, after step one, our code stop for a little bit for our
function to execute. Then after that, we get data
fetched and then step three. Here, for a little, we can see blocking behavior
of our JavaScript code, which is called as
synchronous JavaScript code. Now you might guess
what is asynchronous. In asynchronous also, our
code runs line by line. But for moving forward, we don't need to wait
to finish that task. We can start a task, and
if it takes more time, then we can move
to the next line. In simple words,
asynchronous programming is like doing multiple things at once and not waiting
for each line to finish execution before
starting the next line. You can start a task, and while you wait for it to complete, you can do something else. For example, you are preparing a keg and also
watching this course. So you put the cake
in oven for baking, but this baking can take
ten to 15 minutes time. So at that time, you can do other things instead of just
waiting for cake to bake. So you start watching
this course. And if you like this course, then at that time, you can share this course
with your friends. Similarly, if we define our sum code using
asynchronous JavaScript, if that line takes
more time to execute, then our JavaScript code
will not wait there. It will run forward
in the code and when that line finish
its execution, then it will run that line. We can do multiple things
in asynchronous JavaScript without just waiting for one
line to finish its task. This is the fundamental
difference between synchronous and
asynchronous JavaScript. By default, our JavaScript
code is synchronous, but Javascript provides some
methods by which we can make our code asynchronous for doing specific
types of work. Also, as we know, node is popular because it can
apply asynchronous nature. Now let me show you how we
can make our synchronous or blocking code into asynchronous
or non blocking code. Here at the place of this four loop which is
keeping our code busy, can add another method
for adding delay. We add set time out. This method takes two argument. First one is the function and
second one is a delay time. For example, we pass here
10,000 milliseconds, which is 10 seconds. Now at the first place, we simply pass function
and inside it, we move this step to
data fetched console. After 10 seconds, this function
will console data fest. Now, can you guess the output
of this code? Let's see. See, first we get step one. After that, in the background
or function is running, but that didn't stop
our code there. Our code continue
to run step three, and when those 10
seconds will end, we get step two data ft. So this is the non blocking
or asynchronous code. Synchronous code,
wait for one line to complete execution and
asynchronous code, wait for that line in the background and
continue to work forward. You might ask, what is the best synchronous
or asynchronous. In my opinion, both are useful. No one is better. They both serve
different purpose. Synchronous is great when
we need task to execute in a specific order or when the task is quick
and non blocking. Provides simplicity
and predictability. We asynchronous is important
for tasks that take time, such as file handling,
database access, or network request,
where we don't want to block the main
thread of our server. In node application, we will
use Asynchronous JavaScript we want to perform non blocking operations like
making database queries, API calls, reading and writing files without
freezing the main thread. Main thing is our application should work in non
blocking manner, which will give our users
fast and great experience. So as we know, by default, our JavaScript code
is synchronous. But JavaScript provides some
methods by which we can make our code asynchronous for doing specific
types of things. First one is by using
callback function. Another method is
by using promises. In this section, we will learn deeply about these methods.
51. Callbacks in JavaScript: Now let's understand
callback in JavaScript. So what is callback function? A callback function is a function that is
passed as an argument to another function and is executed after a specific
task is completed. Let me explain you callback
using real world scenario. Suppose you are ordering sandwich from the sandwich
shop with your phone, so you call the Sandwich
Shop and give your order. They tell you it will take 20 minutes to
prepare and deliver. So you hung up the call and do other things instead of just
waiting for the sandwich. Now, when sandwich gets ready, the delivery person rings your doorbell and gives
you that sandwich simple. So in this case, you call
the sandwich shop and give your order is like starting
an asynchronous task. After that, they tell you
it will take 20 minutes. This is the delay in
the asynchronous task. Then you hung up and do other things instead of just
waiting for the sandwich. This is the non
blocking behavior, which means our code
continues to run forward. When Sandwich gets ready, which means our asynchronous
task is completed, the delivery person rings your doorbell and give
you that Sandwich. This is the callback
function being executed when the asynchronous
task is completed. So a callback function is passed as an argument to
another function, and also it is executed after a specific
task is completed. In simple words,
whatever we want to do after the asynchronous
task is completed, that we will pass in
the callback function. Now if you're working or learning Javascript
from the long time, then bet with me, you already used callback function
in your code. Just you don't
know that function is called a ColwcFunction. For example, do you remember in the set timeout function
at the first position, we pass function which
executed after some delay. And you are correct,
this function which we passed as an argument is
called a callback function. Same as we pass callback function in the
set interval function. Let me give you another example. I almost all array methods, we pass callback functions. Like in forage method, map method, filter method. In all these methods, we pass callback function. Also, in our previous project, we pass callback function in the app dot get app dot post, app dot pot, delete, and in all methods. Remember, those are also
called as callback functions. Now for basic understanding, let's create one
function and pass it as callback function
because in node, we need to pass callback
function a lot. So let's remove this code. If you want to watch
the previous code at the bottom of this lesson, you will get
Resources folder Zip. Download it and unzip it. In the resources folder, you will get Gita links for
all code section by section. So you can get
reference from that. So first of all, we
write console dot log. Start. Now we create
here function called fetch student in which we will pretend we are fetching
data from the database. So to simulate delay
for data fetching, we add here again,
set timeout function. And at the first position,
what do we pass? Right, not just function, we pass callback function. So we are passing here function using arrow
function syntax. Arrow function is another
way to define function, and it is much cleaner
for callback functions. Now at the second argument, we pass milliseconds,
let's say 3,000. And here in the set timeout, we consult dot log fetching student data from the database. Now let's call this
fetch student function here after the console line. To make it more realistic, we also pass here student ID, which is the ID of the student we want to
fetch. Let's say one. Now when we complete
the data fetching, after 3 seconds, we want to
return that student data. So we return student
object with ID. Here we get ID as the parameter and simply
pass that same ID here. Also our student
has name to Harley, and let's say we want its
enrollment number, suppose 500. We are pretending we are getting this student object
from the database. Now when we call this function, here we get the student data, so we can simply store it in
the variable called student. To verify, we simply
const dot log, student and pass
here this student. At the end, we simply
consul dot log, and now, can you guess what
we will get in the output? Will we get the
student data or not? Let's see. Node index dot js. See, here we get start,
student undefined, and then we are getting fetching data from the database.
Let me explain to you why. When we call this
well stud function with ID one, first of all, the set timeout runs, which will execute this piece
of code after 3 seconds. Now, at the time, we don't get anything
from the function. That's why it stood undefined value in
the student variable. Our JavaScript code
continued to run. This is why we get student to undefine then and
then after 3 seconds, the set timeout function will console this
fetching data line. Now the thing is, how can we run this code and get
the student data? What is the solution here? To solve this, we use callback function and we will
do that in the next lesson.
52. Solving Problem using Callback: So in the previous lesson, we get the error
undefined student. Now, let's solve this problem
using callback function. First of all, we don't need to store the result in
the student variable. Now, to solve this issue, we will do little change in
the Fed student function. So after the ID, we pass her callback
function as parameter. And at the place of return the student object
from the function, we will call this
function or call back this function and simply
pass student object in it. I know it is a little
bit confusing. See this lesson and you will understand
callbacks very well. Now, as we are accessing
callback function as parameter, we have to pass that function as second argument when we are calling fair
student function. So here we pass function, and inside this function, we can simply move
this console student. Now from where we get
this student object. Right, here we get
the student object in parameter because
when we are calling this callback function in
the fast student function, we are passing
student object here, and that student object, we directly access here in
this callback parameter. Let's see this is
working or not. So node index dot Js. See, first we get start
and then after 3 seconds, we get fetching data from
the database and then using callback function
print this student object, and that's exactly what we want. Here is our previous
and current code. At the place of returning the data from the fat
student function, we call another function for console dot log,
the student data. This console line will only run when this callback
function will call. By this callback
function technique, we make our code asynchronous
or non blocking. Now to make this clear at
the place of this function, we use arrow function syntax. We remove the function
keyword and between the parenthesis and GLY
brackets, we add arrow. See, now our code
looks simple and this way is very useful in
node because as you notice, we always pass callback
function like this. Instead of declaring
it separately, we directly define here. You can see by using
callback function method, we can handle asynchronous task. Previously, we use
this Colbec notation for defining API routes. So that is the
callback function. Callback function
is just a name of the function which we pass as an argument into
another function, and it is executed
after completing task, specifically
asynchronous task. Simple as that. Now let's add
little difficulty in this. Suppose from this student
enrollment number, we want to fetch
his result details. After this fetch
student function, we create a new function
called Get result. Parameter, we add enrollment
number because using that, we will fetch result
from the database. Now, inside this function, we simply return object
with properties, result ID to enrollment number, which we get in the parameter, and after that, percentage
to, let's say, 70. Now as we know, we are again fetching the result
from the database, which means it will
again take time, and that's why this
function is a singer on us. So to simulate delay, here we add set timeout function and inside it callback function, and again, wait for
3,000 milliseconds. Now, in this set
timeout function, we move this return, and as we know this return
has no meaning. We have to pass this result into callback function.
As we did here. So callback function
and simply wrap this result object with
Callback function parenthesis. Also, before that,
we can consult dot log fetching result
from the database. Good. Now, where we call this Gad result function,
think about it. Yes, we can call Gad
result function in this callback
function because we get enrollment in
the student object, and also we can call it here in the fetch
student function. But as that function is all
about fetching the data, and that's why we don't
want to mix things. So we call the Ga
result function in the callback function. The first argument, we
pass enrollment number, which is student
dot E undersceNum. And what we pass at
the second argument, we will pass callback
function here. Now, here in the parameter, we get the result object
which we pass here, and we simply console dot
log result to result. Also, we have to get this
callback from the parameter. Save the gangs and
let's run this score. Here, first we get
the student data, and then after 3 seconds, we get result of that student. So let me quickly show you
what is happening here. So first of all, the
start console works, and then we call this
fall student function. But because of this
set timeout function, it will not run immediately. So our code move forward and
simply console this end. Now, after 3 seconds, the set timeout will run the code which is
available inside eight. So we call Callback function
and simply pass Object. From where we get this
callback function. Right, we get it
from the parameter, and where we define
that callback function, it is here in the fat
student function, and that's why we get
student details first. Now, after we get
the student details, we call another function, which is G result. So this G result
function will run, and because of set timeout, it again takes 3
seconds to execute. And inside this, we call
another callback function, and where we define that
callback function, yes, here, and at last, that callback function
console this result, and this is what happening here. If without callback function, we want to run our code in same order, then we can do that. We get undefined student
and because of that, we can't even get result. So now you understand why I put so much weight on
the callback function. Because if you get confused
in callback function, how can you focus
on learning node?
53. Callback Hell: Currently in this code, we add this callback function inside another
callback function. Now imagine if we have
another asynchronous task, let's say update result. Inside this, we pass
result dot result ID, and Callback function will run after our result gets updated. We get here updated result. You can see our code looks
messy and hard to read because here we have many nested callback functions
inside each other. Let me remove these
all consoles so we can see clearly see, here we get nested callbacks. This situation is called
as callback hell, where we have many nested
callback functions inside each other and that makes the code
hard to read and manage. If we have the same
code in synchronous, then it looks like this. First, we call fair
student function and store its data
in student variable. Same as we call Get
result function and pass enrollment
number of that student. At last, we call updated student function
and pass result ID, which we want to update. You can see synchronous
code is very simple and easy to
read and manage. Now you might ask, what is the issue with the
callback hell? See, even if we have
callback, our code will work. The issue is only it is hard to read and manage because
in the real world, we might have more than
three nested callbacks. Then at a time, it will
be really hard to read, manage, and even scale. So we need to make our
code easy to read. It is really simple. Just we have to make
our anonymous function, which is the function
which don't have name. Convert these anonymous
callback functions into named functions. In simple words, at the place of defining this
callback function here, we will separately define them and pass that
function name here. Let's add our consoles back good and remove
this last function. We just added it
for demo callback. Here we create a function
called cost, print student. We can call it anything, and I am using here
arrow functions in text because it's easy
for callback function. Now we can cut this
Callback function from here and paste it at the
place of this arrow function. Good and at the place of
that callback function, we simply pass print
student function. Make sure you don't call the
print student function here. We just have to pass the
function as parameter, and this print
student function will run here as the
callback function. Now, let's quickly convert this anonymous callback
function in named function. So Cons print
result is equal to, and here we simply paste
this callback function. And at the place of this, we pass print result. See, now our code looks
clean and easy to maintain. To quick recap, when we have
nested callback functions, we get callback heal issue, which is hard to
read and manage. What is the solution?
Right, we convert our anonymous callback functions into named functions.
Simple as that.
54. Promise in JavaScript: In the previous
lesson, we see how to handle asynchronous task
using callback functions. But in that implementation, we get this callback
hel problem, and also we solve that
problem with named function. Now my question is, how many
new functions we create? Imagine we have ten nested
callback functions, then we have to create
ten named functions before we call our
main function. Is there any other way to
handle asynchronous task? Yes, there is
another way which is by using promise.
What is promise? A promise is a special
object which is able to hold result of an
asynchronous operation. In other words,
promise is promise to give you the result of
the asynchronous operation, or if asynchronous
operation fails, then it will return error. For now we learn promises in the new file called
promises dot js. And then we update our previous code with
Promises implementation. So first, we create promise, and then we will see how to consume that promise.
It is really simple. So to create promise, we write new keyword
and then promise. Now, this promise is
a class and it takes an argument which is function
with two parameters. So here, first
parameter is resolve and second parameter is
reject and arrow function. Now inside this function, we can perform our
asynchronous task. So here, again, we are assuming we are getting
data from the database. So we write here,
set timeout function and pass callback function
and 3,000 milliseconds. Now imagine here we get our
data from the database. So we create one
variable called student equals to object, let's say, ID to one
and name to Hurley. Now here we call
resolve because we successfully get the data and
we pass here this student. Here, our promise is ready. Now let's do this promise invariable called
PR for promise. Good. Now let's see how we
can consume this promise. It is really simple we write PR, which is this promise, dot, and here we have two main
methods, then and catch. So when we create promise, it is by default
in pending state. And if we complete the
asynchronous task, then promise is in resolved
or fulfilled state. And if there is an error, then promise in rejected state. This is called a life
cycle of promise. Here, our promise is fulfilled because we call
here resolve function. So when promise is fulfilled, then we get our data
this then method. Now we store our data in this result parameter and then simply consult dot
log this result. So this let's run this file. Node promises dot gs, see, after 3 seconds, we get this result. Now imagine here
for some reason, we don't get data
from the database. Here we create one
variable called status, and make it false. Here we add condition. If status and my system automatically shut down,
let me start it again. Yeah, I am back,
so let's continue. I status is true, then we run this
resolve function, else we simply call
here reject function. Now for better practice, every time we want
to return error, we create new error and pass
our error message here. This is error message. Save the changes, and let's run these promises dot js file. See, we get this error here. Now let's consume this error, same as we get here data
in the then method. After this then method, we add another method called
cache and inside this also, we pass callback function. Here we get error in parameter and consult
dot log this error. Save this and let's
run this file again. See, we get that error here, and that's how we can consume promise using then
and catch method. We can see instead of using callback function for doing something after
asynchronous task, we can use promise for perform that asynchronous task and then simply consume
that promise. In short, if our
promise get resolved, then this then method will run, and if our promise get rejected, then this cache method
will run, simple as that. If you are worried about
the promise creation, then don't worry with practice, you will get
comfortable with it. Let me tell you one thing. In real world, 99% time, we only consume promises. Only a few times we need
to create a promise. Now in the next
lesson, we will handle our callback heal code using
promises. It will be fun.
55. Replacing Callbacks with Promises: Let's handle our Callback
L code using promises. You can download code of Callback hel from
below of this lesson. This code we write
in the lesson four. Copy the code and paste it in another callbacltjs file or also in the resources folder and inside asynchronous folder, you will get this file
as callback hl dot js. You can also add that
in your project. Now, as we know, in promise, we define our asynchronous task. And here we have the
two asynchronous task in two separate functions. Now instead of removing
these functions, we can simply return a new
promise from this function. Don't get confused. Let
me show you what I mean. So here, in the felt
student function, we simply return
here a new promise. Now, what we pass
in this promise, we pass here function, which has two parameters,
resolve and reject. Now in this function,
what we do right, we perform here a
synchronous task. So we move this set
time out function here using holding alter or
option and aro chies. Now here we don't
need this callback. At the place of callback, we pass our data in
the resolve function, and also we don't need
callback in the parameter. Now let's do the same in
this Gad result function. So we return promise, and we pass here function with two parameters,
resolve and reject. If you don't want to
write this whole line, then let me show you
shortcut for that. So here we write new
promise without space, and select this suggestion. See, here we get
our new promise. I don't show this
previously because I want you get comfortable
with promises. Now let's move this
asynchronous code also in the function and at
the place of callba, we return that data in resolve method and from the
top, remove this callbag. Now let's consume this promise. At the top, we comment out this previous code in which
we experience callback hell. Now here, first we call fair student function
and passie ID one. This expression will
return this promise. Let's store that promise
in variable called PR. Here we have our promise
in this PR variable. Now we just have to
consume this promise. Part. Which method we use here, we use dot then method. Here we pass function. Here, we get parameter, data, or we can call it
student anything, arrow function, and what we want to do if we
have student data. We simply wants to call Get
result function and pass here student dot Enrollment because here we have
enrollment property. Make sure you write the same property as
you have and done. So that's how we
consume first promise. Also, instead of storing
this promise in variable, here we can directly
write this expression. You get confused in
this, then don't worry, you can store promise in
variable and then consume it. This is the common practice, and that's why I show you here. Now, as we know this
gat result function will also return promise. How can we consume this
promise? It is really simple. The place of writing
ten method here, we can write it after
our first ten method. Now what we get from
this promise, result, arrow function, and
simply console dot log result to this result. Save the changes, and let's
run our Callback Hel file. Node callback hell dot gs. Good. See, first, it
will fetch student data. After that, we fetch result
and then print result. So we can see using
these promises, our code looks more
clean and easy to read. Now, if we have another function in callbaL then similar to this, we return here, that new
function and call it. Now when we call that
function and return it, we can add here another than
method, simple as that. Some students might get confused about this get result
function calling. They ask what is happening here? Explain this code in details. So here, I again, call this fetch student
function and simply pass here and consume that
promise using then method. And here we get student data. When we get student data, we can pass normal arrow
function with C brackets. Now inside these CL brackets
we don't have any code. Just we want to return Get result function with
student dot enrollment. So because we just want
to return this one line, we can remove these C brackets and we can also remove
return keyword. This is the short way to return something from
the arrow function. So when we want to return one line from the
arrow function, then we write
directly after arrow. Here we are returning
this function. This whole expression is
returning a new promise. So we can store it in new
variable called new PR. Now let's consume
this promise also. We write newpar dot the
and then we get result, arrow function, and
simply console dot log, result and print
here this result. Now, at the place
of this new PR, can we write this
whole expression? Sure. At the place of new PR, we paste this expression. And that's what we did here. Now, as a best practice, when we have promise
that promise can also return error
using reject method. So it's better to
handle that error. For that, we have another
method, which is cache. We also see this in
the previous lesson. So here we also
add cache method. Here we get error object and we simply Consolatog this error. Now for testing, we simply add reject in gat result function
before resolve method, and pass here new error, and error message,
result not found. Save the changes
and take a look. Let's run this
application again. It is patching data. See, here we get
result not found. If we get error in any
of these promises, this sketch method will run. Simple as that. Let me comment
out this reject method. It is just for testing. I hope your all
doubts are clear. Now in the next
lesson, we will see another easiest way to
write this same code.
56. Async:await in JavaScript: Now as we can see in the Calbecl after this
get result function, we are consuming
another promise. Now imagine in this Colbecl after this get result function, we are consuming
another promise. For that, we have to add
another than method, and that also can be
a little confusing. Let's make this code more
simpler using async and await. What is async awight? Async awight is a way to write asynchronous code as
synchronous code. By using async awight, we can consume promises
in much easier way. So first of all, we call here fair student function and
simply pass here ID one. Now, this function will
return a promise which is initially in the pending state
because in that promise, we are performing
asynchronous task. So we can wait here for that promise to resolve
using Awight keyword. So awight pauses the execution of the code until the
promise resolved. Once the promise is resolved, it returns the data. Now when we get data, we can store that in
variable const student. Now we have student object, so we can call Get result and pass here
student dot Enrollment. This function will
also return promise, so we again adhere Await
keyword for that promise, get resolve and this
will also return data. Const result. After that, we simply write CLO Parkansa
log with property name, result, and press Ascap. Now you can see using
a weight keyword, we can write asynchronous code looks like synchronous code. Let me comment out
this previous code. Save this, and let's
run this code again. Node, callbhll dots. See, here we get start fetching
data from the database, and then we get result, and at the end, we get. So our code is working
as synchronous because our code is
blocking here this end. Now as a best practice and to make this
code asynchronous, we have to always use
this await inside async function because in normal function,
it will not work. So here we create a function
called print result. And simply move this code
inside this function. Now you might ask this
is the simple function, but a weight is only
valid in ASN function. So how can we make our simple
function as acing function? It is really simple. Just we have to add ASN
keyword before function. And after that, we simply call this function print result. Otherwise, how it will work. Do the changes, and
let's check this. Run again our file. Good. It is fetching and see here we get result and also
it is not blocking this end. We can see by using
async and await, we can write our
asynchronous code looks like synchronous code. This is more readable and
clear than this previous code. Now, what if this promise written error using
reject method? Here we remove comment
from the reject method. Let's see how we can handle errors in the async at method. So for handling error in asyncawt we need to use
try and catch method. It is also simple.
Let me show you. So first of all, we add try and in the Gy brackets,
we move our code. Now if something goes wrong in this triplog our code directly
move to the cache block. So we add here cache block, and this cache block has
one parameter called error. And yes, it is the same error which promise written
in the reject function. In the CLI bracket, we can simply consult
dot log this error. Now let's see it
is working or not. Save this and let's run
this file one more time. See, here we get error. So in simple terms, if any error occur in this triplog then this
catch block will run. Now, some students might ask. This seems like this 08 is blocking our code
from running forward. And yes, it is true, but that is true only
for this as function. Here we have console and
after this function call. So in the output, we get start, then our function will call. In that function, first, it will call fair
student function, which returns promise. So a weight will pause the execution of the code
until the promise resolve. So our code will not move
forward only in this function, but it will move forward
outside of the function. And that's why after start, we get N. Then if we
have other lines, then it will also run them. Don't worry,
Asyncawd is a way to write asynchronous code
looks like synchronous code, but our code will work
in asynchronous way. To quick recap, when
we have promise, we can use a weigh keyword. A weight pauses execution
until a promise is resolved and we can only use a weight in
the async function. For error handling
in a syncowt method, we will use try and catch blog. We get any error
in the dry blog, then our cache method
will run simple as that. That's how we can
consume promises in much easier way than
this T and catch method. It's your choice,
what you want to use. They both works the same. It's totally up to you
what you want to use. Also, if in Async and AD you
want to add tryCatch blog, then simply write Tricach
and select this suggestion. See, here we get Tri case block. I hope you learn a lot from this section or these
concepts get refreshed. Without clearing these concepts, we can't jump on the
BG applications. If you are continuously
watching this course, then take five to
10 minutes break, get some fresh air, and I will see you in
the next section.
57. Section 07 MongoDB Basics: Welcome to the seventh section of the ultimate no JS course. Till now in our projects, we are defining data in
simple JavaScript array. But the problem with
this approach is when we restart our
server or application, our data gets reset to defined value and we lost
our changes in our data. Now, at that time, database
comes in the picture, so we will store our
data in the database. There are many types of
database options like MySQL, SQL lite, Mongo Dib, et cetera. Mongo DB is most used database
with node applications, and that's what we are going
to learn in this course. Now, one thing I want to clear is after
completing this course, you will not completely
master Mongo DB because Mongo Di B itself is a
four to 5 hours course, but I will try to cover as
many topics as possible. So in this section,
we will start with very basics of database, and then we move
forward for creating, reading, updating, and deleting
the data with exercise. I'm very excited, and
I hope you are too. So let's dive into this section.
58. Introduction of Database: Now before we start
learning Mongo Deb, it's better we learn some
basic terms of database. So as you might
know, database is an organized collection of structured information
called data, and it is stored in the
computer or on Cloud. In old schools, our
teacher used a book or register to keep track
of student attendance. That is database, but
it is on hard paper. In today's world, we use computer system to
store that data, so we can access it
anytime and from anywhere. We all know this, right? Now, there are two
types of database. First one is SQL database
or relational database, and second one is no SQL database or non
relational database. They differ in how
they organize, store and manage data. Let me explain to
you this one by one. So SQL stands for
structured query language. SQL database is well
organized filling cabinets. These two data in tables using multiple columns
and rows like this. We can see this is very
organized structure. Every column of the
table represent different fill of
data, like name, email address, phone
number, and password, and every row of the table
is a new set of user. We have to pass single user
data for all these columns, and that's why it
is organized table. Now, on the other side,
no SQL database is like flexible board where we can organize data however we want. In no SQL databases, we don't have tables
because they don't follow strict table
based structure or schema based structure. SQL databases store data in more flexible formats like DSN documents or
key value pairs. For example, if we have
the same user data, we can store it like
this in key value pair. We don't need to provide
all fields for each user, and that's why we can call it schemas or schema
flexible structure. Popular options for SQL is
MesquLEqtOracle, et cetera. Where in the NoSQL, we have Mongo DiBRds et cetera. Might ask when we choose SQL and when we go
for no SQL database. So it depends on
which type of data you are dealing with and
what your application need. We use SQL, if we want our data structured and
organized in table. Also, if we need to run
complex queries for analytics or if data
consistency is used, then we use SQL database. On the other side,
we use no SQL, if we want our data is
unstructured or semi structured, except rapid data growth, we need flexibility
in our data model our application is
real time or big data, then we use no SQUL database. For example, we are creating
e commerce applications, which has many features like
user can upload reviews, product suggestion in which our data is unstructured
or semi structured, and also users might give
reviews in large numbers. At the time, which
database should we pick? Right, we use here,
no SQL database. Let's take one more example. Suppose we are building
banking system where we need strict data and accurate
financial transactions. Which database we should choose? Right, use here SQL. So it really depends on
which type of data we are dealing with and what kind of application we want to build. Most of the time, your
team will tell you which database you should
add in your application. So don't worry about that. Many developers really
don't know the difference between SQL and no
SQL. You know, right? In many node applications, developers default
choice is Mongo DB, which is the no Squal database. And if you want to
become MSTech developer, this is for Mongo DB. In this course, I add
MongoDB as database. Don't worry, we will learn
MongoDB step by step.
59. Install MongoDB in Windows: Let's install Mongo
DV on Windows, and if you have MG, then you can skip this lesson. First of all, head
over to mongodib.com. Now go to these products and
select Community Edition. Go for download Community. Scroll down. And here we can
select Mongo Di B version. In my recommendation,
please don't change it. Next, we can select
our platform, and then you can select package. Don't worry about this,
click on Download. See, download is started. Now, after completing
the download, open that setup, and it will ask for
installation permission. Allow it. Click on next. Accept the agreement.
Click on complete. After that, from here, we can change our
installation path. But if you don't have any
reason, then don't change it. Just click on next. Make sure to select
this Mongo DB compass, which is the application
for MongoDB, in which you can view
all database tables and edit or delete them. Click on next and install. This will take some time around five to 10 minutes because we are also installing
Mongo DB compass. Now, after completing this installation,
let's verify this. Open Command prompt, and
write Mongo D and hit Enter. We get this error,
Mongo D is not recognized as an internal
or external command. So to solve this error, we have to again go to the
Mongo Db website and here at the top products and go to
view all products in tools. Now, scroll down on this page, and here we get the cell option. So select these now click
on Download, scroll down. And here we again
download this setup. Now, after completing
the download, open that download folder and extract that zip which
we just download. Good. Now, open that folder, and in the bin folder, we get this Mongos file. So just copy these and open
your C drive program files. MongoDB server, 8.0 B, and here we paste
set application. This Mongos is our
Mongo DB cell. Now we have to do one last step, which is set this path
to environment path. So copy this path in start, search for environment variable and open the edit the system
environment variables. Let me close as things. Now, click on these
environment variables, and in system variables, select path, and click on Edit. Now we have to add
that bin path here, so click on New and pace
that path. Click on Okay. Okay, and Okay. Now again, open Command prompt. We write mangoes and hit Enter. We will get the Mongo DB cell, so we successfully install
Mongo DB in our system. Now let me give you a quick tour of this Mongo DB compass. When we open this
compass first time, we have to enter our
connection string, which is our local
host, Column 27017. You can write this
connection string, which I added and click
on Save and connect. See, here we can see our
database and also we can see tables and A
we can see documents.
60. Connect MongoDB with Node App: So we install Mongo
Di B in our system. Now let's connect that Mongo
Di B with node application. So for learning Mongo Di B, we will create a new
project because I don't want you to get confused with
previous tastak project. We will add Mongo Di B as exercise in that
project. It will be fun. So for now, in the
projects folder, we create a new folder, let's say Mongo demo, weird name, but it is
okay for learning. Let's open this folder
in the VS code. Now to initialize this project, we open our terminal
and write NPM in it I. Good. And here we create
a new file, index dot gs. Now in node applications for managing and working
with MongoDB, we will use one very popular
library, which is mangos. In simple words, by
using Mongoose library, we can easily work
with Mongo DB. Let's install this library
using NPM install Mongoose, if you want to get
the same result as I am getting in this course, then right here at
the rate 8.13 0.2. Good. Now to use this library, first, we import it
using required function. Here we pass mongoose and we store it in variable
const mongoos. Now, this mongoose
object has many methods. One of them is connect, so mongoos dot Connect. Here we have to pass the URL of Mongo DB database which
we want to connect. We open here, MongoDB
compass application. Here we can see we have
connection string, which is local
host, column 27017. In our code, we write
in codes, Mongo DB, column, double forward slash, local host, Column 27017, which represent the local Mongo Deb database,
forward slash, and here we write our specific
database collection name, let's say Mongo Demo this is
not the application name, this is the database name. Don't worry, we will see
this in upcoming sections. Now, this expression
returns a promise, and you remember, how
can we handle promise? We can use then
and catch method, or we can use async await. Currently, we don't
have nested promises, so to simplify, we can
use here, then method. If you want to use
ASN and Aviate, then you have to create
a new ASN function. So here we add that ten method, which means we connect it
successfully arrow function, and we simply console dot log, Mongo Dib connected
successfully. So it might happen
something goes wrong like Internet
connection got lost, or database don't found or some type of mistakes in
the connection string. At that time, we will get
error in this promise. So we also add
here catch method, and here we get error object. And we simply console dot log, Mongo DB connection failed. Coma that error object. So basically, we are
saying to Mongoose, connect our application to this local database, Mongo demo. Now you might ask, we don't created this Mongo
demo database. How can Mongoose connect our application
to that database? Will it give us error? And the answer no it
will not give us error because if that database Mongo demo is not
available in our system, then Mongoose will create a new database
called Mongo demo, and if that database
is already available, then it will simply use that. Now, if this database
connected successfully, then we log success message, and if our connection fails, then we log this error. Save the changes, and let's run our application using
nodemon index dot JS. See, here we get
connection successful. So that's how we connect
Mongo Di Be with our node application using
Mongos dot connect method. Now, let me tell you something. Previously, Mongo
Di Be used to have an older way to
connect with database, but it was not perfect, and developers was
some issue with that. In some old code, you will see these options passed
with connection. These options are just for handling the connection
more better. You might use new
URL parser to true, which tells Mongoose to use the modern way to
connect with Mongo DB. Also, we get use unified
topology to True, which tells Mongos to manage connections in a smoother
and more stable way. Now in the new Mongo Di version, these options are by default on, so we don't need
to pass them here. I told you about this, if
you see this type of code, you don't get confused by that. Now in the next lesson, we will create our
first document schema.
61. Importance of Schema: Before we store our
data into our database, it's better to define litter
structure for that data. For example, we want to register a new user
on our website. So he fills form with name, email, phone number,
and password. And when we hit register, we store these details
in our database. Now, as a developer, we know sometimes
user forgot to write name or forgot to write
email or password. User can forgot anything. So to prevent these, we will define litter structure
for that data, which means we can
define name is required, email as unique field, which means every user should
use unique email address. Phone number can be
anything or user can also skip this information and
password also required. Now if user don't pass
name or write wrong email, then his data will not
store in our database. We will return error
for that user, and that's how we can prevent unwanted and unnecessary data being stored in our database. That's why defining structure is important and this structure
is called a schema. You might ask in the database
introduction lesson, I told you Mongo DB or no SQL is the schemas or
schema flexible structure, and SQUL is the schema
based structure, and now I'm telling
you to define schema. Why? We know in MongoDB, we will store data in
the key value pair. Suppose in our previous example, some user pass
field name, email, as email address, or
name as username, then how can we manage
this type of data? MongoDB is schema flexible, which means we don't
have to define schema. However, defining a schema
provide consistency, validation, and
predictability, which are crucial in most
production level application. A schema help us to avoid
unwanted or dirty data. Also, by defining schema, we can write query
logic more clearly. Now you might ask Mongo Di B is schemas or schema
flexible structure. How can we define
structure in MongoDB? The answer is Mongo DB don't
enforce schema its data. But by using other
tools like Mongoose, we can define schema for
our Mongo DB documents. Also, if you're
familiar with SQL, in that we have table, suppose user, and in that table, we have rows for each user. Now in Mongo Di B that table
is called a collection. That row can be
called as document. Don't get confused when I use words like collection
and document. They are very similar
to table and row. To we recap, we can
define schema for our Mongo Dew collection for better use of
our application. Now in the next lesson,
we will see how can we define schema for our
Mongo Dew collection?
62. Defining Schema for Document: Let's see how we can define
schema using mangos. So for that, we use new mangos dot schema,
which is a class. Now in this parenthesis, we will add object with our fill name and
structure for that fill. For example, first we need name, column, and here we write the type of data
we want to store. So for name, we want
to store stream. Make sure you write
as in capital. Now after that, we might have email and this also
we want in string. Now, as I told you before, we want to store email as unique value for every
document or row. So how can we define
that uniqueness here? Simply, at the place of this
string, we pass object. This object has
multiple properties. First, we want to define the type of this fill,
which is string, and then we have
another property which is unique and we set it to true. So if we have already
email for one user, that same email cannot
use for other user. After that, let's say we have phone and we pass here object, and we set type to number. Now here we don't want to
make this fill necessary. User can also skip
this phone filled. So here we don't pass anything. And if we only want to pass
type property for the fill, we can directly write
like this number. But I like to follow
consistent syntax, so I leave this as it is. But in the name field at
the place of this string, we simply add object
with type to string, and also we want to make
this field compulsory. We add here, required to true. Make sure here we write
required, not require. I'm not sure only require
is working or not. Also, we always store our
EML ID in the lower case, and we can also define that
here by lower case to true. You can see defining schema is not hard. It is very simple. Now, after that, we have
password and we want to store it as string and
it is the compulsory field. Tell me which properties
we should add here. Right, we add here type to
string and required to true. You are doing really great. Now, after that, this
user might have hobbies, which can be multiple things. So we want to store it in array. Type to square brackets, which is array, and in that, we want simple string. So our hobbies might look
like this array of string. Now after that, we
might have is verified, which source user
is verified or not, and we set its type to Bullion, which means true or false. And by default, we want to
store user as unverified. So for that, we have another
property called default. We pass default value as false. This is how we define schema
for our Mongo DB collection. In the schema, we have multiple
schema types like string, number, boolean,
date, object, array, object ID, which we
will see in the future, B for storing the binary data mixed for any type
of data much more. If you want to know all schema
types and its property, then you can visit this
mongoose documentation. On this page, we get
all the information. Now in the next lesson, we will see how to apply this
schema for collection.
63. Creating Models: In the previous lesson, we
created this user schema. Now, how can we define which collection should
follow this schema? For that, we need
to create a model. So first of all, we store this schema in variable
called user schema. Now to apply this user schema, we need to create model. So we write here
Mongoose dot Model. Inside this at the
first position, we pass the singular
name of our collection, which is here user. So this user will become
users collection. If we want to create collection called post, which is plural, then here, we have to write singular name of that
collection, which is post. Now at the second argument, we will pass our schema
for this user collection, which is this user schema. Now this expression will return the model for
user collection. So we store it in
variable called user. Can you tell me why I'm using this pascal convention or
why I use U for user model? It is because this user is a class and using this
user class or model, we will do many things. To simplify model is
like a blueprint for creating and working with documents in a Mongo
Deb collection. Defines the shape
of our data using a schema and provides a way to interact
with that collection. In simple terms, the model allow us to create new documents
based on the schema, read data from the collection, update existing documents, also, delete documents
from the collection. In short, without model, we can't do anything with
Mongo Di Bi collection. Don't worry we will see all these in the
upcoming lessons. Currently, let me
show you how can we create a new user
using this user model. So for creating a new user, we write new user, and inside this, we will
pass our user object. So the first field of
this collection is name and pass value as code plus U. You can write your name. Next, we have email, let's say, code at did gmail.com. Currently, we are
manually writing these values, but
in the real world, our user from the
front end will write this value and submit
it using post request. And in the back end, we will
handle that post request, and inside the post
request logic, we will write this
new model expression. So don't worry about that. Next, we have phone, which can be anything
23, 51, 552. Next, we have password. Make sure here we write the same fill name as we
defined in the schema. Password to string 123, four, five, six, 78. And hobbies to array, and here I add learning,
teaching, and tracking. For the last field, we already set the default
value in our schema, so we don't need to
pass that fill here. That's how we can create a new user or document
of the collection. Here, we store it in a
variable per new user. Now, currently, this new user
is available only locally. In the next lesson,
we will see how we can store this new
user in our database.
64. Saving a new Data: So here we have new user data. Now let's store it
in our database. So this new user
data has one method, which is dot CV. By using this save method, we can store this new user
data in our database. Now saving the data in the
database is asynchronous task, which means it can take some time to save new
data in the database. And that's why this
expression returns a promise. You can adhere, await and when our data successfully
stored in the database, it will return the stored user
object from the database, so we can store it
in variable called stored data and simply console
dot log with stored data. Now as we know, when
we want to use await, we need a acing function and
only inside that function, we can use await. Otherwise, our code
will block next lines. Here, before this
new user object, we create a new acing
function called create user. And inside this function, we can simply move this
new user and save method. And at the end, we will call
this create user function. Now don't save the file, it will automatically
run this file. I terminal, I stop our node bond using Control plus C.
Now save the changes, and let's see this
is working or not. The terminal, we run this
file using node index dot js. Don't use here nodemon
because we only want to run this index
dot js file one time. If we use nod M and we change
something in our file, a new user will
create every time. See, here we get
new user data with our fills and at end we
also get underscore ID, which is the unique
ID for this document, and it is generated by
Mongo DB and using this ID, we can do many things. Also, we get here underscore
underscore to zero. Is that? This
underscore underscore is used for document
worsening in mangos. When we create a new document, Mongoose adds the
underscore underscore filled and set it to zero. Now each time the particular
document is updated, mangos increase the underscore
underscore filled by one. For now, don't worry about that. Also, we can see this data in the Mongo DV
compass application. See in the local database, here we don't get our database, click on the Tree dots
and refresh database. See, now we get
Mongo Demo database. Inside this, we have
users collection, which we created using model, and in that collection, we have our first document
with Unique ID, and also we have our fills. Let's say this Obs array, which is a key value pair, key is the index
of that element, and value is our string. We get data from the database
in our index dot JS file, then we get normal
array of string. It is just showing here
in the key value pair. Also, see, by default, we set this is verified
value to false. And yes, we can change data
from this application. So to quick recap, first, we create a new user object
using this user model, and then we can simply use Lots method to save
this document. Now let's try to store
another user data because we need that data
in upcoming lessons. I changed his name to Halley, email to Halley at
direct gmail.com. If you want to change something, and you can change in the
phone number, password, Harley 123, and hobbies, let's say, coding,
Jimming and tracking. Save the changes and
in our terminal, we run node index dot js. Good, see, here we get new data. If we check our
Mongo Di B compass, we don't get here data. So we refresh from here and
see here we get our new data. That's how is to data
in our database. If we follow this step by step, then it is really simple.
65. Query the Data: Now let's see how we
can query the data. But you might ask, what
is the meaning of query? Query is simply a request for information
from the database. In simple words, a
query is just a way of asking the database for specific data based on
certain conditions. For example, from our
user's collection, we want to get
data of all users. First of all, we write the
model of that collection, which is the user dot here we
get multiple query methods. We have find by
ID, and find one. Also, we have other fine methods for update, delete, and replace. Don't worry about those. We will see them in the
upcoming lessons. Currently, we just
focus on this find, which is used to find multiple documents from our collection. So we have Fine n
which is used to fetch only single document
from the collection. Also we have Fine by ID, which is used to fetch
document by its unique ID. Currently, we want to
fetch multiple documents, so we use here dot Find. Now, this expression will
also return promise. We can use here then method, or we can use Ising await. In this use case, ain await is simpler and help us
to write clean core. Here, we create a new
ing function called get users and simply move this
await inside this function. Now, this will
return the data of all users from the
user collection. We store it in variable called users and Consol dot
log this users data. Also at the place of
this creatuser function, we call GtusersFunction, and
move it below this function. Now let's see we are
getting data or not. Save the changes. And let's run this application using
node index dot gs. See, here we get array
of two users data. First one is code
plusU and then Harley. Lovely. That's how we fetch
all data from the collection. Now, let's make this
more interesting. Suppose we want to find only those users
whose name is Harley. So to do that, we pass
object in the fine method. Here, we can define multiple conditions
in key value pair. That's a name two
encodes Harley. This will find all users
data whose name is Halley. Also, here we can pass multiple conditions like Es
verified to false, et cetera. This query will
check if the name is Halle and its is verified
value is false or not. If both conditions are true, only then we get
those users data. Say this and let's
run our application. See, here we get
empty array. Why? As we can see in our database, we have name Halle and also
is verified is set to false. Then why we are not
getting this data. Here in the name property, we pass Halle in lower case. But in database,
H is upper case. So let's change this condition, see what the changes, and let's run our application
one more time. See, here we get our data. So remember, if we pass
string for condition, make sure we write
in case sensitive. Good. Now currently we are
getting data with all fills. What if we just want to fetch
the username and hobbies? So for that, after
this fine method, can add select method
and inside the codes, we pass our fields name
separate by space. We write name space hobbies. If we want other fields, then also we can pass
here with space. For now, we don't want that. Save the changes, and
let's run our application. See, here we get only name, hobbies and underscore ID which is automatically
added by Mongo DB. Also, if from our data, we just want to remove one
or two fills like here, we want all data except
password and is verified. So at the place of
these fill names, we pass minus password, space, minus is verified. Save the changes, open terminal, and let's run our file. See, here we get all fails except password
and Es verified. Simple. Now let's make this
little more advanced query. Suppose we have 100 user data and we want to fetch those data, but how can we fetch 100
data in single array? It's better we fetch
first ten data, then after that,
another ten data. So here we remove this
condition because we want all data and
after the select, we add limit inside this, we can pass the number of
records we want to see. Let's say ten. Save the changes, open terminal, and let's
run our application. See, here we get two records because we
only have two records. If we have 20 records, then we get only
first ten records. Also, as we have limit, we also skip method. Here we pass the number
of data we want to skip. If we have 20 records
and we adhere skip five, then it will skip the
first five records and then display six to 20 data. This limit and skip method are very useful for
paginess and query. Don't worry, I added
separate lesson for that. For now, just
understand Skip and limit are available.
So let's remove this. Good. Now at last, we can also short our
data by any filled. Suppose we want to short our user data by
its name filled. So we can write here dot short, and inside this, we pass object, and here we pass our field name, which is name, and here we
can pass two values one, which is for ascending order and minus one for
descending order. Let's pass both one by one. First one, save this and
let's run our application. See, we get users
with ascending order, which means A to ZD if we
change this to minus one, save this and let's
again run our file. See, here we get
descending order, which is Z to A, and that's why it display har first and then
code bless you. Don't worry, I will give you detailed PDF of this section. You can revise these methods
as fast as possible.
66. Comparison Operators in MongoDB: Let's learn about comparison
operators in Mongodib. Comparison operators are
used to compare values in the database with the values
we specify in the query, and it is really important
when we are working with data, especially number based data. For example, imagine
in our users data, we have age filled, and we want to fetch
users data whose age are 18-30 greater than
18 or less than 50. It can be anything. When we want to do something like this, we need comparison operator. There are many comparison
operators in Mongo Deb. First, we have dollar EQ, which is for equals to. Next, we have dollar N, which is not equals
to dollar GT, which is greater than
dollar GT E. Can you guess? Right, greater than or
equals to dollar LT, which is a less than dollar LT for less than or
equals to dollar E. This is used for matching any
of the values in the list, age should be 18, 22, 25, like that. Can pass multiple
values in array. And at last, we have dollar NIN which does not match
any values in array, and it is opposite of dollar N. Comparison operators
are really simple. Let me show you where we have to write comparison operators. So to make it practical, let's add H field for
these two documents. So Pan Mongo to be compass and go to the user's collection. Here, we can modify each
document using this edit icon. Over any of the field. And at the left side, we get plus icon for adding new field. Select add new field, and here we write our fill name, which is age, and we pass
value, let's say ten. Currently, this value
type is string, but we can change that from here and we set
it to integer 32. See, now it is integer. Also we add new field
for the second document, fill name, age, and
value, let's say 20. As we changed a type to integer
32 and click on Update. Code. Now, in our code, previously, as we can see, if you want to add
condition for our query, then we add that fill
in the object here, like age to ten. This will fetch all users
data whose age is t ten. Let me ask you one
question where we can write our
logical operator. Because as we can
see, for condition, we have to pass key value
pair in this find method. So the solution is at the place of this
hard codal value ten, we can write our comparison
operator in another object. Don't get confused, see this. So at the place of
ten, we add object, and we simply write comparison operator as
key in this sub object. Let's say dollar GTE, which is greater
than or equals to, and greater than
or equals to what? 18. So this simply means find all users whose age is
greater or equals to 18. Just we replace that
hard coded value ten with the object, and inside the object, we use comparison operator. Now let me give
you a little task. Let's remove this whole
object from the fine method. We want to fetch all users
whose age is less than 18. Here we add object, and first, we add on which field we want to apply condition, which is age. And instead of hard value, we pass here object because we want to use
comparison operator, and inside it, we use dollar T for less than
and less than to 18. Simple as that. Save this
file and for the find method, we can run our application using nodemon index dot js because it will not change
anything in the database. See, here we get user
whose age is less than 18. You can see just we have
to pass Object with comparison operator at
the place of hard value. Also, these first six
operators are very simple, but many students confused in this in and not in operator. At the place of value, here we have to pass
list of values. So let me show you
these two operators. Suppose we want to
fetch only users whose age is 18, 20 or 30. So when we want to compare
multiple values for one field, then we use dollar in operator. So at the place of dollar LT, we use dollar in. At the place of value, we pass array of
values, 18, 20, 30. Simple as that, save the
changes and take a look. See, here we get user with age 20 because we pass
20 in this array. Now dollar NN is the
opposite of dollar in. I call it an inside, which means patch users whose
age is not 18, 20 or 30. Save the changes, and see, here we get user with age ten. So that's how we pass comparison
operators in Mongo DV. Just remember in
regular condition, we pass here fill with values, but for comparison operators, we pass here fill with object, and inside this object, we add our comparison operator.
67. Logical Operators in MongoDB: Let's see logical operators. So logical operators
allows us to combine multiple
conditions in our query. In simple words,
they help us to ask more complexes from the
database. Don't worry. There are only few
logical operators, and they are also simple
as comparison operators. First logical operator
is dollar R. We will use operator when we have multiple
conditions and we want, if any one condition is true, then return that data. Example, we want to
fetch users who are 30-years-old or
have name Halley. In this case, we want any one
condition should be true, and that's why we use
here or operator. Let me show you
that practically. I comment out this previous
code and write cost users is equal to await user dot Find. And in this find
method, we pass Object, and in that object, we simply add
dollar or operator. Now you might think a
comparison operator at the place of value, but we are using
logical operator at the place of condition. Let me give you my
trig to remember it. As we know, we always
compare values, and that's why we have to write comparison operator at
the place of value. But we implement logical
operators for conditions, and that's why we have to write logical operator at the
place of conditions. When I started Mongo Deb
I remember it this way. As we know, we want to add here multiple conditions and what we use for multiple conditions, we will use array. Now inside this array, we add our conditions
in individual object. So object age to 30 or we pass
another condition name H. If any one condition is true, then we get that user data. The changes and take a look. See, here we get the user Halley because its
name is Halley. This user age is same as our condition or not.
It doesn't matter. If any one condition is meshed, then we get that data. Simple as that. Now suppose
we want for our query. These both conditions
must be true, which means user age should 30 and also name
should be Halley. In this situation, where we want all conditions
must be true. We will use end operator. So at the place
of this operator, we pass and operator. Now, can you guess the output? Right, we don't get anything
because we don't have data where age is 30
and name is h. See, here we get empty array. Now let me show you shortcut way to write this and operator. When we want these all
conditions must be true, we already write the same query at the beginning of
the query topic. We can directly pass those conditions in the
find method like this. These both works the same. But when we want
to use operator, then we need to
follow this syntax. Now, after dollar end operator, we have another logical
operator which is dollar NR, or we call as logical NR. For example, we want
to find users who are not 30-years-old and do
not have the name Harley, which means both conditions
should be false. For that, at the place
of this end operator, we pass NR operator. The changes and see, here we get data with name God bless you because for this data, both conditions are false, and that's why we get this data. But for our second user data, this name condition is true, so we don't get that
data simple as that. Now the last logical operator we have is dollar naught
or logical nut. This logical knot operator is
little different because we only use not operator with comparison operators and
regular expressions. Don't worry about
regular expression. We will see that in
the next lesson. For now, let's see,
dollar naught operator. Suppose we want to find users whose age are
not equals to 30. Again, I comment out this
syntax and write again, cost user equals to
awight user dot find. Object, and first, we add filled for which we
want to add condition, which is H. Now at
the place of value, we pass object with
operator dollar nu. We write dollar nut operator at the place of value because dollar naught
operator is directly related to value not
with conditions. Now what we want is age
should not equals to 30, we have age and also nought, but we don't have
equals and value. We addhe another object
with dollar operator, which means equals and
simply pass here value 30. Save the changes
and take a look. See, here we get both users because both age is
not equals to 30. You can see it is really simple. Let me show you one more time. In the comparison operator, we are writing like this. Age column, dollar equals
to or greater than or less than whatever operator we want to use to its value. Now here, we just want
to add not operator, we simply wrap this object
with another object and add here dollar naught operator
and colon simple as that. I know this is a
little confusing, but don't worry if you don't remember the syntax
of operators, I will give you my cheatsheet
at the end of this section so you can use it when we will work on real
world projects. That's all about
logical operators. If you are continuously
watching the course, then you can take little
break from your screen, listen some music, or
take a fresh walk. See you in the next lesson.
68. Regular Expression in MongoDB: Now let's see about
regular expressions. What is regular expression? A regular expression is a way to define search
patterns for strings. For example, we want to find all users whose names
start with edge or we want to fetch all
users whose email has gmail.com at the
end of their email. In these cases, we will use regular expressions or some
developers called it jx. It is really useful in auto suggestions query
or search query. Let me show you practically. Here I comment out
this previous query. Also, remove old queries
and write a new query Cast users equals to
await user dot find. Here, we pass object, and on which property we want
to apply search pattern. Let's try name. Now at
the place of value, we pass regular expression. Here is the syntax of
the regular expression. Slash pattern slash. So based on this pattern, we will do many things. Suppose we want to
find all users data whose name start with So at
the place of this pattern, we use Kerat symbol, which means start with, and here we pass cptalH. This query will find all
users whose name start with capital so if we want to find users whose name
end with any letter, then I duplicate this
line and at the place of this carat, we write dollar. This dollar means end with. So this query will return all
data whose name end with U. Now let me give
you a little task. We want to find users whose
email ends with gmail.com. It is really simple. We change this fill name to email and simply at
the place of this, we write gmail.com dollar. Let's see we get
the results or not. See if the changes, and C we get both users because they
both have gmail.com. Now let me show you
something cool. In the Mongo Di B Compass, I change the second user email
at the place of gmail.com. I write Gmail, com. Save this, and let's see
what we get. Save this file. And see here we get,
again, both users. You might ask, even if this email doesn't
end with gmail.com, why we are still
getting this user data. What is wrong with our
regular expression? In regular expression, this period can
match any character, which means JavaScript will
match it with any character. And that's why if we pass any character at the place of this period, we get that data. Now we might ask, what if we want to compare
period as period? Not as any character. It is really simple. Just before the period, we will use backward slash. Now, JavaScript will compare
this period as period, not as any character. Save the changes and see, here we get only first user. Also this pattern
is case sensitive. I any user have email in
uppercase like gmail.com, then we will not get
that user in this list. So to remove case sensitive, we have to just add I at the end of our
regular expression. Now, what if we want to find users whose email
contain Harley? Not start with Halle
or ends with Halley. Harley can be anywhere. So in this case, we
simply write our word at the place of pattern without
adding garret or dollar. So if we want to find
users with exact word, let's say developer
in the description, not part of another
word like developer, we want only developer. In this case, we write
pattern like this. Here at the place of this
pattern, we write that word, which is developer and
before and after that word, we add backwards B, backwards B, which represents
the boundary of that word. These are some common and
useful regular expressions. If you want to
learn more patterns of regular expressions, then you can use this
article and read about other patterns because
it is pure JavaScript. Also, in current
Mangodib version, few developers use
dollar RejxOperator at the place of adding
direct patterns like this. This both works the same. I like this Sater version, but you can also use
this Rjxoperator. It's totally up to you.
69. Count and estimate Document Count: Suppose we want to
count the number of documents available in
our user's collection. I duplicate this query and
comment out previous query. Now when we want to only count
the number of documents, then at the place of
this fine method, we pass count Documents method. This query works
the same as before. We get a number of
data in output. In the Count documents method, we pass our conditions or we
also pass logical operators, comparison operators,
regular expressions, same as find method. Find method returns
the actual data, where Count documents method will return the
number of document. Let's check this. See the
changes and take a look. C, here we get zero because there is no data
for this condition. Now sometimes we don't want
to add any conditions, only we want total
count of documents. At that time, we can
remove this object, save these and see, here we get the total
number of users data. Now, currently, our
data is very small. Imagine we want to
count the number of products for big
ecommerce applications. Want to display the
total number of products available on the platform
in the admin dashboard. At the time, we don't need the ext count that shows the
total number of products. Now, at that time, we can use another method for counting the estimate number
of documents, which is estimated
document count. As its name suggests, this is an estimated count. Same as count documents, we can pass this estimated
document count method right after the model name. Now you might ask, what is the difference
between these two? First one is count documents
returns the accurate number, but estimated count
display approx count. Another difference
is we can pass conditions or filters in
the count documents method. But in estimated
document count method, we can't pass any condition. It can only count
full collection. So count document is little slow than estimated
document count. So to summarize, we use
count documents when we need to apply filters or
we want to get exit count. And at the other side, we use estimated document
count when we need a fast and rough estimate of the total number of
documents in the collection.
70. Pagination & Infinite Query: Let's see how we can create
pagination or infinite query. Before we see the query, it's better we understand how pagination and
infinite scrolling works. Suppose we are working on
big ecommerce application. In this application,
we might have 1,000 or 10,000
products like Amazon. Now at the time, we get all products detailed
in single API call. It will take more time and also the load on our
server will increase. So instead of
getting all data in single request as
a Bond developer, we can divide them
in pages like we get only eight or ten
data in single request. If user need more data, then we fetch next ten data. Don't worry about how we
will create this API. For now, just understand how pagination and
infinite scrolling works. Here is the front end
example of pagination. On first page, we have only
eight data after that, when we click on
second page button, we'll get next eight
data. Simple as that. Let me show you also
infinite scrolling example. Here we have eight data, and when we reach to
the bottom of the page, it will load another eight
data from the back end. See, this is really cool. Now just think in
human language, what is happening here. Don't worry about code. Just think what is happening. Pagination and infinite scrolling
both works almost same. In both techniques,
we will fetch data in small
quantity as we need. Just in infinite scrolling, we keep our previous data. Wherein pagination, we will
replace the previous data. But back end for the both
techniques will stay the same. Let me explain you with
20 records example. These are 20 records at a time, we want to show
only four records. We can define variable
page per data to four. We get four records from here and we can mark
it as page one. Now, if users scroll or
even move to second page, then we will skip
this page one data, which means we skip per page four data and then fetch
another four records from here. Next, when user again scroll
or move to third page, then we will skip page
one and page two data. Mathematically, we will
skip current page, which is three minus
one into per page data, which means two into
four is equal to eight. We skip first eight data
and fetch another four, which is per page data. Every time we increase
our page number, we will skip current
page minus one into per page data and fetch another data which is the
number of per page data. Tell me how many we will skip when we move
to fourth page. Right, we will skip
four minus one, which is three into
four, which is 12. We skip first 12 records and fetch another four
data. Simple as that. In Mongoose, we
have skip method, which we've seen previously, and also we have limit for
just fetch and number of data. So here we remove this query because it
can create confusion. Now we write const
users equals to await user dot Find here we don't
want to pass any condition. Now, after the fine method, we adhere Skip method, and inside this, we have to pass how many number of
data we want to skip. For now, we pass directly zero because for the first page, we want to skip zero data. After that, how many data
we want in our single page? Right, we want to
send four data. So we add here another method called limit and pass here four. This query works for page one. Now let's make this query
work for every page. So when you searchange page
to two and call this API, we will simply skip
first four data. And for that, we created
formula in our example. So at the top, we
define variable. Current page is
equal to say two. And after that, another variable called per page data
is equal to four. Currently, as we can see, we hard coded these values. But in real world, these values are passed by the front end. And do you know by which
front end send these details? Any guess? Right, front end will send these details
using query parameters. Suppose our products
API look like this, slash API slash
products, question mark, current page is equal to two, and per page data is
equal to four or ten, anything our front
end developer wants. But for now, I don't want
to add this complexity. That's why we hard coded
these values here. Now in this kip method,
we pass parenthesis, current page minus one
into per page data. And in the limit,
what we will pass, right, we pass per
page data and done. Let me save this so we
can see this clearly. Great. Now, if we
check our terminal, we get tray because
in our database, we don't have more
than four data, so we don't get
data per page two. If we change the
current page to one, save this and see
here we get to data. We created query for pagination
and infinite scrolling. This single query will
work for both features. I hope I explain this well. If you still have confusion, then try to put different
values in these variables. I bet you will understand this.
71. Update the Data: Now let's see how we can
update the data in Mongo DB. So there are two
ways to update data. In first method, we will first find the document which
we want to update. After that, modify its property, and at last, we save the
updated data in our database. And second method is, we
will use update methods of mangos and directly update
the document in the database. After that, we have
option to return updated data depending
on our needs. Now what do you think
which method is fast? Now, what do you think
which method is useful? Yes, second method is more useful because in
the first method, we have to do multiple steps, which might take
little long time. But in the second method, we will use methods
defined by mangoes, so we don't need to worry about
updating data by our own. Also, we will use first method
according to our needs. Now there are four methods for updating the document.
Don't worry. Every method is very
simple and easy. Let me show you that. Here I
open MongoDB demo project. And here, let's suppose
we want to update our user email ID whose
name is code bless. So we create here a sing
function called Update User. And inside this function, we will write our update logic. So for Update, we
use user Module dot. Here we have Update one method. Now, same as the fine method, we can pass here
query object in which we will apply filters or
we can say condition. So what is condition here? Username should be code plus. So we have declared which
user we want to update. Now we just need to pass which property we
want to change. So for that, we pass another object at the
second parameter, and inside this, we have dollar set operator
and we pass to object. Now in this object, we will specify which property we want to change with
its updated value. So we want to change email to updated at the red gmail.com. So we can adhere
multiple properties. But for now, we just
want to change email. Now, as we know, this
expression is Asnruners task. So we can adhere await. Let me save so we
can see properly. Good. Now, this expression will not return the
updated object. Let's see what we get here. So we store it in
variable called result, and simply at the bottom, console dot log this result. Also at the place of
this Gate user function, we call Update User function. Now, before you save changes, make sure you stop
the application. Now save the changes
and take a look. Run this application
using node index dot js. See, here we don't get
updated user data, but we get some basic
information about ddt task. Let's verify how data gets updated in the
database or not. So Pamongo be
compass and see here our email is changed
to updated@gmail.com. So that's how we
update the data. At first, we pass query
object with conditions, and at the second argument, we pass dollar set operator, and we pass object with
updated properties and values. Simple as that. But
also keep in mind that this update one method will
only update one document, not all documents who
fulfill this condition. Sometimes we want to also
get the updated document. Don't worry, it
is really simple. At the place of update one, we will pass find
one and update. This will return the old
document before we are updating. But here we want to get
the updated document. Forgetting the updated
document, we pass here, third object, and in that
we pass new to true. Also, as a good practice, we will pass another
option here, which is run validators to True. Now you might ask what
is run validators? It is nothing we are
telling our query to ensure these updated values should follow the model schema. Also, let me change this email
value to blessthergml.com. The changes and take a look. Let's run this
application again. See now we get here
updated value. That's how we can
update values using update one and find
one and update method. Both are very similar. Update one, don't return
the updated document. Where using find one and update, we get the updated document, but these both only
updates one document. Now let me give you
a little task here. We want to update name of
the user whose ID is this. Can you write the query?
It is really simple. Here in the query object
at the place of name, we pass underscore ID, and we pass here our
user ID in string. You can copy your user ID
from the Mongo DB Compass and also change this email value
to XYZ at achmail.com. Save the changes
and take a look. Run this application. See here we get the updated document. Lovely. So this is very simple. Many times, we want
to find our document only based on their unique
ID like we currently did. We don't want to pass any
other condition because this ID is unique and there is no point for passing
other conditions. When we want to
update data using their ID at the place of
find one and update method, we have another shortcut method, which is find by ID and update. Now here we don't need to
pass this query object. At the place of that, we
directly pass our ID in string. The changes and take a look. Run this application. See, we
again get updated document. So when we want to
find user by its ID, we will use fine
by ID and update. Now we have seen these
three methods for update. Update one, find one, and update, and find
by ID and update. If you notice, these all methods update only single document
which they found first. Two, update one and find one and update their names are showing they are only
updating one document. And if we talk about
fine by ID and update, what we are passing
here as condition, we pass unique ID, which means it will also update only single record
whose ID is this. But what if we want to
update multiple documents? For that, we have fourth
method of update, which is update many. At the place of this
fine by ID and update, we add update many. So here we have to again pass your query object in which
we will define conditions. So you pass your age to 20. So here we are telling
update our records whose age is 20 and update
their email to this value. Let me change this email
value as hegmil.com. We don't need this
third argument, which are options because
this will only work for fine one and update
and find by ID and update. See the changes, and let's
run this application. See, again, we don't
get updated document, but if we check our database, our value gets updated. So to quickly summarize, we have four methods for
updating data in Mongo DB. Update one for updating
a single document, update many for updating
multiple documents, find one and update for finding and updating a single
document in one step, last one, find by ID and update for finding a document
by ID and updating it. Also, in find one and update, and find by ID and
update method, we pass Option object in which we will set new
to true for getting new updated data also
run validators to true to follow the model schema for updated values.
Simple as that.
72. Update Operators in MongoDB: Now let's talk about
update operators. Update operators are used to modify documents during
an update operation. In simple words,
update operators allows us to update
specific fills, increment values, set new data, remove fills, and much more. Let me tell you something. We already use one
update operator, which is dollar set, but we have many other
update operators. So head over to browser and type update operators
in Mongo DB. Open this first link, and here we can see we
have update operators like dollar current date for setting the value of the filled
to current date. Dollar ink for incrementing the value of the fill
by specific amount. Suppose someone like the post, so we can use
dollar ink Operator for increasing the
value of the fill. Also in the ink operator, we can pass negative values
which can decrease the count. Next we have mean, max, ML for multiplication, rename, set, set on insert, set for removing the
field from the document. Also at the bottom, we have operators for array, pull, push, pull all, et cetera. Don't worry about
these operators. We will use many of them
in our future projects. For now, I just want to introduce to these
update operators. In the next lesson,
we will see how can we delete document
from MongoDB?
73. Delete the Data: Now let's see how we can delete
data from the Mongo Div. It is very similar to
updating the data. We have four methods
for deleting the data. Delete one, delete many, find one and delete, and last one, find
by ID and delete. Now you understand why I take more time for explaining
update methods. They are very similar
to delete methods. Let's quickly see
these delete methods. Here, we create a new ASN
function called delete user. Now inside this, we use user dot Delete one and what
we will pass in this method. We just pass query object because here we just want
to delete the document. We don't need to specify
what we want to update, we can pass something like this ascore ID to some user ID, which I copied from
the Mongo Dib compass. This will remove this
single user with its ID. Now we can adhere await, same as before, and simply
store its result invariable. This result is the object
with deletion property. We don't get here document. Now, let me ask you one thing. What if we want to get this deleted document
in the result? Which method we will use? We can use here, find
one, and delete. And here, we don't need
to change anything. So at the place of fine
one and delete method, we can use fine by ID
and delete method. But when we use fine
by ID and delete, we don't need to pass here
object with ID property. We can directly pass like this. I don't want to
borrow by showing these methods because they
are almost same, right? Imagine we want to
delete all users whose age is greater than 15 and they are
also not verified. It is really simple. We use here, delete many method, and at theblas of this ID, we pass query object,
and inside it, age should be greater than 15. And another property
is verified to false. Now let's console
dot log this result. And at the place of calling
Update user function, we call deleted user function. Save the changes, and let's
run this application. See, here we get
deleted count to one because only one document is fulfilling this condition. And if we check our database, then see we have
only one document. So that's all about
delete operation. It is really simple
than updating the data.
74. Exercise 01 - Configure MongoDB: Now it is time for exercise, you can practice by your own whatever you have
learned in this section. Open up our first project, Task track, which we left
in the Section five. We can practice on that project. If you don't have
previous project code, don't worry, you can download it from below of this lesson. Now I design this exercise for you to go through various
steps of Mongo DB. First of all, step one, I want you to connect this project one with
Mongo DB database. You can give this database name, Task track or anything. After successfully connect
to the database in step two, you have to create a
schema for storing Tudous. In your project,
create a new folder called models and
inside that folder, create a new file
called Tudous dot js. In that file, you have
to define schema and model for Tudos collection
separate from our other code, and fills should be like this. First, we had task in which we will define the
text for that Tudo. Make sure this fill
is compulsory. Next fill we have is status, which can be Tudo doing or done. Have to think which field
type you want to use. Also, the default value of
this field should be to do. Now, last field is text, which is the area of
values in which we can add related language
texts like SDML, CSS, JavaScript, react, node, et cetera, and that's
it for this schema. Now step number three,
based on this schema, have to define a model
for Tudou's collection. Simple. I know you can do this. I also add this exercise
instruction PDF below of this lesson. Also, don't worry if you don't remember the
methods of Mongo DB. You can use the
summary PDF which I added at the end
of this section. The goal of this
exercise is you get through the process of
the Mongo DB steps. So give your 100% and
then watch the solution. I hope you solve this
exercise or you try to solve this exercise and
stuck in one of these steps. Don't worry, at least
you try to solve it. So give yourself
credit for that. Now let's see the solution. First of all, in our project, we have to install
mangos because without Ted we can't
do any other things. So NPM install mangoes
at the red 8.8 0.0. God. Let's minimize
this terminal. Now in our index dot JS
file, at the very top, we input const mangos is
equal to require mangos. And after this Studos route, we write mangos dot Connect. At the first argument, we pass an excess ring, which is Mongo DV colon double
forward slash, Local host. Column 27017 slash, and here
we write our database name, which is TAS TAG. Now, as we know, this
expression returns a promise. So we useart then method and inside it simply
consult dot log. Mongo DB connected successfully. Now, what if we
get error in this? So we also add cache method. Here, we get error Object, error function, and
console dot log. MongoDB connection filled. And print this error. So step number one is done. Now step number two, we have
to define schema for task. So we create a new folder in
our project called models. And inside this folder, we create a new file
called todos dot js. Now you might ask why we
have to create a new file? We can't define it in
our index dot js file. Yes, we can define schema and models into index dot JS file. But imagine our project
has five different models. Think how messy our
code become we add five different schemas and model in our index dot JS file. This is the real world practice. We will define all models and their schemas in the separate
folder, which is models. And whatever model
we want to create, we give its name to the file. In this case, it
is Tudos dot js. Here we first import Cs mangos is equal
to require mangos. After that, we create a
new Mongoose dot schema, and inside this, we pass
our fills in object. First one is task, and we set it to string. But also, we want to make
this fill compulsory. So at the place of this string, we pass different
properties in object. Let's say type to string
and required to true. Next we have stats to object, again, type two string. And here we want to
give the field values. So we write Enum
property to array, and here we pass values. First one is to dos then
we might have doing, and then we write done. So if we try to enter
any fourth value, then these values, then
that will not work. Also, we want to give
its default value, so default and we set
it to todo. Good. Now next, we have text, which is array of string. We can directly pass here
array and inside it, we pass string Done. Our schema is ready. Let's store it in variable
called Tds schema. Let's move to the third step. Do you remember how
can we define model? We use mangos dot model. Here, we first pass the singular
name of our collection, which is Todo, which will
become Todos collection. At the second argument,
we pass Todos schema. Now this will return
the todo model. Let's store it in
variable called todo. Make sure we use
your pascal case because this todos works
like JavaScript class. We completed our first exercise. In the next lesson, we
will move little forward.
75. Exercise 02 - For Storing Data: Now let's move to second
exercise in which we will recall about inserting
data in Mango Div. So here is the exercise
to Instruction PDF. You can also download
it from the right side. Also, I added one JSON file in which I added eight
records for Todos. Now in this project, we have already API
for creating new todo. But in this API, we are simply pushing new data to
our local array. Instead of that, we want to store data in our
todos collection. Let me give you
here a little hint. We can do anything in the
collection using the model. You have to get model from the models folder and use it here. Here is a little demo for you. When we send post API request with this JSON object,
in the back end, we will store that data in our database and return
stored data in the response. We already done this
in this section. You can also use that current
section code for reference. Try to solve this and
then watch the solution. So I hope you solve it
or try to solve it. Now let's see the solution. First of all, we have to insert records in our todos collection. But we know we're doing
anything with collection, we need that model. How can we get Tudo model here in this Tu Dos routes file? Right, we have to export our
model from Tudos Model five. So we write here,
module dot Exports equals to this Tudo model. This will export this Tudo
model as default. Save this. In the TudosRout at the top, we import Const Tudo is equal to require now currently we
are in the routes folder, and we want to access
the models folder. So we have to go back
in our folder using double dot forward slash models, and inside this
todos dot js file. Good. So we have Todo
model in this file. Now let's quickly store that Todos data inside collection. So we move to the post request, which we create for adding new to do inside our local array. First of all, we can comment out this Tudos array
dot push method, and at the place of this, we can write Nu to do. And here we pass
out to do object. First property is task, and how can we get the task? Right at the top, we get the request body and
store it into do variable. So at the bottom, we
write to do dot Task. Next, we have status, which we again get from Tudos dot status and also
tags to Todo dot tags. Here we are not adding ID because it will auto
generated by MongoDB. Previously, in this section, we use here hard coded value, but here we are using values which we get
in the request body. Simple as that. Now,
this will return a New to do object
and to save it, we write here Nut dot save. As we already know, this
is async runners task. That's why we use here away. But see here we are
getting runtime error. For using Avid, we have to
make our function async. But how so here in this API, our function is this started from request and response
to this CR bracket. Now to make this function async, before this parenthesis, we pass an keyword. Simple as that. See, error is gone. Now, this expression will return the stored data
from the database. So we store it in variable
const stored data. And then we send this
store to do as response. Now let's see this
is working or not. Save the changes and let's run this application using
nodemon index dot js. Good server is running, and also we get Mongo Db
connected successfully. Perfect. Now using our
tender client extension, we will send post request. We have already saved
this create to do API, so we will directly use this here in the
body of the request, instead of using form encoded, we will use JS and format. Will make our task easy. Now open JSNFle which I
attach with this lesson. You can download as you download
PDF from the right side. Now, simply, I copy
the first to do object and paste it
in the request body. Also, make sure you a prefix API in the URL and
send the request. See, here we get our new
data with auto generated ID. Also, we can check that in
the Mongo DbCompass see, here we get the todos data. Now I fast forward this lesson and one by one,
insert all todos. Good. You can see how we are progressing and solving
each exercise step by step. In real world, also, you
have to work step by step. By that way, you get less
confused and work with ease.
76. Exercise 03 - Fetching Data: Now it is time for
the third exercise, which I design for
fetching the data. After adding multiple
values in the collection, I want you to write query
for this collection. We have already one G method
for fetching all to dos. You can write all queries one by one here and complete
the exercise. Exercise three first query is we want to fetch
all to dos data. After that, second query is
Fatudos who has tag, react. Third query, this
is a little tricky. Festuds who has tag STML and
also a status should Tudo. After that, fourth
query count the number of Tudos whose status
is set to done. Last fifth query is faz
tudos in which task, there is word create, only the word create. It should not include
recreate created, only create and also make sure you ignore
case sensitive here. As you know, you can
use my summary PDF if you don't remember the methods
and solve this exercise. This will boost your confidence, and that's how you will learn on which things you
need to focus more. Also, if you're
confused in one query, then you can move forward with other queries and
solve other queries. So I hope you try to
solve this exercise. Now, let's quickly
see the solution. For first query, for fetching all to dos
from the database, here we have this GPI in which we are directly
returning this todos array. But now we want to fetch
data from the database, and then we will
return that data. So here we write todo dot find. We want all todos, so here we don't
pass any condition. Now, this expression is promise. We can await here and store that data in variable
called todos. Now here we get runtime error. Can you tell me how
can we solve it? We make our function
async function, and at the end, we will send
this todos as response. Save the changes, and
let's call this GTI PI. Here we don't have this GIPI
so we create a new request, and in the URL, we pass local host column 3,000 API slasTudos
and send the request. See, here we get
the list of Tudos. So we solve the first query. Now let's move to second query. In these, we have to patch
toudos who has tag react. I duplicate this line and
comment out first query, now for finding is single string available
in the array or not? We don't need to do
anything special. We directly pass here, object, and add here condition, text, and in string, we write our keyword,
which is react. I in the text array
there is any element, same as this react, then we get that to do. Also, we can do the same
by using in operator. Both works the same.
Save the changes, and let's send this request. See, here we get three to do and all have
react tag in them. Now let's move to third query, which is fast todos, who has tag STML and its
status should be to do. So at the place of
this tag react, we pass STML and we pass another condition
status to to do. So we can use your
dollar operator, but we already know this also works the same as that operator. Save the changes
and take a look. Send the request. See,
here we get to Tudos. Great. Now let's move to
fourth query in which we have to count the number of tudos whose status
is set to done. Let me ask you something. Should we use your
fine method or count the number of tudos No, we have something else. Do you remember yes, we use count documents method. So we replace this fine
method with count documents, and here we pass condition status to D. Save
the changes and take a look. See, here we get five, which means five
task status is done. Now we have last query, which is fetching to dos in which task there is word create. Here, we again add fine method and at the place
of this status condition, we a task and here
we have to use regular expression because
we are working with string. Now, do you remember the
syntax of regular expression? Yes, it is pattern slash. Now to search a specific word, we use Backwards B,
create Bwards B. For making this
case insensitive, we use here, I, save the changes
and take a look. Send the request
and see here we get three records in which we
have create Word in task. That's how we fetch data from
the database in real world. I hope you get the idea
about what we are learning.
77. Exercise 04 - Updating & Removing Tasks: Now let's move to
the fourth exercise. Don't worry, you can solve
it in just two to 3 minutes. In this project,
we also have put method for updating
specific to do by its ID. The first query of this exercise
is updated specific task by its ID and only change
task text to updated to do. As you have to return that
updated to do in the response. Here you can copy
any object ID from the Mongo Di BC pass and pass
it in the query parameter. After that, second query is, you have to update
all the documents whose current status is doing and update
those status with De. Also, here you don't have
to create a new API. You can simply write
only update query and check it when I
show the solution. At last third query is, you have to delete a
specific task by its ID. Here, you can also use any ID from the
Mongo Divi compass. And after the deletion
process is complete, you have to return object with message property to do
deleted successfully. And for delete query, you can
use delete API in our code. These are pretty
simple exercise. You can use Summary PDF
or watching the syntax. Don't worry if you have to
watch the syntax of methods. Even sometimes I
forgot the syntax. So no worries with practice, you will know this syntax. So I hope you solve this exercise or try to
solve this exercise. Give yourself credit for that. Now, let's quickly
see the solution. So first query is we have to
update the specific task by its ID and change only task
text to update it todo. So here we have ID, which we are getting
from request dot PAMs. Now from body, we just need task so we can
remove other fills. We don't need this
code because we write this logic
for local array. But now we have MongoDB methods. We can write here to do dot. Now, which method we use here? Should we use Update
one, find one, and update, fine by ID, and update or update many? Yes, we can use
any of these four. But here, we have to return
the updated document. So two options are
simply removed, which is update one
and Update many because these two will not
return the updated document. Now we have only two options. Which one is more simple? We have to update to do with ID. So we use here, find
by ID and update, and here we pass this ID. Also, we don't need to
convert this ID into integer because Mongo DV
object ID is a string. Now in the second argument, we pass object with
dollar set operator and we pass here object
with task to task, which we get from the
body of the request. Now here is one
thing. If in object, our property name and
value name both are same, which is here task and task. We can simply write it as task. But for giving this code, I'm keeping it this way. Now, do you remember
we have to pass third argument in this wind
by ID and update method? Here, we pass new to true and also run
validators to true. Let's save this so
we can see clearly. Nice. Now we know this
expression is asynchronous task. So we pass here a weight, and we store updated value
in updated to do variable. Also, for a weight, we have to pass here async, and at the bottom, we simply return
this updated to do. Save the changes, and let's
start this application. Good. Open the updated API test. Here we have body ready. Just at the top, we need to pass the real to do object
ID as parameter. So we copy any to do ID from
the Mongo Di become pass, and here at the
place of this ID, we pass our ID and also make sure we addhe
less API prefix. Send this request. Nice,
we get our updated data. Now let's move to second query, which is update
all the documents whose current status is doing and updated
status with done. I duplicate this query and
simply comment out this. Now we have to update
here multiple documents. So at the place of
find by ID and update, we use update many. Here at the place of ID, we have to pass query object
with condition status to doing and we want to
change the data as done. Also, we remove this
third argument. We don't need it,
save the changes, and don't worry about
ID and task body. We don't use them in our
query. Send the request. See, here we get
modified count to one, which means we updated
it successfully. You can also verify it from
the Mongo Di B compass. This is one, right, working
with data. I love it. Now let's move to third query. Is for deleting a
specific task by its ID. So here we are already getting ID from the request dot PRMs. Also, we remove this
past integer method because we don't need it. Now we just need to write query. Remove this previous code, and we use to do dot, which method we use.
Think about it. We need to delete
specific task by its ID. Yes, we can use Dilt one, fine one and delete, or we can also use
fine by ID and delete. Here, I don't specify, we need to return
deleted document or not. So for first performance, we use here DLT one. If we want deleted document, then we use here, fine
by ID and delete. Here we pass query object with underscore ID
property to ID. Now at the beginning, we add await and store it
in variable called result. Now for await, at the top, we add Async and at the bottom, we return this result into this JSON object with
this message property. Save the changes
and take a look, copy any ID from the
database and open the delete request
At W of this ID, we paste our ID, and also we adhere slash API
prefix and send the request. See, we get deleted
count to one, which means we successfully
delete the todo. If we refresh our database, C, we only have seven todos. One to do is deleted
from the database, so that's how we perform
update and delete in Mongo DB. I know this section is little
long than other sections, but as you can see, Mongo Deb
is very important to learn. That's why I designed these exercises specific
for learning Mongo Deb. If you're watching this
course continuously, then take little break
from the screen. Drink some water and
take care of your eyes. See you in the next
section where we learn advanced
topics of Mongo Deb
78. Section 08 - Built in Validators: Welcome to the eighth section of the ultimate Node JS course. In this section, we will learn advanced
concepts of Mongo DB. We start with applying
validators in model schema, returning custom errors
for those validation. After that, we will see how to build relation
between collections, different types of designing
database structure, and how to make patch very fast. These concepts are very useful when we want to build
real world applications. Let's start this section. In the previous section in
the Mongo demo project, we created this schema for our user model,
and in the schema, we define the type of the fill and some other properties
like required. This required is one type of
validator for this schema. Previously, we just define them, but we not really see the practical implementation
of validator for schema. Let's see what happened
when we try to add data without
passing the name fill. We cont out this name property from here and at the bottom, at the place of
delative user function, we call creative user function. Save the changes,
and in the terminal, let's run this application
node index dot js. See, here we get error. Let's scroll to the top. Here we can see we are getting validation error, user
validation filled. Name path name is required. Now you might ask why we are getting this
type of long error. So the reason is we are not
handling error properly. So here we know this new user dot cv method returns a promise, and that's why we use await. But here we are
pretending we always get data in this object and we
forgot to handle error. So do you remember in the
Asynrna JavaScript section, how we handle error
in Asnawt methods? Right, we use try
and catch blog. So here we also add Tr blog
move our acing task into it. Now, if you get error
in storing this user, our code, move to catch blog. Here, we get error
object, and for now, we simply consul dot
log this error object, and it has one property
called message in which we will
get error message. Save the changes, and again,
run this application. See, now we get
our error message. But here I'm also getting
this update information. Let me see what is wrong. Yes, at the bottom, I forgot to comment out
this update user function. Save NGs, and let's run
this application again. See, here we get
our error message. This is good and clear. In real world, we can return this error message as response
with Status Code 400. Now, don't worry about that. Now, what if we want to pass custom error message for
this required field? At the place of this value
true, we pass array. At first place, we pass our value for the
required, which is true. At the second place, we pass
our error message in string. Let's say, please
enter the user name. Save the changes
and take a look. See, here we get our
custom error message. Validators help us to maintain the quality and
accuracy of the data. By using validators,
we check if the values in the document follows the rules which we have
set for each fill. There are two types of
validators in mangos. First one is built
in validators, which are the predefined
validators by mangos. Second, we have
custom validators, which we can define by our own. Required is a part of
built in validators. Let me show you another built
in validators of mangos. For string, we have min length, which will check minimum
length of the string, and we pass the
minimum length here. After that, we have max length which will check maximum
length of the string. Next, we have match which validates string using
a regular expression, and its syntax is slash pattern slash developer use this match validator
for validates email. For now, we don't want that. Now suppose for each user, we have to mention the
role of that user, which can be simple user
or it can be admin, and the string other than
this will not valid. So at that time,
we set the type to string for setting the
two possibilities, we use here num property
and we set it to array, and you guessed it correctly, we will pass our
values in this array. First, can be user and
second, can be admin. We can adhere as many
values as we want, but we have to pass one of these values when
we save new user. We have already seen
this in our exercise. Remember, these are the
validators for string. Also, there are two validators
for the number as well, mean and max, but we don't
want to add them here. That's all built in validators.
They are very helpful. Also, one thing I
want to make clear is the validators is
a mongoose feature. It doesn't do anything
with Mongo DB. If from the Mongo Debi database, we remove the name
field from one record, then Mongo Deb don't care
about in our schema, we set the name field
to required or not. Mongoose give us these
features so we can validate user values before
storing it in database. If it is not valid, mongoose don't store that
data in the database, and if it is valid, Mongo store it in the database. Simple as that. Now
in the next lesson, we will see how to make
custom valid datas.
79. Custom Validators: Now let's see how we can set custom validators in our schema. So here we have hobbies filled, which is the array of string. We want to make sure user
must add more than one hobby. For that, we need to
define custom validators. It is really simple. So after this type property, here we have another
property called validate, and we pass here another object. Now, inside this object, we have validator property, column, and here pass
validator function. Here we want to check. Value passed for this field
has two elements or not, but for that, we
need the value here. We get that value as parameter, and we call it V for value. Now in this function, we can simply return condition. Let me ask you, how
can we check this? V has two elements or not? Yes, we can write here, dot Length should be greater
than one. And that's it. Let's see it is working or not. For that, we comment out this hobbies field and also I change the
email to Harley one. Save the changes, and let's
run this application. See, here we get two errors, one for the name and
another for hobby. Validator failed
for path hobbies with value empty string. Now imagine we return
this error with our API. Almost no one can
understand this error. So we have to give
meaningful error message for changing this error message, after this validator function, we have message property and
we pass here custom message, please enter two
different hobbies. Make sure we had this
message property in this validator object, not in the validator
function because I have done this mistake while
I'm recording this lesson. Save this and let's run this
application one more time. See, here we get our
custom error message. Now let's test more
things for this fill. What if someone pass
only one hobby? Let's check this.
Remove the command, and we remove other two
hobbies from our array. Save the changes, and let's
run this application. Nice, here we are getting the error message,
it is working. Now, what if someone
pass here null value? Save this and let's run our
application one more time. See, here we get cannot read properties of null,
reading length. Now why we are
getting this error? Because in our
custom validators, we try to get the
length of this value. But if user pass null as value, we can't access the
length property. We can adhere another
condition type of V is equal to array. And its length should
be greater than one. If these both
conditions are true, only then our data is validate. We can also modify
message to please enter hobbies array with
two different hobbies. Save the changes, and let's
run this application. See, here we get the custom message for
null value as well. So to quick recap for
adding custom validators, we have valid property
after the type property. And in that validate, we
can add two properties. One is validator, which
has a validator function, and another is custom message. And from this
validator function, we will return the
condition for validation. You might ask if we want
to return the condition, then why we need this function? We can directly add
condition here, but check this one more time. In this condition, we always need the current
value of that fill, and we only get that in
the validator function. That's why we need
this function. That's how we define custom
validators for schema.
80. Async Validators: Sometimes in our project, our validation might involve some
asynchronous operations, which means the operation
might take time. For example, we want to fetch
data from the database, and based on that data, we want to validate our fill. In this case, we can
apply Async validators. Let me show you. For applying the Async validators in
the previous version. Here in the validator object, we have to add one property
called async to true. But in the latest
Mongoose version, this option is by default
on. We can remove this. Now we can simply make this function async and
inside this function, we can perform our
asynchronous task. We don't want that
complexity or this model, so to demonstrate the delay, we use here set timeout method. First parameter is
callback function, and second, we add
3,000 milliseconds. Now before this set
timeout function, let me console dot log
fetching data for validation. Now suppose after
3,000 milliseconds, we are getting data from the database and
based on that data, we can return any condition. Just remember we have to return condition like we
returned previously, which means it should
be true or false. Save the changes and let's
run this application and see, here we get our error. That's how we can apply
Async validators. I personally never use
this in my project because acyc operations
for validation is rare, but this topic may
help some of you, and that's why I
add this lesson. Now in the next lesson, we will see some schematized options.
81. Useful SchemaTypes Options: Now let's see schema types
options in mongoose. We already use them
in our schema, which is lower case to true. This will store our
string in the lower case. Same as this, we have
upper case to true. This will store our entire
string in the upper case. And after that, we have
another useful option, which is stream to true. This will remove unnecessary
blank space from our string. For example, this will convert this string into
this type of string. So our data looks well
organized and clean. It's not necessary,
we have to use all these schema
options in our project. We can use whatever is needed
according to our model. There are no rules for that. At the end, validators
are just for maintaining the quality
and accuracy of the data.
82. Relationship between models: So till now in this course, we have seen models or
collections which are simple and don't have
connection with another model. But in the real world, most of our models are connected
to each other. Let me explain you
with the example. So imagine we are working on social media application
like Twitter. So in that, we have
one collection for users in which we will store
all users data like name, email, password, et cetera. After that, we have
one collection for post in which we will
store all post data. In the post document,
we have content, which is the post
content text date when this post was created. After that, we have user who uploaded this post,
let's say Harley. Now, these two documents
or we can say collections are not connected to each
other. We have to connect them. This is called a
relationship of model. Now you might have question why we need to connect
models with each other? Why we need that relationship? So if we don't use relationship, we might end up with storing the same user data repeatedly
for every post they make. Suppose one user posts
ten different posts. If we don't use relationship, then we have to store
that same user data ten times for each post, and that's only one user. Imagine on our social
media application, there are 100,000 students
or more than that. Think how much data have
to repeat in our database, and this is also
harder to manage. So we have to use relationships between models which
have association. Now there are two approach
to implement relationship. First one is using references, and another one is embedded
data in another document. Let's first see how we can implement relationship
using references. In the post document, we have this user and we pass user name. Now, this user already available in the
user's collection. Instead of storing user name
in the post collection, we can use your user
ID of that user. By user's unique ID, we can run another
query for getting the user data like name and profile picture
from users collection. Now for every post, we have just user ID at
the place of user name. But we can see have to run another query
in this approach, which might cause
performance issue. I'm not telling it will definitely cause
performance issue, but sometimes it can. The another approach
which can solve this issue is embedded
data in another document. At the place of
passing here user ID, we can pass here user object and we remove our
user's collection. This approach, we don't
have to run to queries, so we don't face here performance issues,
but here is one thing. Imagine that user has
20 to 40 details. That will make our post
document much bigger. Also, it will cause data duplication
because for each post, we have to store user object. Now, some developers say these days database
storage is cheap. We can afford the data
duplication and that's true. But what if our user wants to update its
username or email ID? That time, we have to
update multiple post and if somehow two to three documents
don't get that updates, then that will lead us
to data inconsistency. But when we use references, we have to update data
only at one place. To sum up, when we
use references, we get data consistency because our data is stored
at single place, but we might get
performance issue because in that we have
to run multiple queries. Other side, when we embed data in another document,
we get performance. We get our data faster because we don't need to
run multiple queries. We have our data in single
document, but with that, we get data inconsistency because we have same
data at multiple places. This is buy one get one free. We have to decide
for our application. We need data consistency
or we want performance. If we want data consistency, then we use references, and if we want performance, then we embedded data
in another document. Different projects have
different requirement. As a developer,
you have to decide which approach works
best for your project. Nowadays, developers
use hybrid approach, which combines
these two approach, and we will see that
in the next lesson.
83. Hybrid approach for Relationship: We have seen when
we use references, we get data consistency, and when we embed data in another document,
we get performance. Let me show you the
hybrid approach which developers use nowadays. Suppose we have users which
have 20 to 30 properties, we will store that users details in the
user's collection. Now for post, we have
these other fields, same as we see in the
reference approach. But in reference approach, we store here user ID. The place of that, we can
use here, small user object, which contains only
necessary properties which we want to
display with post. We might have user ID, which is the reference to the user's collection
and the user name. We don't store here all
20 to 30 properties, but we will embed
only that much data. We want to display with post. With this approach,
we can quickly fetch post because we don't need
to fetch multiple queries. We have all necessary
data in single object. This approach works best when our collection
needs small details that are frequently accessed. For example, in our
e commerce project, where we have products, orders, cart, et cetera. Each order, we want to use small details of our
products like ID, name, cover image, and price because we want to display that order details
with the products. So we have separate
collection for products data, but still we will embed some small data about
products in each order. So when we want quick
access to small details, but we also want to reference
large or complex data, then we use hybrid approach. Again, I'm telling you you don't have to stick
to one approach. Some application works
best with references. So works best with
embedded data, and some works best in
this hybrid approach. You have to decide according
to your project's needs. Don't worry when we build
other two projects, I will show you how we can
decide relationship approach. I'm talking about relationship
between collections, not about other types
of relationship.
84. Applying Reference Approach: Let's see how we can apply reference approach
for relationship. For practicing this concept, I created this new
file because I don't want you get confused
by that messy core. Let me explain to you what
I added in this file. First, we connect this file with Mongo DB database called
Mongo that's relationship. After that, as we
see in the example, we define here simple user
schema with only three fills, name, email, and age. After that, we define
a new schema for post. In that we have
two fills content, which can be text and then date, which is the current
real time date. We have user which we will
add in just a minute. After that, we create
two models for users and post collection.
We already done this. I don't want to bore you by writing the same code
again and again. Now, after we create
these two collections, I define few functions. One is for create
user in which we can create a new user and store
it in user's collection. Next we have Create Post
function in which we create a new post using
content and user data, who created this post. Last function is for getting
all post from the database. We have already seen this all. Let's create first user
with these values. Run this application using
node, reference dot JS. Good. Now let's try to create a new post without
adding any user data. Here I comment out this create user function and remove comment from Create
Post function. Let's run this application again and see here we
create a new post. We have content and see, we don't get any user data, even if we pass user as
filled in the new user data. We can see mangos will only add those fills which we
defined in the schema. It will not add all other fills. Now in this post model, have to add the
user field and we give user reference
from users collection. Let me show you what I mean. Here, we add user. Now whenever we want
to give reference, we can give that by using
object ID because it is unique. We pass its type to Mongos dot schema dot
types, dot Object ID. Yes, we have to pass
this expression for define object
ID as filled type. After the type property, we have to tell which other model reference we are adding. We pass RF two and here we pass our collection
name in singular word. Same as we pass in this mongoose dot model
method, which is user. Now let's pass our user ID
in the create post function. In the Mongo D BCmepass we get the new database called
Mongo relationship. In that, we get
users collection, copy this user ID, which we just created and paste it at the place of
this users string. In the Create post function, we had this ID at
the place of user. Also let's change this
content to content too. So the changes, and let's
run this application. See here we get new
post with content too, and we also get here user ID, which is the reference
to user model. Adding a reference is very easy. We have to set that fill type to Mongos dot schema dot
Types dot Object ID, and we pass ref
property for adding the reference model
name in singular word. Now, if we try to get post, so comment out this and remove comment from
this G post function. Save the changes
and take a look. See, here we get two post. First one, which we
created without user and second one in which we
define user ID as reference. Now you might ask we are
getting here simple user ID. How can we get the
user data like username or email
from that reference? This we will see in
the next lesson.
85. How to extract data from Reference [Populate]: Let's see how can we extract data using this reference ID? It is really simple. For getting the
data by reference, we use one method
called populate. After this fine method, we add dot populate
and inside this, we have to pass the fill
name which is the reference. Yes, it is user here. Save the changes
and take a look. Run this file again. See, here we again get to post. In the second post, we get
all details of that user ID. Now imagine we just
want to get here name. We don't need other fills. Can we do that? Yes, we can. At the second argument, we pass pills name which we want to get from our
reference collection. In codes, we add name, save this and let's run this
application one more time. See, here we get only
name and underscore ID. Now, instead of only
showing the name, we want all details about user, but we only don't need this
age. How can we do that? Ay place of name,
we want to remove, minus ge Very simple. That's how we extend reference
data using popular method. Now let me tell you something. We had reference in our model, only mangos use that reference. Mongo Deb don't care about that. Even if we pass here
invalid user ID, Mongo DB don't
check that user ID is available in the
user's collection or not. It stores the data without
worrying about it. Let me show you that. We
create here another post. Comment out this
Gad post function and remove comment from
this create post function. Let's change this
content to three, and we simply modify this
user ID last character. In our user's collection, we don't have any user
whose ID is this. Now let's run this application. See, our user three
is created here. Now let's get the list of post, comment out this function and call this Gad Post function. The changes and see
for the third post, we get user to null
because we don't get a reference for
that invalid object ID, which proves MGB
don't verify that user ID is available in the
user's collection or not. Now in the next
lesson, we will see another approach which is
embedded in another document.
86. Applying Embed Approach: To apply embedded approach, I don't remove the previous code because I want to give you
this code for reference. We download the file embedded
dot JS from the below, or you will get that in the resources section
eight folder. Now in the reference approach
here for user filled, we pass type to object ID and reference it
with users collection. But in the embedded approach, we directly add here user
object with all properties. We don't create here user model, delete this line, we only
create one model which is post. Also, we can remove
create user function from the bottom because for each post document
has a user object. Now we can add here object
and we have to pass here properties which we
need for this user object. We can say name to string, email to string,
and age to number. Now the bad thing about this
user object is our user can pass no values for
the name, email, or age. We have to pass the
validations for these fills because we need
user object in each post. So instead of adding
here validation, we already have user schema. So at the place of passing
this simple object, we pass here user schema. This schema, we can add all our validators just as
we have separate collection. Also, for defining the schema separately has one more benefit. We can also use that
user schema for another collection in which we also want to embed user's data. Now let's create a new post
with this embedded user data. So I remove this create
user function call, and we call Create
Post function. Now at the place of this user, we have to pass
user object because that user object will directly get stored
in the post document. For user, we pass object
with properties name to code email to code
etheridgmil.com. Age, let's say 25. Save the changes, and let's
run this embedded s file. So node, embeds, and hit Enter. C, we get new post with user object which have
name, email, and age. And if we also check our
database at the bottom, we get post with user object. So in the reference,
we simply pass object ID and add reference
to other collection. But in the embedded approach, we directly add
whole user data in the single document. Now
I have one question. What if we make this
name field required and then don't pass name
in the user's object? Will our post create or not? This is not a part of lesson
I really wants to see. Let's et this. Here at the
place of name to string, we add object, type to string, and required to true. Now at the bottom, let's remove this name field and let's change this content to trial error. Save the changes
and take a look. Oh, we get validation error, which means new post
is not created, and we can also verify
that in our database. Now imagine we want to store 20 to 30 properties
for users data. We can't add all those fills
in the each post document. A, suppose we want to get
users data like email and password then how can we get those data separately
without fetching all post? This embedded method
is not really practical for social media
types of application. That's why developers use hybrid approach to bring
balance in the application, and we will implement hybrid
approach in the next lesson.
87. Applying Hybrid Approach: Let's implement the
hybrid approach in the same code template. I again add the same code
in the hybrid dot js file. You can also download it from
the below of this lesson. Now in the embedded approach, we added all users property in single collection and we don't even create
user's collection. Now in the hybrid approach, we create users collection in which we can add
all properties. But what we will add in the user's field of the
post collection because this post document needs
users data who created that post so instead of passing
here all users property, we only add few properties
which we required with post. For example, with post, we want to show user name
and the email of that user. We don't want there, so we addre name,
which can be string, and we want to show the
profile picture of that user, which can be string of URL
of the profile picture. So we adhere only necessary
fills for the user. Not the whole document. Now, this will work
perfectly fine. But what if maybe in some case, we want more information
about user with the post. Some developers also
adhere user Rf, which is the user reference. It is the same reference we added in the previous
reference lesson. We said it's type to Mongos dot schema dot
Types dot Object ID. What we add here
yes ref to user. Also, we can change this
field name to user summary. I think this is much better. Now let's change a little bit in the create post function. Here, we add user ID in the
parameter, and in this post, change this user field to
user summary and after that, another property use a
ref to this user ID. Now at the bottom, we call
this create post function. Here we can change the content. This is hybrid way and
pass your object name to codes and profile picture
to profile dot JPG. For user ID, I copy the
ID from the database. And paste the ID here. Save the changes
and take a look. See, here we get new post with user summary and user
rep. We get here both. If you want to quickly show data without calling
multiple query, then we will use
this user summary and if we want
more data of user, only then we use
this user reference. By this way, we can optimize the performance and also
we get data we want. Use what works the best
for your application. At the end, this isn't is yours.
88. Indexes in MongoDB: Now let's talk about
indexes in Mongo DB. Indexes is very interesting and important topic of Mongo DB. Index is used to make
our database query fast, and we will also see
the demo of that. Even Mongo DB says
that with indexes, our database search query can execute Tenex
faster than before, which is really
advanced and cool. Now you might ask how index makes our
search query faster. Let me explain you
with the example. Imagine we have here list of hundred users data
and we want to find a user whose email is
this y123 atgmil.com. Now how real query works, I pick up one document object, check its email is he 123
aterra gmail.com or not. If it is not, then
it will move to another document object and
again check that email. It will continue
this process until all hundred users
document are scanned. As you can see, this
is a little slow. What is the solution here? We will create index for our
collection filled email. By just creating the
index for email filled, our search query for
email will become faster. Let's see this practically. In our users collection, currently, we only
have one user. Let's add some fake user's
data in our collection. So we can practically see our query is working
better or not. For that, in the resources
section eight folder, I added one file, testing index dot js. Simply at this file
in our project. And in this file, I added
almost same code as before. First, Mogadbi collection,
here you have to write your database name which you are using
for this section. Next, we create the
user schema with name, email and password, so we
can focus on our query. Then at the bottom,
we have functions. First one is for insert some random data in
the user's collection. See, by using this simple for loop and this faker package, we can generate
random user's name, email and password for testing. For that, in our project, we have to do NPM install
at the red faker Js faker, at the rate 9.6 0.0. And hit Enter. Good.
Now, let's simply call this function to
generate 100,000 users data because here we are
running this loop 100,000 time. And here we don't want to get
error in the adding email, so I remove the unique
fill to true from our schema just to allow
100,000 users to enter. Also, from your database, remove this users collection, so we get all fresh data. Drop the collection and type here users and drop
the collection. Now in our testing INExis file, we call here Insert
test users function. Save this and let's
simply run this file. Node testing in axis
dot js and hit Enter. It will take little time and see here we get
success message. If we check our database, refresh the collection, S
in the user's collection, we get 100,000 users data. Lovely. Now first, we comment out this inset
this data function. We don't want to run it anymore. Now let's see what
finding user by email, how much time it is taking. At the bottom, we have
function called find user. In this function,
first, I declare time. After that, we run user dot Fine query in which we are passing email
to email parameter. After completing this query, we again declare timer, which is the end time, and here we simply show end
time minus start time. By this, we will get how much time this
fine query is taking. Also, performance
dot now is more precise and reliable for
measuring execution time. Now, let's call this
fine user function. Then from our user's collection, let's copy any email and pass that email in our fine
user function with codes. Save this and in the terminal, we terminate the code and run again our tasting
index dot JSFle. See, here we get time
130 milliseconds. Now let's create index for our email fill and taste how
fast our query will become. For creating index, here
before we create collection, we add userschema dot
index and inside this, we pass object email, which is the fill name, we want to mark as index, and as value, we can pass two
things one and minus one. Now you might ask, what is the meaning of one
and minus one? It is really simple. One is for ascending order and minus one is
for descending order. When we create index, Mongo DB shot that fill data in ascending order
or descending order. This will help Mongo Deb
to find data quickly. Don't worry, I will explain
you that after tasting. Here we pass one for
ascending order and done. That's how we create index for email filled, one line of code. All other code in this
file is for tasting, don't worry about that. We just want to see
by creating index, our query is becoming
fast or not. So here we again run this same find user function and
let's run this file. See, here we get
133 milliseconds. Let me run this
file one more time. See, now it just takes
76 milliseconds. I get this much time
because in my system, currently many large
softwares are running. Previously, when I taste
this without any software, it is taking 60
milliseconds before adding the index and
after adding the index, it only takes four milliseconds. Is almost 15 ngs faster
than without index query. This is really interesting. Also, we are getting query time in milliseconds
because currently our application is running locally and also our
database is local. If we deploy both
things on the Internet, then the 130 milliseconds can become 1.3 seconds with index, we run that same
query in 0.7 seconds. Imagine how fast our website becomes that's why
indexing is very useful. Let me show you index
which we created. Open Mongo Divi compass, we go to the indexes Dab. See, here we have two indexes. First one is for underscore ID, and second one is for email. So in the Mongo Di B collection, Mongo DB always create index
for underscore ID filed, and that is the reason when
we search data by its ID, then we get those
results quickly. Now here at the bottom, we have another function
called width index. In this function,
first we create index manually using this line, and then we write fine query. At the end of the query, I use this explained method. The explain method helps you to analyze how Mongo
DB execute a query. It shows whether
Mongo DB is using an index or performing
a full collection scan. Inside this explain, we pass the string execution stats and simply unst log these users. Let's see what we
get inside this. Here at the place of
fine user function, we add B index function and let me simply move
this at the bottom. The changes and take a look. Let's run this
application using node, tasting indexes dot js. See here we get all details about the query like
execution stats, commands, server
info, and much more. Here we just need to
see execution stats. Here we can see
and return to one, which means one data
is returned from the squery if we see
total docs examined, which is also one, if we don't create here index
for email field, then these total docs examined will be much
higher than this. Let me show that also. Here we have without
index function. First in this function, I remove all index from
our users collection, and then we run the same
query with explained method. Don't worry in our
real node application, we will only use userschema
dot index method when we define the schema
of the collection. I added these two
methods to just remove and add index
using function. Now let's call this without index function and pass
here the same email. Now again, run this file, node testing indexes dot js. See here in the
execution states, we get the number return to one and total docs
examined to 100,000. That's why it is taking
more time than with index. Previously, if we
check with index, we get total docs
examined to one, and that's why by
creating index, our query becomes fast. Now you might ask how by indexes our query is
becoming this much faster? What is happening here? We will see that in
the next lesson.
89. How indexes works in MongoDB: So previously we have problem. Our database query is scanning all hundred
thousand documents one by one for finding specific email which is
making our query slow. And we solve that
problem by simply apply index for email filled. But the main question is how? What is happening behind the
scene when we apply index? Let me explain you with
interesting example. Imagine you are in a
library with 100,000 books. Now, you need to find
book titled Harry Potter. Now if the books are not
arranged in any order, you have to start
from the first book, checking each title
one by one and continue that process until
you found Harry Potter book. This is like scanning
all 100,000 documents in Mongo Di B without an index which we can
consider as slow. Now, let's say library
creates an index, something like the list for storing the books information. That list, or we can
say in that index, we have book titles, which we sorted alphabetically, and we also add a pointer to the exact self or location in
which that book is placed. Now when you search
for Harry Potter, you go to the index, find Harry Potter in seconds because it is
sorted alphabetically, and then with the book title, we add the pointer. You use that pointer to
directly go at the book. Instead of scanning
100,000 books, you find your book in
just a milliseconds. In Mongo DB, also,
when we create index, Mongo Di B create a
tree like structure, which we called as B tree or balanced tree and
short our data in ascending or descending
order depending upon we pass value
as one or minus one. With that fill, it's stored pointer which does the
original document location. By creating index, we reduce the number of document
scan and because of that, our fine query becomes
f for that fill. Now you might ask the
shorted data, also, we have to find our data, how it is taking less time than checking all
documents one by one. This is the really
great question. Let me explain you
that. The thing is, suppose if you have unsorted array and a
shorted array in both, you want to find element 60. Then in the both array,
we don't need to scan each number how can that
sorted array takes less time? As we see previously, when we create index, Mongo B creates Battery
or balance tree structure with pointer attached
to each data. But in this Battery, Mongo Dib also has
to search data, and for searching the data, Mongoi use another
search technique, which we called
as binary search. So when we don't create index, Mongo Db use linear
search technique, which means Mongo Db checks data one by one
for all documents. And if we create index, then Mongo Dib use
binary search technique. Let me explain to
you quickly how binary search works so you
get better understanding. Imagine here we have
shorted array of numbers ten, 20, 30, 200. In this array, we
want to find 60. Now there are two ways. First, we check ten
is 60, no, 20 no. 30, no. 40, no. 50, no. 60, yes. So here we search all
items one by one, which is the example
of linear search. Now let me explain
what is binary search. In binary search, we
make jerms on half. So here at the place
of checking ten, 20, 30, we directly jum to the
half of the list, which is 50. Here we check, 50 is greater
than 60 or less than 60. Yes, 50 is less than 60, so we move to the right
side of the list. Now our list is 50 to hundred. In this, we again
jump on the half. Tell me on which
item we will jump. Right, we jump on 80. In that, we again check, 60 is greater than 80 or
less than. It is less than. So if our number is less than, then we move to the left side. Now 50-70, we again
jump on the half, which is 60, and we
get the element. So at the place of getting 60 in six atoms
in linear search, here we are getting 60 in three to four atoms.
This is really fast. What do you say? And
this is just a ten data. Imagine we have 1 million data, then how much time we
can save by using index. So that's why index
makes our searching more efficient and it takes less
time than linear search. Also note that binary search
works only on sorted array, and that's why when
we create index, we pass whether it
should ascending array or descending array
one or minus one. Now you properly
understand what is index and why it
works really fast. Now, is that mean we
need to create index for every field in our
database? The answer is no. We have to create index for only those fills for
which we want to search. Suppose we have users data and we want to search
users by name. In that, we can create
index with filled name. So Mongotbcmmand,
only use index for large collection in which we are going to store thousands
or millions of data. You might ask why
we can't create index for small collection
or for every field. The reason is when we
create index, as we know, Mongotb creates Btree structure, but that Bitr is also
stored in our database, and that requires space. Let me show you.
Open Mongoib compass and go to our database. See, here we get
users collection. Here is the storage
size of our collection, which is 6.65 B at
the right side, C, we have total indexes
size to 3.86 B, which means our data
is only three MB and other 3.86 B is just
acquired by indexes. So more than 50% is
acquired by indexes, which is really huge, and it is just two indexes. If we create index
for all fields, then how much it will
take our storage. Also, when we have
small collections, then our linear search
will also run fast. Difference is
ignorable and that's why we don't create index
for small collections. When we need to
apply index, first, when we have large collections, indexing helps use datasets
like millions of records. Without an index, Mongo Di B checks each document one by one. Second, when searching
is frequent. If we often search for
a fill like email, user name, order ID, then we can index it. For example, in
user logging system in which we search
user by email. This boosts read
speed significantly. Next, third, when
shorting is common. If we often use Short
Method by Price, then we can index
that filled price. For example, in the
ecommerce product listing. The time, we want to short
products by price or date. Index avoid full
collection scan. Number four, when we use fills in find update,
and delete method. Index speeds up
these operations. Now let's also see when we
don't need to apply index. Number one, when our
collection is small. If we have only a few
hundred documents, then searching without an
index is already fast. Number two, our data is
constantly changing. Indexes slows down, inserts, updates and deletes method, because Mongo Di B must have to update the index
tree every time. For example, a log system. In that, new logs
added every second, and this will slow down if
we create index for logging. Number three, when we already
have too many indexes, each index uses storage, too many indexes is equals
to vested disk space. So the solution is only index
the most important fills. Number four, when we are
quering many different fills, we should avoid index. So if our searches varies
a lot on fills like name, email, age, city, indexing, each one might not help. So the solution is only
index frequently used fills. So to quick recap, we create index in Mongodi B
when we have big collection. We want to make our
query really fast. Don't worry, we will apply these indexes in our
project when we need it. Now, from the next section, we will start building
our Project two, which is Ecommerce
application backend.
90. Section 09 - Project 02 and Planning: It is time to create
our second project. In this project, we are going to create Ecommerce
application Ben. We will not create front end because that is not
scope of this course. For that, I have
separate reac JS course. Now when I start a new project, I like to plan the
project roughly. It will give me clarity, and also I recommend
you to do the same. So first of all, we have to visualize the basic front end, not perfect design,
just visualize. For example, here we are
creating ecommerce application. On that website, user can create account or
login on the website. After that, they
can see the list of products and when they
click on that product, they can see full details
about that product, more images and description. It doesn't matter user
is logged in or not. All users can see products. After that, user can see
their order history. Also, they can add
product to card, remove product from card, and place the order by payment. So these are the features
we need for our project. Don't worry if you don't know all features
about your project, as I told you, this
is just of plan. In future, we can add or remove features
from our application. So first, we will
start with creating the user model create API
for user authentication. After that, we will
move to products, then card, and then orders. You will love this
and also you will get the confidence to create
no projects by your own. Let's start this
amazing project.
91. Creating a new server: Now in our projects folder, let's create a new folder for our second e commerce
project called cart Wish Backend and simply open this
project in the VS coe. Good. Now when we create a new project,
what should we do? Right. We will initialize
the project using NPM int Y. This will create a
package dots and file. Now here, we create a new
file called index dot js. Let's create Express server
for this application. Const Express is equal to
required Express package. After that, we're
creating Express app, Const app is equal to, and we call here this
Express function. Now let's listen this server, so app dot LISN here, first we pass port, but at the place of
passing hard coded value, we create here variable
const port is equal to process dot nv dot
Port or 3,000. So if we have port
in the environment, then it will use that
else we have 3,000, and we simply pass here
port at the first place. And what we will pass at
the second parameter, we pass here callback function
and simply consol dot log. Server is listening on port, dollar Ci packets, port. Now, let's run this
application and check we have done
this correctly or not. Why I think we might
get here error? Let's see, error in
developers life is constant. Don't get afraid by that. Open up terminal and run
here nodebn index dot js. See, here we get error. Oh, we forgot to install
Express package. So NPM install Express. And if you want to use
the same version as mine, then write at the rate
5.1 0.0 and hit Enter. Good. Now let's try again. See, here we get
server listening. Now in the next lesson,
we will connect this application
with the database.
92. Connecting to Database: Let's connect this application with a database because we need to store information about
users, products, et cetera. First of all, in our project, we have to install mangos. We will not repeat
the same mistake. Open up terminal and create
a new terminal from here. By this way, we don't need
to stop our application. So NPM install Mongoose
at the red 8.13 0.2. Good. Let's minimize this. Now in our index dot s file, we import const mangos is
equal to require mangos. And after this app, we write Mongos dot connect. At the first argument, we pass our connection string. If you don't remember the connection string of Mongo Di B, you can get it from
the Mongo DB Compass. Here in the side bar, we have this local
host connection, and at the right side of that, we have three dots option. Here, we get Copy
Connection String. And paste it at the
first position. Now, at the end of this
connection string, we add our database name, which is Card fish. Now, as we know, this
expression returns a promise. So we use here dot TN method and inside it simply
console dot log. MongoDB connected successfully. Now, after then method, we also add dot cache
method for handling errors. Here, we get error
Object, error function, and gonsol dot log, MongoDB connection, failed,
and print this error object. Now let's test this
implementation, save the changes, and in the terminal, see, here we get Mongo DB
connected successfully. In the next lesson, we will
design our user model.
93. Exercise - Creating User Model: Let me give you little exercise because we have
already did this. So you have to create user
model for this project. You have to decide which users fill we want for this project. Don't worry if you add
more fills or less fills, but the important part is, you think about fills. So remember, which are the
features related to users, and according to that,
decide the user's fill. Define a schema for those fills and then create model
with that schema. After completing this exercise, you can see the solution. Now let's see the solution. First of all, we
will create here new folder called models. And inside this folder, we will create a new file
called users dot js. Good. Here, first of all, we import Const Mongos is
equal to required mangos. Because without mangos, how can we create schema or model? Cost user schema is equal
to new mangos dot schema. In the Cali brackets, we will pass our schema
in key value pair. First of all, name to object, we set its type to string, required to drew, and we
set mean length to three. After that, we can set Emil field and we set
its type to string, also required to
true we also need unique to true because all users should have
only one unique email. And for the best practice, we will also add
lowercase to true. After that, which
field we can add, yes, we can add password, type to string, and also we
have to required to true. After that, for e commerce user, we need delivery address
to deliver the product. So type to string
required to true, and also we set mean
length to five. After that, we can specify
the role for each user, whether he's user or admin. So we set it's type to string. We can restrict the role field only two options by using
Enum property to array. Here we pass our
values user or admin. Also, we can set its
default value to user. If we want to change
the role to admin, then we have to
access the database. By default, all users
role set to user only. I think that's pretty much
all fields for user model. If in future, we need
more functionality, then we can also
modify the schema. Don't worry about that. Also,
if during the exercise, you take different
names for these fills, then you can change
them, same as mine. Otherwise, it will might
give you bug in the future. Now we have user schema ready, so we can create model
using this schema. So Const user is equal
to Mongos dot model, and what we pass at
the first position. Right, we pass the singular name of the model which is user. At the second argument, we pass user Schema. Also, let's export
this user model. We will need it in
the user's route. Module dot exports
is equal to user. Perfect. Now in the next lesson, we will create first route of this project for
creating new user.
94. Creating the new user: Now let's create an API
for creating new user. Here, we create a new
folder called routes. In this folder, we will create all our routes in
separate files. Create a new file users dot js. Now, do you remember how can we create API in separate file? If we have to create API in
our main index dot js file, then we can use
this app variable. But how can we create
API in separate file? Right, we will create
router for that. So first, we input Express is
equal to required Express. After that, this express has router method
which we can call. This will give us router. Store it invariable router. Good. Now let's create API
for creating a new user. Which method we can use here, get or put, we will
use post method. So Router got post. Here we write our endpoint, which is forward slash, and after this endpoint, we will add callback function, which will run when someone
call API with this endpoint. This function has two
parameters request and response, and
error function. Now, first of all,
in this function, we want values which user
pass in the request body. So const user data is
equal to request dot body. Let's Const dot log
this user data. And after that, simply
responsdtsN or we can use here response dot JSON because
we are sending JSN data, which is this user data. Now let's check our API is
defined correctly or not. I like to take small steps because it will not
create confusion. This is working, then we
can move to main logic. Currently, we define API here, but we have to add this route
in our index dot js file. Otherwise, it will not work. We know this right. So let's
export this router using module dot exports
is equal to Router. Save this file and head
over to index dot js file. Here, after the
Mongo DV connection, we can add app dot U and
at the first position, we will add our API prefix, slash API slash user, and at the second position, we have to pass Router, which we export
from users route. So at the top, we
add cost user routes is equal to require
here periods. Here we go to the routes folder and inside
it users route. Now we can simply pass this user's route here.
We already done this. Remember, and don't worry if you don't
remember the syntax, it is completely fine. Many times, I also
forgot the syntax. For now, focus on
building the application. Save the changes, and
let's test this user API. So on tender client here we can see our
previous activity, but I don't want to mix things
with the previous project, we go to the collections
and from the right side, we have option new collection
and give it a name cartwis. We will add all our API
taste in this collection. The right side, we
have more options. Click on Create New Folder
and give it name users. So in this users folder, we will save all our API
taste related to user. You can see how
systematic this looks, and if in the future, we visit this project, we don't get confused. So we create a new request
and give it a name. Create a new user. Select the method to post, and we enter our
APIURL which is DTP, Column Abo forward slash
Local host, Column 3,000, or whatever you use
as port APIs user. Don't forget to add this prefix. To send data in the
body of the request, we select your body and we will pass data here
in the JSON format. Object, first field name, make sure we write
in double codes. Value to code plas Next, we have email to code
at the red gmail.com. After that, which fill we add, let me check in
the user's schema. Yeah, we have password
and delivery status and both are required. Password, and in string,
one, two, three, 45678 and delivery address
to let's say XYZ, at XYZ. Make sure we use the same filled name as
we used in the schema. Otherwise, we get error. Now, let's send this request. What do you think?
Will it work or not? Let's see. Send the request. See, here we get 200 status, which means okay,
but we don't get our data in the response
of this request. Let's check Console A. See here also, we get undefined. Can you guess why
this happen again? Remember, when we want to get data from
request of the body, we need to use one
middleware for converting the
data into JSNFmat. Now you remember, in the index dot js file,
before this route, we add app dot ug for
adding middleware and we simply pass here
express dot Jason. By this, we will get data from the request of the body.
See if the changes. Let's send the request again. See, now we are getting
the data in the response, our current implementation
is working. Now let's save this data
into our user's collection. But before saving the data
in the user's collection, we need to check
whether this user is already exist in
our database or not. Don't get confused, see this
and your allots will clear. Here, we need user model
for running the query. Cost user is equal to require here we have
to go one folder up, period, period, slash models
and we go to users model. Now, after this user data, we can do something like
this user dot Fine. Here we pass object for filter. Here we will find user by its
unique email and for value, we pass here userdata dot email. So we are finding user, which email is same as
this userdata dot email. Also, here is one thing. If we don't have any user whose email is same as
this userdata dot email, then this find method
will return empty array. So instead of this find method, we can use here Find one method. If user don't exist, then this find one method
will return undefined, and that will help us in
writing the condition. So we will use here
Find one method. As we know, this expression
will return promise, so we have to await here, and for using await, we have to make this
Cavey function an. Good. Now, let's store the result in the
variable called user. Now you can simply
put here condition. If user is available, then we will return
error in the response. So response Dodge status to 400 for bad request and also in the response
want to send data, so dot Json, and
here we pass object with message user
already exists. Now here is one thing. If
user is already available, we don't want to run
this bottom logic. To do that, we have to pass here return before this
response dot Status. Otherwise, it will
run the code forward. What if user is not available
in the user's collection? Right, we will store the
user data in the collection. So const new user is
equal to new user, and here in the object, we pass our data. So name to userdata dot name and email to userdata dot email. Wait, here we have to write
user data multiple times. Instead of that, we can
destructure our object here. So at the place of user data, we add Object and pass fills name which we get
from request dot body. This is called as
Object destructuring. So we have name, email, password, and last
delivery address. Make sure we write here the same filled name which we pass in the request dot body. Now at the place of
userdata dot name, we can write only name. Also if the property name and a value variable name is same,
then we can remove this. But for your understanding, I'm not removing it. Email to email password to password and delivery
address to delivery address. Also, we have to change here in the fine method, email to email. Now we have new user object, so we can save it
in our database, new user dot c, and this is the Async operation. That's why we adhere await. Now, this will return the
stored user from the database. If we don't want to return
the stored user data, then we can also use
this new user data. Don't worry, we get
ID in both objects. So at the bottom, we adhere response dot status Tell me status code we will
use for new data. Right, we use 201 dot Json, and here we pass
new user object. Perfect. Now let's test
this implementation. Save the changes, and let's
send the post request. See, here we get new
user with its unique ID. And if we check our database, refresh the database,
here we get Cardwish. Inside this, we get users, and here we get our first user. What if we send the same
request with same user info? See, here we get user
already exist with error, 400 bad request. Perfect.
95. Hashing the password for security: Now currently in our usage data, we are storing password
in simple string. But what if someone gets
access to our database, then anyone can see
user's actual passwords. To solve this issue, we can has the password in
some random string, which makes our
password unreadable. For hasing the password, we use one very popular
library called BcryPt. NPM install BCRP. Don't worry about it.
It is really simple. We will understand password
hasing in separate file, and at the end, we will add
it in our user's route. Here, we create a new file
called pass dots. Good. Now, first of all,
we import const, Bcrypt is equal
to require Bcrpt. Now Bcrptespecially strong
because it had SLD. You might ask what is sold? SALT is an extra piece of random data added to a
password before it's has. Let me explain you
in simple words. Let's say we have two users, user A and user B and
both choose password. Let's say one, two,
three, four, five. Now suppose our BRp package has this password which
might look like this. If we just has one, two, three, four, five, without any salt, user A
and user B has password. Look the same in the database. This is a problem because it's say these both users
have the same password. If hackers see identical
heed password, then they might guess that both user have the
same plain password. So we need SLT to
solve this issue. SALT is nothing,
just random data, added to a password
before hesing. So before userAPassword,
we add some random data, so it's password look like this. For user B password, we add some random data or sold. So it's password look like this. It's not necessary, Bcrypt
keep the same as password. I just show you
for understanding. Let me show you
that practically. Here we can use crypt dot and this function
except two arguments. First one is the
original password, which is the password
entered by user. For example, here we
pass one, two, three, four, five, and the second
argument is sold round. This control the cost of hasing. High value means more
security but slower hesing. Usually, a value of ten is considered secure
and reasonably fast. Now at the third parameter, see, here we have to pass
callback function because this is
asynchronous operation. But instead of using callback, we can use here awight
at the beginning. For using a weight, we have to write
this expression with a sync function called a pass. And we simply move this
line into this function. Now store that invariable called st pass and simply console
dot log this st pass. Now, let's call this same
function two times so we can see password at similar
or SALT is working. Open up terminal and simply
run node spass dot js. See, here we get two
different st password. Even if we pass the same
password one, two, three, four, five, for a, this is
the power of SLT. Let's implement this code
in our user's route. Cut this line from here and paste it before
our new user variable. Now let's do some
little changes here. First, we change this hard coded password
one, two, three, 45 with our password, which we get from
the request body. This will generate
password for that user. At the place of saving
the original password, we store this st password. Also, we have to import
Bcrt at the top. Const Bcrypt is equal
to require BCR. Sears the changes
and take a look. In our database, we have
used data without hasing. So we can delete this record
from here, delete it. Now let's run our application
using normon index dot js. Good. Now again, send the post request
with the same data. See, now we successfully has the password and stored
it in our database. Even if someone entered
in our database, they can't see the
password as it is.
96. User input validation using Joi: Now currently, our user
route is working fine. Weg user is already
registered or not, and also we are
using the password, but there is a rule for
the Bend developers. We as a Bend developer, never trust the data
sent by client. We have to always
validate that data. Suppose at the place of name, client pass, user name property. Also, sometimes they forgot to send email or even password. At that time, we can't store half information
in our database. Now you might say we already set up validation in our schema. Should we have to add
one more validation, yes it's often a
good idea to add multiple layers of validation beyond what's set
up in the schema. We will perform backend data
validation in this lesson. This is another layer
of the validation. If Acker somehow bypass
this validation layer, then we already have
validation in schema which will prevent invalid data from
entering in our database. Here we have two options. Can perform manually or we can use very popular
package called Joy. Also, we have other validators
like Express validator, Yup and validator dot js. You can use whatever
you want to. I love Joy because it's
versatile and easy to use and A it integrates well
with many nodejs frameworks, especially express dot js. Let me show you that. Open
up terminal and write NPM, install Joy ethert
17.13 0.3. Good. Now to use joy is very simple. First of all, we will
import joy in this file. So const joy with J is
equal to require joy. You already know
why we write here J because this joy
package returns a class. Now with joy, we have to define schema for request body data. In this joy schema,
and do many things, same as we've done
in the user schema. We define here new
variable called user schema or create
user schema is equal to, and here we pass joy dot
Object and inside this, we pass our schema object. In this object, we pass our
fields with Joy schema. What is the type of
properties? Is it required? What is the mean value, max value or mean string length ortring length, and
much, much more? First of all, we need
name to joy here, we can specify the type
of field which is string. Make sure we call
this function here. Now we can also add here
mean to three characters, and also we want this
filled to required. There are lots of methods joy. You can see all methods by
using its documentation. Next, we have here email
fill joy dot string. Now for email joy, we have email method, this also validate email and also pin this fill as required. Next, we have password to joy dot string dot Min
26 letter of password, and we also need this required. Last will, we want to validate
his delivery address to joy dot dot Min to
Pi and required. Good. Here we have this
Joy schema now we want to apply this schema on the data which we are getting in
this request dot body. That's why we write the same fields name as we
pass in the request dot body. To validate the data using Joy, we have to use this
creative users schema dot validate method. Inside this, we have to
pass which data we want to validate and what we want to
validate, request dot body. Now, this will return
object we store in the variable called
Joe validation. Let me show you what we get in this Joy
validation variable. I comment out this bottom code and just validate
this request dot body here and simply return response
dot json, Joy validation. Say this in Js and take a look. Send the post request. See here our data is validate. That's why we get
the value filled. If I remove this password from request dot body
and send the Again. See, here we get error object and in that we have
these details, which is array, and in
that we get error message. Password is required. By using this error object, we can return error
in our response. Don't get confused.
Let me show you that. Remove this response
dot JSN method. We don't need it and we simply
write here I condition and check if joy validation
error is available, then we simply return
error in the response. Return Response dot status, 400 for bad request dot Json, and here we return Joy
validation dot error Object. Now as a good practice, developers don't pass full
error object from here. They like to send
only error message. We add dot details, which is the array and we access its first element
by square brackets, zero index dot MessagE
let's test this, send the request without
password filled. See, here we get
password is required. If we pass password
filled again, so undo, but in name, we pass only two characters. And send the request. See, here we get
nice error message. Name length must be at least
three characters long. In Mongo schema validation, we don't get these type
of error messages, and that's why
developers like joy. Now here, I have one question. If we don't pass here two
fails, what we will get. So remove these first two
fills. Send the request. See, here we get only one error. Joey run validation
line by line. If first line don't
get validate, then it will immediately
return error. That's why we return always details first
element dot message. That's how we validate input data which we get
from the request body. You can remove comment
from other code. God. To quick recap, there are three layers of
validation in real world, client side validation, which front end developers
perform on browser. Users can see that
on front end form. Next, we have Bacon
side validation, which we done just
now using joy. In this, we are validating data, which we are getting from
front end in request dot body. Next, we have Mongo schema, which is the final validation. Will prevent invalid data
entering in the database. If someone pass client
side validation, then we have wagon
side validation, and if someone pass that also, then Mangus schema
is always there. Adding these layers provides a robust approach to
preventing invalid data for entering our database and it ensure a secure and
reliable application. Now you can see we are
working as a professional. Now in the next lesson, we will see how
user authentication works in the real world.
97. How authentication works: Now let's talk about user
authentication in node js. User authentication plays very important role
in any application. I nodejs, we do
authentication using JWT, or we can say it JSN webTken. Now you might ask, what is
this JWT or JSN webTken? Don't worry about that.
It is really simple. JSON web token is a long token string,
which looks like this. By using this token, which we will generate
in the back end, by that, we can
authenticate user. Let me explain you
with example how authentication works before
JWT and how it works now. Here is a Harley he log in with his account information,
email and password. Now our server first
check the information, and if it is true, then server returns
its user ID as response and store that
in the session or ooki. Now, whenever he sends requests for some
secured information, let's say, all his
bank information. So server first ask
for the user ID, and if he has user ID, and then server sends
the secure information. But here is a one big problem, the SSN or Cookie in which
we store our user ID, it can be easily
modified in the browser. Let's say I change this user
ID to someone else user ID, then we get the information
about that user. This approach is not secured. Now to solve this issue, we introduce JSN web token. Now Harley again login with
his email and password. Now our server first
check the information, and if it is true, then server returns the
long encrypted unique token as a response and store
that in the local storage. Now, the great thing about
this token is it is made with usage details and one secret key which only we will
define on the server. No one knows the secret key
except you and your team. So whenever Harley
sends request for some secured information,
then in the server, we first ask for the JWT token and verify it
using our secret key. If it is verified, then and then from the server, we will send that
secured information. And if we change anything
in user information, then our token will also change. As a Bend developer, our job to send JSN web
token when users sign up or login and also verify
the token when we need. Storing the token in
the local storage and everything else is a job
of front end developer. Set a sum, when users
successfully login or register, we send JSON web token, which simply works
as security card. When user request for data which is only accessible
by log in user, then server first check
the security card, which is our JSON web token and validate it with
the JWT secret key. That token verified,
only then server returns the data to that
user. Simple as that. Now in the next lesson,
we will generate JCN web token and send
it in response.
98. Generate the JWT token for user: So we understand JN web token. It is like a security card. Now let's generate a token
when user successfully registered and send a
token in the response. So for generating JSN web token, we need package
of JSN web token, open up terminal and write NPM, install JSN web token. And if you want to install
the same verson as mine, then write at the rate
9.0 0.2, and hit Enter. Good. Now in our user's
file at the top, we import that
package using require and simply store it in
variable called JWT. Now when we want to
create this token, at the very beginning
or at last, we want to create token when our user data stored
successfully. After this same method, we write JWT dot Sign
In the sign method, we have to pass two arguments. At first, we have to pass data which we want to
send within the token. For now, we want to just
send user ID with our token. Object and first property, underscore ID to
nwuser underscore ID. Make sure you write
here, underscore ID. I have done this mistake before. Also, I want to send
the name property of this user name tonuser dot N.
Now at the second argument, we have to pass string, which is our security key. For now, we pass
GWT, security key. We can pass any string. There are no rules for the key. Also, currently we are
passing this key directly. But in the next lesson,
we will add it in the environment variable.
Don't worry about that. Now, this will generate
JSN web token, so we store it in
variable called token. Simply at the place of
sending new user data, we pass only token. Let me show you how
this token looks. Save the changes and
from the Thunder client, we send new user data, write the full name, and change the email to code one@gmail.com. Make sure you addhe comma
and send the request. See, here we get
this long token, which we just generated. Now, let me show you
more about this token. So copy this JWT token. Make sure you don't copy
these double codes. Open a tab in your browser
and search jwt dot IO. And this is the official
documentation of JWT. Here in the libraries, you can see JWT implementation for different
different libraries. Now back to main page and scroll down to
Debugger section. And here we can
decode our token. Now, let's understand
what token contains. So past our token here. Now all GWT tokens
divided into three parts. First part is about header, which is in the red color. Second part is about payload, which is in the purple and last and most important
part is signature, which is in the blue color. Now, this header contains the
algorithm and token type, which is very common.
Focus on that. Next, this payload contains the data which we want
to pass with the token. In this case, we pass
user ID and use a name, which is this object
in this sign method. The reason we pass here
this data is we can display that data on our front end
without calling separate API. After that, we have
one more property It, which stands for issued at and its value is time when
our token is generated. By this, we can see
how old our token is. The last part, which is
in the blue is signature, and it is generated
based on our header, this payload data, and the secret key which is only
available on our server. So this will prevent users from getting their own token and then modify it with ID to
pretend to be someone else. Because if you modify anything
in this payload or header, then the signature
will regenerate. So there are no chance for users to do something unethical. By only this secret key
our token will validate. Otherwise, it will
give us error. That's why JWT is so popular. To quickly sum up, when users successfully login or register, we get JCN web token which
simply works as security card. When user request for data which is only accessible
by logN users, then server first check
the security card, which is our JCN web token and validate it with
JWT secret key. The two verified,
only then server returns the data to that
user, simple as set.
99. Setting Expiry of Token: Many developers as
a good practice, set an expiry time for
this JSON web token, like 2 hours or 24 hours. After that time, this
token will not valid. If user want new token, then they have to login again. So for that, we can pass third argument in
this JWT Sign method, Object, and in that we have
property called expires in. We can pass values
in milliseconds, if we want to set 2 hours, then write two for 2 hours into 60 for minutes because 1 hour
has 60 minutes into again, 60 because 1 minute
has 60 seconds and into 1,000 for converting
seconds into milliseconds. You can see this is a
little bit confusing. So we can also pass
here in string to edge, which is 2 hours. Or we can write here
one day for one day. Ever you like. A numeric value is interpreted as a
milliseconds count. If you use a string, be sure you provide the time units like
days, hours, et cetera. Otherwise, milliseconds
unit is used by default. So if we only pass
here 120 in string, then it equals to
120 milliseconds. I like to set 2 hours, so two edge, this expiry time is really depends on
your application. You might notice in the
banking applications, your login get expired
in very last time, like five to 10 minutes. After that, you have
to login again. The reason they use
expiry Time for token is because they want
to secure their application. On the other side, many
social media websites set expiry time to
30 days or 60 days. Once you login, they
don't expire your token because they want us to use
more of their social media. Expiry time depends
on your project. Choose best for
your application. Just make sure you don't
get annoyed by ExpiryT.
100. Secure the Security key in Enviroment: Currently, we pass our JWT
secret key directly here. But in the real world,
this is not secure because when we deploy our
application on Internet, we also upload our code on some cloud like
Github or Gitlab. If we write our secret
key in this code, everyone can see our secret key, and this is not secure. So at the place of
defining secret key here, we can edit in the dot ENV file. In this project, we didn't
create dot ENV file. Create a new file
here called dot ENV. In this file, we
simply define variable called JWT underscore
key is equal to, here we will write our key, which is JWT security key. Also, many developers
like to adhere random key name so no one can
predict the security key. You can use any security key, just to keep the backup
of that security key. Now to access this JWT underscore key variable
in our project, we need to configure Dot NV and which package
we use for that, we need dotnV package, open up terminal and simply
NPM install Dot ENV. Good. Now in the index dot
js file at the very top, we add require dot NV and we
call here dot config method. Save this file, and in the user's route here at the place of this
hardcod string, we add process dot nw
dot our variable name, which is JWT underscore Key. And done. Now, if we need
to use this key again, we don't have to write
the original key. We can write process dot E
and w dot JWT underscore key. Now in the next lesson, we
will create login route in which we will authenticate,
email and password.
101. Exercise Create Login route: Now it is time for
little exercise. You have to create a
new API for login, so it should be a
post request with endpoint API slash login. And in the body of this request, user can pass two properties, email and its password. So first of all, in this API, you have to find
user by its email. If you don't found user, then you have to send error in response with message
invalid credentials, and if user is available, then you have to
compare password. Just these two steps. Don't worry about
comparing the password, leave the exercise
from that point. Just define new API and
find the user with email. After completing the exercise, you can watch the solution. So I hope you solve this exercise or at least
you try to solve that. Give yourself credit for that. Now let's see the solution. First of all, at the bottom, we define a new API
using router dot post. Here at the first position, we pass endpoint slash login. At the second position, we pass callback function
with two parameters, request and response, and
adhere arrow function. Now let me show you my trig for writing code without confusion. I like to write steps in comman. For example, here, first step is find user from
database by email. If we found user, then after that, compare
encrypted password. If password is matched, then we create JSON web token
and send it in response. By this way, we get the clear
path for writing the code. First step, we have to
find user by email. For that, we have to get data
from the request dot body. Cost data is equal
to request dot body. Or we can also destructure
our object here. So at the place of data, we add object and we access our properties,
email and password. Now using this
email, we find user. So Const user is equal to here we have to
await user dot Fine one here we pass comparison object and we compare email filled
with our email variable, same as we done previously. Here, we get runtime
error for using a weight. We have to make our
function async. Good. Now we can
check condition. If user is not defined, then we return here error. Response dot status 401
for invalid credentials, and also we send GSN Object with message
property invalid credentials. Now, what if user is found? Yes, we have to
check password is messed or not, but
here is one thing. We store user password
in encrypted format. We can't compare it directly
with simple string password. So here we have to again
use crypt package. So crypt dot compare the first position,
we pass password, which we get from the
request dot body, and after that, we have
to pass a password, which we stored in database. So user dot password. Now, this expression
again take little time, so we can await here and simply store that result in
variable valid password. We can again pass condition
if valid password is not available and we return the same error
with same error message. I paste this line here. Now you might ask why we
send the same error message? It is because for
security reason, if we specify
password not match, then it means we found user and just password
is not matching. That's why developers
send this type of error message
invalid credentials. Now if password is matched, then we have to
create a new token. I copy the code from the register API and paste
it in our login API. Just we to do little changes. Here in the data, we have to pass user dot
underscore ID and user dot name. At the end, we simply send
response dot Json this token. Good. Now let's taste
this implementation. So go to Thunder client and
in the user's collection, create a new request
and give it a name, log in a user. Change the method to post, and URL to STP, call a double forward
slash Localhost 3,000 slash API slash
users slash Login. And in the body, we pass
Object with email fill. Here, we pass email, which we didn't create
code 12 athergmil.com. Also pass password 12345678
and send the request. See, here we get error
message invalid credentials. Now if we write valid email and pass the wrong password
and send the request, see, we still get
the same error. Now if we pass write email and write password and
send the request, see, now we get JSON Web Token. That's how we create Login API. Simply, we have to find user, compare its password
with BCR package. If it matched, then we generate JSN WebTken and send it in
response, simple as that. Now, before moving forward, here we literally
copy and pasting the code for generating
the JSON web token. Just we change this data. It's better we create a separate function for
generating the token, and then we can use
it in both APIs. So at the bottom, we create
a new function called generate token is equal
to arrow function. Now from this function, we want to simply return token. Copy this JWT dot Sine
method and paste it here. Now here we just want
to pass different data. We replace this data object with data variable and we get this
data from the parameter. Also, if you want to set
different token expiry time, then you can also pass
parameter for that. For now, we don't want that
2 hours is okay for both. Now in the log in API, we got this data object, which we pass in the JWT dot sign method
and we simply call here, generate token function, and pass that same data
object as argument. Good. Now, same we
do in register API, cut the data object, and here we call generate to confunction and pass
here that data object. By this implementation, our
code looks more organized.
102. How to Authenticate User? Logged in or Not?: So in the previous lesson, we see after sign up and log in, we generate JWT token and
send it in the response. Now let me tell
you what front end will do with that token. So when we send JWT token
from back end to front end, front end will
store that token in the local storage of the
browser or in the session. By that, front end will know
user is logged in or not. When user want to
access secure data, which is only accessed
by login user, front end needs to send that
token with the API call. So there are many ways to
send token with the API call. But most commonly, front end will send token in the request, specifically in
authorization header. If your front end
use another method, then they will
contact backend and then to implement it that way. So front end set the token
in the authorization header. And when user wants to
access protected API, which is only accessed
by locked in user, then in the back end, first, which token is verified or not. If it is verified, then we allow user
to access that API, and if token is not verified, then we return error
with Status code 401, authorization
token required. Now, where we write this logic? And create a middleware, especially for verify the token, and we can add that middleware
for all protected APIs. Let me show you
that practically. So in our project for
defining a middleware, we create a new folder
called middleware, and inside this folder, we create a new file
called oth dot gs. Now, do you remember
is middleware? Middleware is a function
that either call the next middleware function or send a response to
the current request. So here we define
a function called Orth middleware and AV know
for express middleware, we get three parameters
request response, and next error function. Now, first of all,
here we have to get the authorization header, SeconOth header is equal to request dot headers
dot authorization. Make sure you write the correct spelling
of the properties. Otherwise, we don't
get the header here. Let's console dot log this auth header to just see what we are getting
in this variable. In the future, we can
remove this console. Also, in the real world, we get this outh header
as this in the string, we first get error space, and then we get full JWT token. The reason we get here this
error is because it is common authentication
standard and ensure the server processes
the token correctly. Now before we verify the token, we have to check whether we get token or not in this header. We adhere if condition, if O header is not available, or we adhere one more condition, oth header starts
with here in string, we pass bearer space. If this condition is not true, then we return error. Make sure you add here not true. Now inside this, we return response dot status 401 for unauthorized
request dot Jason, and here we send object with message property,
authorization, token required. Make sure you send the object with the same property
for every error. It will make easy to
handle error on front end. Now, what at the oth header. As we know Oh header is
a string with prefix, bearer, space, and then token. So we have to get token
from this string. It is really simple. Const token is equal
to oth header. Here, we use split method, and from where we want to
split right from the space. So we pass double
codes and space. Now, this split method will
return array like this. First element is bearer and
second element is our token. We can access token
by index one. I square bracket, we write one. Now we have to only check
this token is valid or not. So for that, we need
JWT package at the top, we write Cast JWT is equal
to require Json web token. At the bottom, we write JWT dot verify this method
except two parameters. First one is a token
which we want to verify, which is this token. At the second parameter, we have to add the
JWT secret key, which is process Env dot
JWT, underscore key. Now if our token is
verified successfully, here we get the user data
which we send with the token. We can store it in variable
called decoded user. We can save this user data as request dot user is
equal to decoded user. If in any protected route, we want to get the current
logged in users data, then we can simply access it
by using request dot user. Don't worry, I
will show you that also as we move forward
in this project. Now after setting the user
data in request dot user, we don't want to do
anything in this method. So we can simply call
the next function, which we call the next
middleware in the call stack. If we don't call
this next function, then express will not call the API function or another
middleware function. Our server will
stay at this point, and that will make our
server really slow. Always call next function at the end of
middleware function. Now here in this code, what if our user
is not verified? What if user passed a fake
token or expired token? We also need to handle that. For handle error, we
add here try and catch blog and simply add these
three lines in the Try Blog. If we get error in
these three lines, this catch blog will run. In this catch blog,
we can simply return response with status 400 for invalid request dot
Json and What we pass here. Right, we pass here object
with property message and error message will be
invalid token and done. We complete the
authentication middleware. Let's also taste this. First of all, we have to export this orddalware to use this
middleware in our routes. So module dot Exports. Equal to Auth middleware. Save the changes and head
over to users route. Here at the bottom, after the login route, we add Router dot Get method and point to only forward slash. After that, we add callback function with
request and response. This is the normal API. But how can we make
this API protected, which means only locked in users should access
this API route. For that, we have to add middleware before this
callback function. At the top, we import
const middleware. You can also call
it OT is equal to require we go one fuller
up, middleware OT. To run this middleware before
this API callback function, we have to add it here before
the callback function. When users send Get
request to this endpoint, first, this orth middleware
function will run. In that middleware, which
act token is valid or not. If it is valid, only then
we call the next function, which will run this
API callback function. If in our application, we want to create
any protected API, we have to only add Omddalware before that
API callback function. Simple as that. Can you tell me how can we get the
logged in users data? Right, we get the logged in users data from
request dot user. So cost user equals
to request dot user. And here we simply send this user in the response
dot json method. Now let's test this
implementation. Save the changes and first, we need to generate token. Open Thunder client
and go to Login API. Here we have the data, so we can simply send
this login request. Good. Here we get the token. Make sure you
generate the letters token because we set 2
hours of expiry time. Copy this token, and here
we create a new request. Give it a name, logged in user. First of all, we write the
API endpoint, which is HTP, Callan double forward
slash local host, 3,000 API slash user. Now send this request. See, here we get the error. Authorization token
required because we didn't pass the token in
the authorization header. So for passing the
authorization header, we go to headers. Here we add a new header,
key to authorization. See, we also get authorization, and here what we will pass. Yes, we have to pass
token, but with prefix, error space, and here
we passed a token. Make sure you don't pass
it with double codes. Now let's send the request. See, here we get the log in
users data with ID name, It which is the issued at, this is the time
when this token is generated and EXP is
the expiration time, which means when this
token will expire. Also, if we check our terminal, a Console the auth header. See, first we get bearer prefix, and then we get token. So that's how we will add authentication middleware
to make our API protected. So here we are getting
only user name and ID. Let's send all information
about the logged in user. So in the user's route, here we add Fine query, sconcTUser is equal to
await user dot find By ID. And here we pass our user ID, which is request dot
user dot underscore ID. Dot select, and here we
don't want to send password. I string minus password, and then we return response
dot jacon this user. Remove this first user
line, we don't need it. Also, we have to make
this function an. Save the changes and take a. See, now we get the full
logged in users data. Now we can use this
route for getting the full information of
current log in user. That's how the authenticate
user is logged in or not by using this Os
middleware function.
103. OAuth in Details: So in the previous videos, we seen how we sign up and log in users using
email and password, which is very important. But nowadays, you
might have seen many websites provide
some additional features, like we can sign in with Google, sign in with Facebook, Twitter, sign in with Github, and list goes on and on
depending on the website type. Many users like to use
these methods for signing. If they use these methods, they don't need to
create a new password for another website
and remember it. They simply use their Google ID some social ID and
login into our website. Also, it is really secure. So it is very useful that we add these features in our
application as well. But before adding them, you might ask how these features
works behind the scenes. So here is the full architecture of the signing process.
Don't get afraid. Listen it as just a story
because in our daily life, we already using these features. Suppose here is a Hali he used one website called
Amazon or Ebay. That website, he has option for signing or
sign up with Google. If you click on this button, this button redirect him
to login page of Google. Here he enter his Google email
and password and log in. If he enter write
email and password, then something happens on the
web page, and at the end, he logs in in the
Amazon or eBay website with his username email without creating
the new password. This is really amazing. Now the main part is
happening between these. Let me tell you what
happened there. When Harley enter, write email and password on the Google
account and hit Login, Google server generate
a temporary code and send this temporary
code our backend. Now, in the backend,
using this code, we can fetch some usage
details which we want to access and store
it in our database, like name of the user, email ID, et cetera. So our backend will again contact to the Google server and tell them we want to access usage data instead of
this temporary code. So Google server verify the code and extract the user
data from that code. And then Google server send this usage data
to the backend. In our back end, we
have this usage data and we can use that data
whatever way we want to use. Like we can store user name and user email in our database
and generate a new user, or if user email is
already available, we don't do anything and simply redirect user to
front end with token. Now, when I first time learn
about this architecture, which is the Oath or we can
call open authorization. The time, I have one question and I'm sure you have
the same question. Why Google server send first this temporary or authorization
code to our backend? Why it can't directly send
the user information? There are some reasons why Google server send this
authorization code. Suppose Google directly send user information like email or profile data
to the front end, then it can be stopped or
deleted by hackers and also tempered or misused before
reaching to our backend. Also the temporary code act
like a permission sleep. It tells Google this user has logged in and given
permission to our app. Only our back end
with a secret key can extend this code for
the user's real data. This secret key is
given us by Google, and it will only
available in our back end inVFle same as our
JWT secret key. For the security reason, Google sends first, only
temporary or authorization code. Here is the recap
of how OAuth works. User first click on, sign in or sign up with Google button and then redcurs
to the Google Login page, or we can say Google server. Now, after that, Google server, check the email and
password is right or not. If it is right, then
Google generate authorization code and
send it to backend. Backend again send the
authorization code with the secret key
to Google server, and Google server extract
the user's data like name, email, profile pig, et cetera. Now, at last in the back end, we can store that
data in our database, or if user is already available, then we can simply
redirect user to front end homepage
with JWT Token. Simple as that. So
this architecture will work for all type of social networks like
sign in with Facebook, sign in with Github,
Twitter, et cetera. Not only for Google. I use here Google as example. If we need Facebook, then this Google
server changed by Facebook authorizes and server or Github authorizes and server. Now in the next lesson,
we will practically implement this Oath in
our Catwis application.
104. OAuth in Node Application - Signin with google: Let's implement sign in with Google feature in
this application. And for that, we need one
package called passpod dot js. So in OT Architecture, when user click on sign
in with Google Pattern, we will redirect user to
Suppose Local host Column 3,000 slash APIs OT
slash Google page. This is our backend
API. How can we define? We want to show
Google Login page or link Google server
with this page. Also, if on the back end, we get authorization code, how can we send requests to Google server and
retrieve user data? Yes, we can do
this all manually, but passport Js Library
makes it so much easier. We will use it.
Don't get confused. Just see this full
lesson and you will get how this Oath
implementation is working. So we go to passports.org page. Here we can see passport is authentication
middleware for No Jz, extremely flexible and modular. At the bottom, we get all
strategies like Facebook, Twitter, Google,
Github, et cetera. Don't worry, simply go to the
search bar and search here, Google, and select this
passport Google Oth two. Make sure you don't
select this oath 20. We use Oth two. These two are very similar, but this Oath 20
is older strategy. It is not really
well maintained. So we will use this OR two, which has new features and it is actively maintained
by passport. Let me zoom in a little bit. Yeah. So first of all, we need to install this package. So copy this command
back to Vas code, open terminal, and
in that terminal, we simply past the command here. And for the same version, you can write at
the rate 0.2 0.0. And also, we need passport
package at the rate, 0.7 0.0, and hit Enter. Good. Now back to documentation. Here they give us complete code for implementing passport
with Google strategy. Copy this whole code, and in our backend, we can paste this code in
our index dot Gs file. But Detw messed up our
index dot js file, so we can store that code
in the separate file. For that, we create a new folder called Config and inside it, create a new file,
passport dot JS and page that code inside this. For now, don't worry
about this code. I will explain this code and we will also do
some changes later. Now back to documentation,
scroll down, and here we can see they give us to get APIs to add
in our application. First API is Auth Google and at the place of
callback function, it passes passport dot
authenticate method, and at the first position, they pass Google, which is
the authenticate server, and after that, they pass
the scope of the data. Basically, they are saying which user data we want
to get from the server. We want email and profile, which include many
details about user. Now you might ask what
this API will do. When user send get request from the browser on this Oth
slash Google endpoint, this passport dot
authenticate method will open Google Login page. To add this route
in our application, we can do something like this. In the route folder, we create
a new file auth dot js. Creating separate routes
for authentication. In this, first of all, we need Cost Express is
equal to require Express. For adding route, we add cost Router is equal
to express dot Router. Now, from the documentation, we copy this first route and
paste it in the oth route. Also at the top, we have to input passport because here you
are using passport. Const passport is equal
to require passport. Now at the place of this app
dot GAD, what do we use? Right, we use router dot GAD. Also, we don't add this OT. We will mention
slash OT as prefix, same as we define
prefix for users route. Now at the bottom, we have
to export this route. So module dot exports
is equal to router. Save this and before
we forget to add this route in the index
dot js file, let's add it. After this user's
route, we add app.us. Here we add prefix API OT. At the second parameter, we have to pass Auth routes. So we input cost Os routes
is equal to require. Here we go in routes folder OT. Now we simply pass these other routes in
this app due method. Now back to auth dot js file. Here we have specified user
Xs API Auth slash Google, it should open
Google Login page. Now, after users
successfully login, then what we want to do we
need to specify that also. Reemember previously, we had some strategy code in the
passport dot JS file. Understand this code, and also we will do some
changes in this code. First of all, at the top, we need passport from the passport library because
we are using it here. SeconctPassport is equal to require passport and also
change this r to cost. After that, we have passport
dot ug and inside this, we have new Google strategy, which we get from passport, Google author to package. By this strategy, we don't
need to write manual code for fetching authorization code and extract data from
the Google server. This strategy do
all these for us. After that, we have
object with bunch of properties like client
ID and client secret. You will get this ID when you register your app
with Google Console, and with that, we also
get client secret code. Without these two fills, Google will not
give us users data. So let's generate this too. Again, I'm telling you, don't worry about this code. When we complete
this implementation, I will show you full
workflow of this code. In the browser, search,
Google Cloud Console. Open this second link. Here, we have to login
with our Google account. You can use any account. Now, after login,
it looks like this. Don't worry, simply go to here Select Project and create a
new project. Give it a name. Let's say Card Wish
and click on Create. Here in the notification, it is creating and
select this project. When we select this project, we can see that project here. Now we need API and services. Click on these three lines
and in the API and services, we go to O OT content screen. Simply do as I do and
you are ready to go. Click on this Get
Started button. Here, we have to do some
configuration setting. First of all, here we
have to write app name. Again, we write Cartwish. After that, select your support
email and click on Save. Now here we have
to select the type of audience, select
here external. By that any user can login in our application
and click on next. Here we write our email
on which we want to get notification related to this app and click on next at last, accurate terms and
continue and create app. Now we go to data access
from the left side bar. Here, we have to select
the scope of user data. Scopes express the permission you request users
to authorize for your app and allow
your project to access specific types of private users data from their Google account. Simply click on add
or remove scopes. Select the first to email and profile and click on
Update and save this. Now go to this audience, and here we can add taste users. Sometimes if after
all implementation, our users can't log in, then it might be we have to
add them in the taste users. When I did this, I
didn't found any error. So don't worry about that.
Simply go to clients. Here, we have to
generate OO credentials, and for that, we click
on Create Client. Select the application
type to web application. Name, we don't need to change. But at the bottom, we have to add authorized redirect URIs. A redirect URI is
the endpoint in our back end application
where Google redirects the user after
they login successfully. Here, we write STP, Column double forward
slash local host, Column 3,000 API OT,
Google, slash callback. Remember this endpoint. We need to define this
API in our back end. And click on Create and done. See, here we get client ID
and also client secret. Copy this client ID and in our application at the place
of this Google client ID, we can page that ID
or to make it secure, we can put it in dot ENV file. I think that is more secure. What do you think? So in dot
ENV file at new variable, Google, underscore,
client underscore ID. Equals to make sure don't addere space and past here client ID
of your Google application. After that, copy this client
secret, back to VSCode, and simply adhere Google, underscore, client,
underscore secret. Equals to base here secret. Save this file and back
to passport dot js file. Here, we pass process
dot and dot Google, underscore client underscore ID. And here we pass process
dot n dot Google, underscore client,
underscore secret. Now after that, we have
callback URL property. Make sure here we write the same endpoint which we pass in our Google Console App. We can see that from here, click on this edit button, and here we have callback URL. Copy this and paste
it in double codes. At the end, don't touch this pass request to
callback property. Make sure it is set to true. It will pass the request
object to this callback URL. Now, after this, at
the second parameter, we have callback function, or we can say strategy
callback function. This function will run when users successfully login
with their details, and we get here request object, access token, which is a token issued by
Google to our app. It allows our app to make authorized request to Google
APIs on behalf of the user. After that, we have
refresh token, which is a token that
can be used to get a new access token when
the current one expires. Don't worry, we don't
need this for now. After that, we have profile, which is an object
with information about the authenticate
user like name, email, Google ID, et cetera. Get this data from Google, and it really depends on
the scopes we requested. And at the end, we have done, which is the function
that you call when you complete
process of user's data. Now inside this function, we don't want to find user
or create a new user. We will do all of that
in this callback API. So here we remove this code and simply reten here
this done method. At the first argument, we
pass error which is null. At the second argument, we
have to pass users data. In this case, we can
directly pass profile. To summarize, when users successfully logged
in with Google, the strategic callback will
run and in this profile, we get users data
from the Google. Now when we call this
done method with a null and user data to profile, passport will run
this API endpoint, and we will define that
endpoint in the next lesson.
105. OAuth with JWT: Now let's define this last step of the Google authentication, which is adding
this callback API. Where we add this API, we will define it in the
routes shorthtjsFle. Here we already add one
API, which is step one. Now we add here another
router dot Get method, endpoint slash Google
slash callback. You have to give it the same as you pass in the
callback property. So as we know, Google will send usage data on this
callback route, and for that, we have to again adhere
passport Middleware. Passport dot authenticate. Here we pass Google at
the second argument, we set object with property, which is SSN to false. This will tell
Passport library to not store user ID
in the session. And also we pass one more
property, failure, redirect, and when user data will fail, then here, Google server
will redirect our user. So here we have to pass
our front end URL. Here, for example, we
pass Local React app. You can write SDTP column, double forward slash Local host. C5173 slash Login. This is very important. Now, after that, we can
add our callback function, which we will run after we send profile data from the password strategy
callback from here. Now, how can we get
data in this callback? So we get data in the
request dot user, and let's store this invariable
called profile and simply return this profile object in the response dot Json
and pass profile. Now let's test this
is working or not. For testing this implementation, we have to send Get
request to the endpoint. Open Browser and head over to SGTP Column double
forward slash. Local host, Column 3,000. If your application is
running on another pod, then you have to write
your Bean URL here, and then slash API
OT slash Google. And here we get error. It is saying error and non
authentication strategy, Google. Let me check. Google strategy is correct. Also, we pass the right
name of the ENV variables. Let me double check that.
Yes, they are same. Oh, wait. We didn't add this passport dot js file
in our index dot s file, and because of that, this file didn't config in
our application. I think this is the reason
we are getting that error. So go to index dot Gs
file, and at the top, we add require, we go to
Config folder and passport. See the changes, and let's
restart our application. Mon index dot js. Now back to Browser
and I rest the page, see, we directly redirect
to the Google Login page. Here we can see our
application name, which we mentioned in
our Google Console. Good. Now, you have to write your Google account details
and simply login with that. Here it asks for your
permission for allowing Google to send your email and profile data to
this application. Continue, and here you can see we get the user
profile data object. At the top, we get provider, which is Google ID, which is the Google Unique ID. Display name to your name, email underscore verified to true so we get EML ID
and many more details. You can use any of these details and store
it into your database. So we have completed
our major work for the Google authentication. Now we just need to
check if user with this EML ID is already available in our
application or not. It is not available, then we create a new user with
its email name, Google ID, but we leave the password fill because for login with
Google or Facebook, we don't need password fill, and then we generate JWT
token for that user's data. If user is already available in our database,
then don't worry, we simply update
its Google ID in our database and then generate JWT token for that user's data. So to implement this, we need to change our users
Schema little bit. How to do these changes
because in our application, we have user who can log in
with email and password, and also some user can
login with Google. Let's first modify
the schema of user. Here we make password,
required to true. But as we know, when
user login with Google, we don't store password, so we have to change
required to false. Also, when we create a new
user using Google Login, then we also don't store
the delivery address. So we can also make
required to false for the address and also remove
this mean length property. After that, we can
add one more field for user who used
Google for login. We add Google ID type to
string and unique to True. You might ask why we need this
Google ID in our database? We need this Google ID
because using that, we can identify user is already available in
our database or not. But here, we have
already users email. Why can't we use that email? See, in the browser, we are getting these
profile details. Here we have emails property, which is the array of emails. One Google account has one or more emails
like hurled gmail.com, which is the primary mail, hurleredcorporate.com for
work email, et cetera. If Harley change
its primary mail, then for the same
Google account, we might create a
new user account. For removing this email risk, we store Google ID. Another reason here we
have to use Google ID is because someone can change their email address
of Google account, but they can't change
the Google ID. Google ID is unique
for all Google users, and that's why we
store users Google ID. Also, Google ID is for identify only that users
who log in using Google, not for users who created account using simple
email and password. For those we use
email for identify. Save this file and let's quickly write our logic in this
Google Callback route. Me write comment for this logic. First we check user
is available or not using its Google
ID or with email. If user is available, then we update its
Google ID field and then simply generate JWT token
and send it in the response. Here we don't need to verify password because user is
logged in with Google. I user is not available, then we create a new
user with name, email, Google ID, and then generate JWT token and send
it in response. At the end, we have to send JWT token to front end,
simple as that. First of all, which user
is available or not. For that, we need
users collection. So at the top, cost
user equals to require, we go one folder
up models users. Now at the bottom,
we write fine query, user dot Fine one. And here we pass Object and
we add condition, Google ID. To profile dot ID. Also, what if someone
first login with email and password and then try
to login with Google. At the time, we don't found that users Google
ID in our database. So we need to also find
user based on email. You can use here or operator. We add dollar or column, and we have to add here
array of multiple condition. So we add one condition object, Google ID to profile dot ID, and second condition Object, email, to profile dot email. Now here is one thing. This
profile dot email field will not 100% available. For some Google accounts, it works, and for few
accounts, it will not work. So as a good practice, developer don't use this
profile dot email field. They use profile dot emails, which is the array and we access its first element by
square bracket zero index, and for value, we
adhere dot value. As we know, this query
returns a promise, so we can adhere await, in store the user object
in variable, let user. Also, for using await, we have to make this
callback acing. Now here we have two scenario. If user is available, and else user does
not available. Now what we want to do
if user is available, we will check Google
ID is stored or not. I user Google ID
is not available, then we set user dot Google ID. Equals to profile dot ID. This profile dot
ID is Google ID. And after that, for saving aid, we write await user dot save. Now in the else,
we have to write logic for user does not exist. Basically, we will
create a new user. So we write user is
equal to new user. Here we pass object
with properties. First one is name to
profile, dot display name. Then email to
profile dot emails. Here, we access first
element dot value. Same as we did before. Last property, we need
Google ID to profile dot ID. For saving this new user, we write here await
user dot save. You need to do only one thing. Generate JWT token and send it. So cost token equals to, we go to the user's route. We copy this JWT sign method for generating token
and paste it here. Here at the pace of
this data variable, we have to pass data which we want to send inside the token. And what we have
passed in login, yes, we pass ID and name only. So here we also pass
underscore ID, not Google ID. Google ID is just for backend for checking
this condition. Simply copy this data
object and pass here. If you want to send email also, then you can also menton that. But it's important in all token, you send the same details. It will easy for front
end to use that data. Also make sure here we
have to import JWT. Cost JWT is equal to
require JSON web token. Now at the place of sending these profile
details in response, we send JWT token, sems we send token in the simple signup and login with
email and password. Also, sometimes front end
developers tell you to redirect user at direct
homepage of the front end. At that time we can do
something like this. Response dot RediC and here
we use template string. Here, we have to mention
our front end page URL on which we want to redirect user after successfully
login with Google. F locally, we can write SDDP, Column double for
slash local host, Column 5173, which
is DeitRact app. Here you have to
write your front end URL, slash dashboard. Here we pass token in query parameter
using question mark, token is equal to dollar
Cali brackets, token. You can also change
this URL as you want. It really depends
on your front end. Many developers redirect users, and some developers
send only token. I show you both way, you can choose whatever your front
end developer tells you. Finally, we have implemented the sign in with Google
feature in our application. Let's recap what we have
done in last two lessons. First of all, user click on the sign in with
Google Button. Our front end will redirect
user to Local host Column 3,000 slash API
Ath slash Google. This is our Bend URL. If your back end is running
on local host 8,000, then user should
redirect to local host 8,000 API Auth Google. Now, when user redirect
to API Auth Google, this code will run. Here we mention passport
dot authenticate with Google and
mention the scope. Now, after that, directly
this code will run, which is available in
the passport JS file. Here, passport send
our client ID, client secret, and
the callback URL where we want to
send profile data. After users
successfully login with Google account and
allow the permission, then this function will run, and here we return the method with error null
and profile data. This profile data, we can access as request dot user in
our callback function. Also, make sure here we unable pass request
to callback to True. After that, our
callback API will run, and here we write
our user logic, and then we send or
redirect user with token. Simple as that. I'm sure
your all doubts are clear. Don't worry, this is your first
time and because of that, you are a little confused. But with time, you will understand this
without any confusion. Last two lessons a little long, but just see what you have implemented in your application. Congratulations for
that, you can take little break from the screen and then continue this section.
106. Sign in with Facebook using OAuth: So previously, we added
sign in with Google. Now we can also add
sign in with Presbook or any other platform
like Github or Twitter. There are some common
steps we have to follow. Step number one, install passport and passport strategy library in node application. Step two, add first
API endpoint, which will trigger passport
with that strategy. Step number three, add passport strategy code in
the passport dot js file. Same as we add Google strategy. Step number four, generate ID and secret for that platform. Last, step number five,
define Callback API, store users data if needed, and send JWT Token. With these five steps, we can add any platform in OT. Let me quickly show
you how we can implement the sign
in with Facebook. Step number one, we have to
install passport strategy. We go to the passport
documentation and search here Facebook. See, here we get
installation command, copy this and simply
paste it in our terminal. Also, if you want to use the
same version as I am using, then you can add here at the
rate 3.0 0.0 and hit Enter. Now step Number two, we
have to add API and point, which will open
Facebook login page. So in the passport
documentation, scroll down. And here we get API code. See, it looks very
similar to Google API. Remember, and simply
copy this first API. And in our th dot js file, after this callback API, we simply paste it here. Hang this API point
to slash Facebook and also passport dot
authenticate with Facebook, and for scope, we
have to write here public underscore
profile and email. Step number three,
we have to add passport strategy
code for Facebook. So go to passport website again and copy this
strategy code. Back to viscodeopen
passport dot js file, and simply at the bottom, we paste this code. Now we have to do
some correction. First of all, have to import Facebook strategy
from the package. So duplicate this Google
strategy line and simply change this variable
name to Facebook strategy. Same as they use here, and we also change
the package name to passport Facebook. Good. Now here, we have
to add these fills Facebook ID and Facebook secret which is the step number four. For generating the app
ID and app secret, we have to go to Facebook
developers page. Mention all details on the password documentation for every platform. Don't
worry about that. The new tab, search
here, Facebook, developer, console and
open this first link. Here, click on Login. I'm login with my account. Good. After login, we get
here get started option. It will may ask you for
the register and verify. Complete the basic steps, and then you will redirect
to this apps page. Click on Create app. Here, we write our app
name, which is Cardwis. This is the same details which we fill in the Google Console. Now add use cases, so we want to
authenticate and request data from users with
Facebook login. Se like this and click on next. Here, select, I don't want to
connect business portfolio. You might don't get this
option because recently, Facebook becomes
strict about this. So sorry if you don't
get the option here, but don't worry, write
a code as I'm writing. In the future, you
just have to add only Facebook app
ID and app secret. Here, click on next and next and done go to
Deskboard now here, we have to complete these steps. So Clikon customize adding
a Facebook login button. Here, we had permission. Add email and public
profile is already added. Now click on this quick start. Here, we select web right
here, our front end URL. For now, just write here, STTP, Column double forward slash, Local host, Column 5173. In real world, you have to
write your front end URL here and click on
Save and continue. Click on next, click on next, click on next and done. Now simply go to app setting at the bottom and go
to basic setting. Here we get ApiD and App Secret. Copy this APD and in
our dot ENV file, we add another variable
called Facebook, underscore app, underscore ID. Past the app ID. After that, we add another variable called
Facebook underscore app, underscore secret is
equal to back to browser, and here we click on show it may ask your
Facebook password, write that and see here we
can see the secret code. Copy that and simply
paste it in ENV file. Save the file and back
to passport dot JS file. Here, we at process.nw dot Facebook underscore
app, underscore ID. And here we also process dot nwt Facebook underscore
app underscore secret. Now, after that, we simply copy these two properties
from the previous code, callb URL and also
pass request to callback and page them
here for Facebook. Just here, we have to change
the callback URL to slash API Oath, slash
Facebook callback. Also, we have to addhe one more property because
Facebook is not like Google. Facebook don't send much data, so we have to specify property called profile fields to array, and in this array, add all
fills name from Facebook. We add ID, which is the
Facebook unique ID, and it is unique for
every Facebook user, same as Google ID. Also, we pass emails for
emails, name, display name. Comma picture dot Type, and in the parenthesis, large. This is for getting
profile picture. Most of the application
use this data. Now here for callback, we want to do the same
as this Google strategy. So we simply copy this strategy callback and
based it in Facebook strategy. Here, our step four is complete. Save this file. Now
last step number five, we have to just define this callback route
for the Facebook. Pens dot gs file. Here we can simply copy this Google callback and
paste it at the bottom. Also at the top, I notice we have to change this app dot GAD
to router dot gat. Good. First of all, we change the API and point to slash Facebook
slash callback and also in passport
dot Authenticate at the place of Google,
we pass Facebook. Now in the API callback, we have to change this
Google ID with Facebook ID. So we have to add
another field in our users schema
same as Google ID. Here, select Google ID and
press Control plus T or Command plus and select all Google ID in
this Facebook API. At the place of that,
we add Facebook ID, and we don't need
to change anything. Save the changes
and don't forget to add Facebook ID field
in the user schema. Open users dot gs file
and simply duplicate the Google ID field and change
its name to Facebook ID, and we are ready to go. Save the changes, less is this. Open Browser and
go to local host, Column 3,000 slash API
OT slash Facebook. See here we get app not active. This app is not
accessible right now. The reason is in this app, we didn't add or
verify the business. Recently, Facebook becomes
strict about that, but this doesn't matter
because we can see when we make this Get API
with APIs Orth, slash Facebook, we are
getting the Facebook page, which means by our end
our API is working fine. Our main goal is we implement
sign in with feature. If your client requires
sign in with Facebook, you can use their business
and verify your app. After that, this
feature will work, same as our sign in
with Google is working. So that's how we implement Auth for our backend
application. It is really simple. We have to follow
these five steps.
107. Simplifying the Code: Now as we know, in
this both callback, our code is the same. Just we change the Google
ID with Facebook ID. As a better practice,
we can define a common reusable function which simply written
token at the end. Let's cut this logic from et user till this
token generation. At the bottom, we create
a new function, const, handle, or oth callback is
equal to arrow function. In the Gly brackets, we past this code, and at the end, we
simply return token. Now, first of all,
for using this Avid, we need to make this
function async. Now what we want to change here? Here, we need this profile data, and also we need this property
which we want to change, Google ID or Facebook ID. So we can pass here two
parameters profile, which is the full profile data, and after that, provider ID. We can't add this provider ID because when we call this
handle or callback function, we pass Google ID or
Facebook ID as string, and we can't add
here that string. It will not work.
Simply, we can use here, square bracket, provider ID. Also, at the place of
user dot Google ID, we can write user square
brackets provider ID. This is the second
way to accessing the value from object A here and also in the
Else blog and done. Now in the Google Callback API, we call this handle
orth callback function. And pass here profile
at the first parameter, and for provider ID, what we will pass write, we pass here, string, Google ID. Make sure you write the same field name as you
set in the user's schema. Now, this function will
take some time to execute. So we can use here await and
this function returns token. So we can store it
in variable token. Let's do the same for
Facebook callback. Copy this token line and in the Facebook callback from
the let user till this token, we remove this and simply
past here this function call. Here, don't forget to change this provider ID to Facebook ID. That's what we said, fill name? Yes, it is Facebook ID. See, now our code looks clean. Now in the next section,
we'll create APIs for products card and user
orders. This is really fun.
108. Problem with Single Token [UPDATE]: Now let's see how big companies
like Google, Microsoft, and Netflix implement secure
and seamless authentication, and this is how other
production level applications also implement authentication. They use two types of JWT token, access token and refresh token. Don't worry about
these big words. Let me explain to you this
in very simple words. Currently, in our application, we created API for register, a new user, and then we create
API for logging that user. Both these APIs, we are sending JWT token to front
end and our front end will send that token in the request header with bearer prefix and
using that header, we get user information
in our oth middleware. Now there are two problems
with this approach. First one is the token
expiration problem. We set token expiration time
for 2 hours after 2 hours, our user need to login
again to get a new token. This is bad user experience
because they have to log in again every
time the token expires. Now you might say we can
increase the expiration time, and by that, our user
experience will be fixed. Yes, this is true,
but in this way, there is another problem
which is about security risk. Imagine for good
user experience, we increase the expiration
time to ten days. Now, if that JWT token gets
stolen by someone else, then the attacker or
hacker can use that token as we are using
until the token expires. In this case, we have no way to log out or
revoke the token. We have to only wait
for token to expire. So as we can see, we can't increase the time
of the expiration. Have to make this
expiration time to 5 minutes or 10 minutes. Now, to make user
experience great, first Google, come up with
these two tokens concepts, which is access token
and repress token and we will see how this concept
works in the next lesson.
109. Access Token & Refresh Token Logic [UPDATE]: Now let's understand the concept of excess token
and refresh token. So in the place of
creating one token, we create two tokens in
our register or login API. First token, we called it as excess token and second token, we called it as refresh token. These both are JWT tokens, same as we generated token
at the end of our API. But the only difference
is for excess token, we set expiration time as sort like 5 minutes
or 10 minutes or max in hours for
reference token, we set expiration time
as long for example, for five days or two
weeks like that, excess token and
reference token, both are JWT tokens. We can generate them by
using JWT dot sign method, but just their expiration
time is different. Now you might ask what will make difference by creating
two tokens separately. To make this simple me explain your full workflow so your
all doubts will clear. Imagine this is front
end and this is backend. User fill the login form like email and password
and submit it. At the back end, we check the details and
compare password. If information is correct, then previously, we are
creating one token. In this approach, we
create two tokens. One is excess token and
second is refresh token. We give excess token
expiration time to let's say 5 minutes
and for refresh token, we give expiration
time to five days. We store this refresh token in user's collection and then send these both
tokens to front end. Our front end we
set excess token in the global API header with
better prefix, same as before. Now, if user excess protected
API, then at the back end, we get excess token from the request header and we can send the data our user needs. Now imagine after 5 minutes, our access token get expired. Our user send the
API request again. Here, our backend checks, this access token
is valid or not. Here it is not valid, from the back end we
send the response with 401 error in valid token. Now on the front end, developer will write code. When front end, get 401 error, developer will call another API for getting new access token. Let's say OT slash refresh. Now in this API, we can access the refresh token. But here, we have to verify if this refresh token is valid
for this user or not. Remember, when we generate new access token
and refresh token, we store the refresh
token in the database. We can compare these two refresh tokens if
they are matched, which means user is valid. At that time, we again
generate a new access token, a new refresh token, update the refresh token
in the user's collection, and same as before, send these both tokens
to the front end. By this, user don't need
to login again and again. When user don't
have refresh token or when a token get expired, only then you need to login
again. Simple as that. Now the question
you might ask is, what if this access
token gets stolen? With that, attacker
can easily get access. Yes, that is possible, and that's why nowadays, front end developers are not storing excess token
in the local storage. They store it in the apse like JavaScript variable or
RXtate or Redux store. And even if somehow attacker
still the excess token, we keep the excess token
expiry time to very short. Now you might ask attackers can also still the refresh
token. What about that? And also, it has long expiry. To solve this issue
at the place of sending refrestocan
to front end, we can set refrestocen
in the SDTPOly cookie. Now, what is the SDTPOly cookie? An SDDPOly cookie is
an special type of cookie that cannot be accessed by JavaScript
running on the browser. This SDDP only Cookie can't
access via our browser. Our browser can only send this
cookie with the API call. But JavaScript on the page or running in the
browser cannot read, modify, or delete a cookie, which makes our refresh
token safe from attackers. Now you might ask
this SDDPOly cookie is sent only our API calls, or it will send with
any other API calls. This is really great question. SDDPOly Cookie has feature which we called as
same site to street. We set property, same site
to street, and by that, this SDDP only Cookie will only send to our
backend domain APIs, not to someone else backend. That's why refers
token is high in security because it can't
access by JavaScript. Now let's recap this access token and refers token concept. When user register or login
at the place of one token, we create two tokens. First one is excess token, which expiration time is sought, and second token
is refresh token, which expiration time is long. We store the refresh token in the user's collection
for that user, and then we send
the excess token to front end we set refresh
token in the SDDPOlyOki. Now, the great thing
about SDTPOly Cookie is it cannot access by the
browser JavaScript code. Friend and browser can only send that SDTP Cookie
with SDDPRquest. This makes our refresh token
more secure, and as we know, our access token is available
in the application state, not in the local storage. So our boat tokens are secure, and that's how we
can give our user better experience
and better security. Simple as that. So we
use access token for accessing the data from the back end and that's why
we call it access token. Also we use refresh token for repressing our access token, and that's why we call
it refresh token. This workflow is really
important for your application, and many developers don't really know about this full workflow. But I'm sure now you know about it, which is really great. Now in the next
lesson, we implement this logic in our
code. It will be fun.
110. Implement Access Token & Refresh Token [UPDATE]: Now to implement excess token
and refresh token approach, we need to do very little
bid in our backend. First, when we register
or login user, we need to generate
two tokens and store refresh token in
the user's collection. After that, we have to
return excess token, same as we are sending it
previously in the response. And then we have to ct refresh token in the SGDP only cookie, so our browser JavaScript
can't access or refresh token. Don't worry, it is really
simple. Let's do that. So here we created one
function for generating token, and we are directly
returning the token. So instead of generating
single token, here we can generate two tokens. So first, we change this function name
to generate tokens. And inside this at the
place of this written, we store it in variable
called access token. And after that, we duplicate this line and we want to
generate refresh token, such as the variable
name to refresh token, also, we need to change the
expiry of excess token to 5 minutes and
refresh token expiry to seven D for seven days. Also, in the refresh token, we don't want to send
all the user's data which we want in
the excess token. So here at the place of data, we simply add object with ID, column, and passier
data dot underscore ID. Also, many developers
like to use different secret key for access token and repress token
for security reasons. Here also, we open dot ANV file, and here at the
place of JWT key, we write access token, key. After that, we create
variable for rephrase, token, key, and give you
any key you like to add. Save this and in our users
route, in our function, we change JWTk to
excess token, K, and this JWTk to rephrase
token, K. Great. Now, at the end
of this function, we return object with two
properties, excess token, access token or we can remove this access
token and after that, we return refresh token
to refresh token. Good. Now let's see what we have
to do in the register, a new user API. At the bottom, we
can see we call this generate token
function and we pass here under square ID
and name property. Here, as we know, we don't return single token
from this function. We are returning object, so we can destructure
this object here and get access token
and refresh token. Now in the response,
same as before, we return excess token. Also, some companies also send refresh token
in the response, but we are not going to do that. We are implementing here
production level code. So we will set this refresh
token in the SDP only Cookie. For that, we write
response dot Cookie. Make sure you at response
dot Cookie before writing response dot send
or response dot JsN because we can't set cookie
after we send the response. If you write it after response dot json,
you will get error. With the first parameter, we write our property name. Let's say refers token. After that, we pass
refresh token. At the third parameter, we have to pass object with
some important properties. First, we write
SGDP only to true. This means JavaScript on the browser cannot
read this cookie. After that, we add secure to true for ensuring
the SDP as request. Now currently we
are testing it for local STDP request and that's
why we make it here false. But to remember we write
here command change this secure to true
for production. After that, we have
same site to street. By that, we stop our cookie to go on
another domain API call. But here, you have to make sure your frontend and backend
should be on the same domain. For example, gobleu.com for front end and
cola.com for backend. If your backend and front end are hosted
on different domain, then we can set this
setting to none, which means our Cookie
can go to any domain. Here we want our browser, send Cookie only for
our backend platform. For that, here we write
domain property to our Bend domain like
api dotben.com. By this property, only this
domain will get this Cookie, not any other domain. Currently, we are
running our back end on Local Host 3,000 and
in the domain field, we can't add local host 3,000 because here we have to
add only real domain. We comment out this
property also and at last, we add MaxH to our repress
token expiration time. Here we pass seven days, same as our refresh
token expiry time. Seven into 24 hours into 60 minutes into 60 seconds
into 1,000 milliseconds. This simply means
after seven days, this STP only Cookie will also expire and remove and done. We set the refresh token
for SDTPOly Cookie. Now we have to do the
same for login API. So we copy this response
dot Cooke method, and before this response
dot Jason, we paste it. So here at the place
of this token, we destructure object and get here excess token
and repress token. Also change the function
name to generate tokens, and here we return excess
token in the response. Also, when we create
refresh token, we have to stood that token in our users collection
because using that only, we will know how refresh
token is valid or not. First of all, in our
users schema, at the end, we add refrescen two Carly
brackets, type two string. Save this and back
to users route. Here in the login route
before sending excess token, we add user dot refresh token
is equal to refresh token. And then we add a weight user dot s. Now
here is one thing. Many developers like
to store he has or encrypted refresh token in the database for extra security, and it is also good practice. So here also, we can
do cost, new hed, refresh token is equal to await, crypt dot a, here, we pass refresh token
and we set SALT to ten. Same as we are
hezing the password, we are hezing the refresh token. Here at the place
of refresh token, we store the new
hesed refresh token. Also, copy these lines
and in our register API, we paste it here. Also we have to change this to new user dot refresh token
and await Nwuser dot CV. That's it. Let's check
this is working or not. Save the changes, make sure
our backend is running. Now, open up Postman, open the login API
and send the request. See, here we get the new token, which is our access token. And if we see after
this body tab, we get one Cookie. If we check that, see, here we get the Cookie name
which is refresh token. So we get value, domain, path expires, STP only to true and secure to
false, which is great. So we successfully set refresh
token in the SDDPOlyGoGi. Now in the next lesson,
we will create route in which our front end
send this refresh token, we verify that token and then
send them new access token.
111. Refresh Route for New Access Token [UPDATE]: Now, as we know, when our
access token get expired, front end will send
refresh token to our back end for getting
new access token. But the question is
on which endpoint, front end will send
refresh token. We have to define that endpoint. So in the author
route, at the end, we add outer dot post, endpoint to slash refresh
and callback function with request and response. Also, this is not compulsory. We have to define
route called refresh. It is just a common
convention developers use. Now in this function, we
need to do little things. First, we check refresh token is available in
the cookie or not. So to access the cookie, we use request dot Cookies
dot our Cookie name, which we set to repress token. Let me show you. See,
this is our cookie name. Now we have to
store this value in variable called
user repress token. And after that, we
simply add condition. If user repress token
is not available, then we return
response dot status 2401 dot JSON message property to no refresh token provided. Now what if we get
a refresh token? Simply, we have to compare
that refresh token with the token which we save
in our user's collection. Now the question is how we know which user send
this refresh token. For that, remember when we
create a refresh token, at the time we pass
here ID in the token. So by using this ID, we can pass the
stored refresh token. For that, we write
JWT dot Verify. First, we pass refresh
token, after that, we have to pass the
secret key which is process dotenv dot
refresh token key. Make sure we add here refresh token key,
not excess Tgenkey. Otherwise, a token
will not verify. Now, this expression return the payload or we
can say the data. So we store it in the
variable called decoded user. Suppose this token is not valid. Someone changed
something in this token, so this expression
can return error. So we can adhere, try and catch blog
and simply add this line into dr blog
and in the cache blog, we get here error, and we simply return response, dot status 403, dot Jason and messet property
to invalid, repress token. Now suppose we get decoded user from the token and
now by using that, we have to find the
stored user token. So cost user is
equal to a weight, user dot find by ID, and pass here decoded
user dot underscore ID. Here also we add condition. If user is not available, then we return
response dot status, 404 dot Json and message
to user not found. Also, here for using await, we have to add async
for our callback. Good. Now, if user is available, then we just need to
compare the token. Now, as we know,
in our collection, we stored as token, so we need to compare it in the same way we are
comparing our password. So for that, we write
await, decrypt dot compare. First we pass user
refresh token, which we get from the cookie, and then we want to compare it with user dot refresh token. This expression will
return it is valid or not. So we store it in variable. Cost is valid. Also, make sure we import
B crypt in this file. So at the top, we add cost. B crypt is equal to
require here we add Wikip. Now back to our route. Here, after this, we need
to pass her condition. I is valid is false, then we return
response dot status 403 dot JsN and message to
refresh token is not valid. And if token is valid, then we need to repeat exact same process which we are doing after
login successful. We open Login API here
after comparing password, C, we generate new access
token and new refresh token, as the new refresh token, update user dot
refresh token with new stoken then we set the
refresh token in the cookie, and at the end, we
return access token. We simply copy this code. And paste it in
our repress route. Now here, first of all, we need this generate
to Cs function. We can input our function
from the user's route file, but for that, we need to change input in the index dot JS file. I don't want to confuse
you for doing that, so we can simply copy this
generate to C function from here and we simply paste it in the outhoute
file, and that's it. Here, our refresh route
implementation is done. Let's this API, save the
changes and take a look. Open Postman, here we create a new request called refresh
token method to post, and here we write our URL, STP, Column double for
slash Local host, Column 3,000 slash APIs OT
because it is in the Outhoute. Slash refresh. Now simply send the request. See, here we get error, cannot read properties
of undefined. I think we are not getting ooky in the user refresh token. Let me try to console dot log. User repres token,
save the changes, and send the request again. See, here we get the same error, and if we open our
via code, see, we are not getting the refresh
token in the console log, which means we are not getting the user refresh
token in the cookie. The reason we are not getting cookie is because in Node js, we can't directly access Cookie, same as we can't directly
access request body data. Remember, for accessing
the request body data, to add Express dot
JN middleware. For Cookie also, we need to add one middleware which will help us to get Cookie from request. So for that, we install
another package Cookie parser, NPM install Cookie, das
parser and hit enter. Good. Now, in our index dot Js file, first we input cost cookie parser is equal to
require cookie parser. And then at the bottom, we add app dot ug, Cookie parser and call
this function here. Without this Cookie
parser function, we can't access Cookie
in request dot cookies. Also, in our represe API, I notice this decoded
user is grade out. It's because we define it with Cst in the dry blog
and because of cost, this decoded user is only
accessible in this dry blog. Here we want to access decoded user outside
of the dri blog. So for that, before
this Tyblog we define let decoded user and
then remove this cost. Now we can access
decoded user here, save the changes,
and take a look. Send the request and see
here we get user not found, which means we are not getting
user data. Let me check. Here we are passing decoded
user dot underscore ID. Let me check it is underscore
ID property or not. Yes, see, in the
generating refresh token, we pass ID property
not underscore ID. So we update this to
underscore ID and also with the same the user's route generate
tokens function. Save the changes
and take a look. Here, if you get token expired, then you can call the log in API that will set the
new token to Koki. Now let's send the
refresh request again. See now in response, we get our new excess token, and also new refresh token is
set in the request cookie. If we again send
the same request, then see here we
get both new token. This is similar to our front
end page get refreshed. If excess token get expired,
then our front end, call this refresh API, and then our front end gets the new excess token
and refresh token.
112. OAuth with two tokens [UPDATE]: Now, let's quickly
update Oth code also because here we are
also sending only one token. So as you can see, we
are generating token inside this handle OT
callback function. Now at the place of
generating the token here, we can use our generate
tokens function and pass here the
same data as this. And now we get here
const cli brackets, excess token, and refresh token. Now we don't need this token
variable, and after that, we simply return object with excess token
to excess token, or we can remove this and
refresh token to refresh token. Now here we have one thing. Here, we already have
user data. It's good. We save the refresh token
in the user's data, so we don't need to send
another data by query. So from the login user API, we copy these crypting code, and also save token in
user dot Refresh Token. And paste this code in
handle or callback function. Now in our Google Callback API, here we get object at
the place of token and we destructure excess
token and repress token. Now, before redirecting user
to front end with token, we need to set refresh token
in the SDP only cookie. So we go to the login
API again and copy this response dot Cookie method and paste it in the
Google Callback API. At last, we simply pass
excess token at the place of togan now we do the same
for Facebook callback API. Here, we get object
with excess token and repress token and paste
here, the same Goki. Then we simply change this token with excess token,
and that's it. Now in the next lesson, we see what we have to do
for logo the user.
113. Route for Logout a User [UPDATE]: Now, can you tell me what
happened when user logged out? If we expire excess token
from the front end, then in the SCTP only cookie, we have already refresh
token which has long expiry. Our front end can
run refresh API again and get the new tokens,
but we don't want that. We want to completely
log out the user. So for that, we have to
only remove refresh token from the SDDP only Cookie and
from the user's collection. So let's create here new route, outer dot post slash Log hut and ASN callback function
with request and response. Now for removing Cookie, we use response dot
clear Cookie method. Here we pass our cookie name
which we want to remove. We pass here refresh token, make sure you check your cookie name while
you generate it. Now, this method will
remove refrestken cookie, and it works in
the local tasting. But when we want to implement
this in production, then we have to also add
object with properties. This expression work
for production, but sometimes it can cause
problem in the production. It's better we pass properties. Make sure it is the
same properties as we add during
Cookie creation. Copy this object and in our
clear cookie, we pass it. Good. Now after that, we simply return
response dot Json Object with message property
logged out successfully. Now before removing the
cookie, it's better. We also remove the refresh token from the user's collection. For that, we need to refresh
token from the cookie, decode the user ID
from the token, fetches the token
from the database, and then we have to remove it. We have already done
this in our refresh API. Remember, we can simply
copy this code from getting Cookie to decode user until
we are getting the user. Copy this and simply paste it before the
clear Cookie method. Now here we have user. We just need to do user dot refresh token
is equal to null. And then we await user
dot save and done. Let's test this logout API. Say the changes NO one Postman, duplicate this refresh
token request. Good, change the request
name to logo a user. Change API and point
to slash logout. Now, currently, we
check our cookie. See it is available here. Now, if we send this
logout request, see, here we get logout successfully, and if we again
check the cookie, it is gone from here. So this is how production
level registration, login, logout and token works. Also, last six lessons
are updated lessons. So if you don't see this
code in upcoming lessons, then don't worry about it. You can follow those lessons. I just want to make
these sores up to date. So currently we are
tasting this project. So if we set excess to
an expiry to 5 minutes, then it is very hard to taste. So for tasting only, we set expiry to one day. Also, in the user's route
file at the bottom, we change excess to
an expiry to one day. In production, we can
update it to 5 minutes.
114. Section 10 - Creating the Category Model: Welcome to the then section of the ultimate Node JS course. In this section, we will
continue working on our project two Ecommerce
Application backend. We will add some
cool features like adding products
images in backend, role based authorization, simple and fast search
query, and much, much more. So let's start
into this section. First of all, in our
ecommerce application, we want to add
products by category. If you select
category earphones, we will only send products
whose category is earphone. For that, we have to
define category model. So in the models folder, we create a new file
called category dot JS. Now, inside this
file, first of all, Const Mongoose is equal
to require Mongoose. After that, cost category schema is equal to nu
mangos dot schema. Inside this object, we
will add our schema. Now, what do you want to add
in this category collection? First, we need the
name of the category. Then we might need the
icon of the category. These properties depends on
the UI part of the front end. We want to display categories
like this with icons, then we have to store that Cn
file name in our database. Now, in our application,
we want to do one thing. Only Admin can add
categories in our database. Simple user can't add
category or delete category. A, if in our application, we have multiple admins, then we can add here
filled for Admin. Is the ID of that admine. But here we don't want to store the admin details so we
can remove this fill. So we have only two fills for this category model
or collection. So we add here name
to object, type, to string required to true
and also unique to true. Next, we want icon image
name to object, type, to string because we want
to store the image name here and required to
true, and that's it. Now many students get confused in storing
image in the backend. Let me explain you the logic. In database, we can't
directly store images. So we do something like this. Suppose user upload this one
image from the front end. In the backend server, we create one folder called uploads, and in that we store the image. Now we have uploaded file in our server with whatever name we want to give to that image. After that, we simply store the name of the image
in the database. Also, some people like to
store whole path of the image, but that is not
needed in this case, because we are storing this
image on our own server. So we need only to fill name of the category and image
which is the image name. Now, after that, we can create a model based on this schema. Seconst category is equal
to Mongos dot model. First, we pass the
singular name of our categories collection,
which is category. And second, we pass the
schema, and at the end, we module dot exports is
equal to this category. Done. Now in the next
lesson, we will add API for creating new
category with image upload.
115. Create a new Category API with Image upload: Now let's create a
new API for category. For that, we create
a new file in the Routes folder
called category dot js. In this file, what we do? Write, we have to create
Router using Express. So const Express is equal
to require Express. And next const Router is
equal to express dot Router. And also at the last, we have to export this router. Module dot exports
is equal to router. Good. Now before
we forget to add this new route in the index
dot JS file, let's do this. I have done this mistake
and tried to test my APIs. Save this file, go to
index dot js file. Here, we import category
routes is equal to require here we go to the routes folder and
in that category. Now at the bottom, we
use app dot g. Here, we define the prefix, which is API slash category. After that, at the
second parameter, we pass category routes. Save this file and back to
categories routes file. Good. Now let's define API
for creating new category. Here we define a new route
using router dot post. 8.2 forward slash,
and here we pass callback function with request and response
arrow function. Here we want to store the icon image with
the name of category. So for storing the image, we need one package
called Multer. This is very popular
package for storing all types of files in
the Express application. A lot of developers
confused with Multer but it is really
simple. Let me show you. Open up terminal and write
NPM install Multer at the 1.4 0.5 dash ts
dot one and hit Enter. Good, minimize this terminal. Now, in our file, we first add Const ulter is equal
to require Multer. And after that, we
create a variable called st upload is equal to here, we call this Multer method, and inside the parenthesis, we have to pass object
with dest property, which is the destination. Here, we have to specify
in which folder or path, we want to store our image. We write upload category. By this, our category images will stay in separate folder. Now we have to add
this upload method as middleware of our post API. Here we add upload dot. Here we have many
methods like single. This will accept
only single file. None, this will not
accept any file. A will accept multiple files. N will accept all
files and fills, which accepts specific
multiple fills. If you want to go
more deep into these, then you can look at
its documentation. For now, don't worry about that. Here, we want to accept
and save only single file. We use here, single method. Now in this parenthesis, we have to pass the field name. Suppose we pass here, icon. Now from the front end, we have to use input
tag, type to file, and in the name
field have to pass the same name which is
C. By this filled name, Malta knows which file it
has to save, and that's it. Multer will save
our file icon in the destination path
upload category, and we will get the
information about that file in the
request dot file, and we will get rest
of the form data in the request dot body,
same as before. Now let's write the rest
of the code for this API. So first of all, here
we add I condition, and this will check request
dot body dot name is not available or request dot
file is not available. Then here we return
response dot status, 400 for bad request dot JSON, and here we pass Object with message property and
pass here error message, name and icon are required now
what if we have this both? We create a new category, const, new category
is equal to new. Here, we need category model. So at the top, cost
category is equal to require here we go one
folded Up models category. Good. Now at the bottom, we adhere new category, and here we pass object with properties
of this collection. First, we add name to
request dot body dot Name. We have only one
fill in the body, and that's why we don't destructure this
fill same as before. Next, we pass image
to request dot file. And here we want to
save the file name, so we write filename. If you want to store the
full path of the fill, then you have to adhere, request dot file dot path. Also, we can adhere
console dot log, request dot file to see what we get into
request dot file, so we don't get confused. After that, we add await, new category dot c, and you know what
we have to do now. Yes, we have to
make this function is for using await,
at the very end, we send response with stats
to 01 for new data dot Json. Here we add object with messet property, category
added successfully. As we pass category
to new category. Now let's test this API. It is a little different because here we
have to send file. We can't send a file
in the JSON format, so we have to use here form, and also we can't
upload file using Thunder Client
because that feature is only available for paid user. Many developers like Postman and don't like Thunderclient
because of this. We will use Postman here. If you don't know Postman, I will give you my
previous tutorial for quick guide of the Postman. So to download the Postman, we go to postman.com. Here, we can download
this application. Simply install this application. It is really simple. This is how it looks when
we open it first time. We have two options, create a new account or
log in with account. I quickly log in with
my account here. If you sign up or log in, then you can create
here collection just like Thunder client. Here, we create a new
blank collection. Caris and in our Cartws project, we add a new folder
called category. If you want to zoom
in, then press Control and plus
or Command Plus. In this folder, we
add a new request, create a new category. Now, first of all, we select
post method, URL to STP, column double forward
slash local host, Column 3,000 ABI category. Now to send a file in the
body, we go to the body. Here we select the form data. Here, we get key, which is the fill name,
value, and description. For first, we add our key, which is the filled name
to let's say laptops. Next, we enter our fill name
for file, and what was that? Remember, we write here icon. So here also, we
have to write icon. For upload file at the
right side of the key, we get text and drop down, which is the content type. Here, we select File, click on Select File and select a new file from
the local machine. Now for laptop icons, go to the resources folder, which you download previously, or you can also download it
right now below this lesson. Resources folder, I added
folder for Project two. In that folder, we go
to category folder, and in that, you get
all icons for category. Select laptop dot
PNG file, and OpenI. Now let's send this request. Here I get error. Sorry, I forgot to
start this application. So note on index dots good. Also, here I notice I make this mistake,
response dot status. Save the changes and back to Postman and send the request. See, here we get
the new category, laptops and image to
this random name. Also, let's check our file get saved in the
upload folder or not. See, here we get the file
with that random file name. So it is working there
are some issues. This file, save without its
extension like dot png, or dot JPG, et cetera. Without extension, our image will not visible on the browser. This is the number one issue. Next issue is here, we can upload any type of
file, not only images. Suppose by mistake,
someone upload here dot PDF file,
then what we will do. So here have to add
filter for that also. Only dot png or dot jpg or dot GIF can be upload
for this image fill, and we will solve these
issues in the next lesson.
116. Set file name & filter in multer: The previous lesson,
we have two issues. We don't get extension for our image file and
also in database. Second, we can't
filter only images. Let's solve this. This
is really simple. Let me move this
category above of the multer so we can
see this clearly. Here, we pass only dest property
in the multer function. But in multer, we have few
more properties than desk. Remove this destination and here we can pass
storage property, which except the multer
disk storage property. So instead of
cluttering the co here, we can define it in
separate variable. Cost storage is equal to
multer dot disk storage. Inside this, we can define how and where files should
be stored on our server. We pass Object and
first property is destination where we
want to store our file. This except Callback function, which has three
properties, request file. This is the uploaded
file object, and CB is a callback function to specify the destination path. Error function here, we simply call this callback function
at the first position, we pass null, which
is for error. After that, we will pass
our destination path, which is upload category. This callback will set the destination as
upload category. If this directory
does not exist, then we have to
create it manually. But we have already
this folder here. If you don't have, then you
have to create this path, upload, and in that
folder category folder. Otherwise, ulter
will give us error. Now, after the destination, here we can also specify
the file name property. This also accept the
callback function with the same three properties, request file, CB for
callback and arrow function. Now in this function,
we want to define the specific and unique
file name for our file. Unique file name because if
two file names are same, then the last one will
replace the first image. Instead of female picture, user will get male picture, and that's not we want,
as a common practice, we store the timestamp in
the file name like this. First timestamp, and then
file name with extension. This are almost all files
will get unique name. SecctTtmp is equal
to date dot now. By this, we get
the current time. After that, we want to also do something
in the file name. SeconstOiginal name is equal
to file. Dot original name. By this, we will get
the original name of the uploaded file
like laptops dot PNG. Now in the file name, there
are maybe some space, so it's better we replace
that space with the dash. It will make our file
name URL friendly. We add plex method. Here, we first add
regular expression and its syntax is double forward
slash between these, we add backward slash as for space and plus for one
or more white space. At the end of the
regular expression, add G for global flag, which makes sure all matches in the string are replaced,
not just first one. Here we pass dash
in single codes, this means all space
will replaced by dash. Now, what if in the file name, we also get the
special characters? We need to remove them as well. We add another replace method. First, what do we pass? Regular expression, and what is the syntax, double
forward slash? Between these, we are getting regular expression for getting
the special characters. Here we add A to Z, Alt A to Z zero to nine, also now we want to get other characters
which are not part of this, and that will be our
special characters. T inverse this, we wrap it in the square brackets and after
the first square bracket, we will add Garret at the end, we also add G for global flag. Now we want to replace these special
characters by nothing. We just want to remove it. So we add here only
codes without anything. Don't worry if you
don't know much about regular
expressions, it's okay. Now by these two methods, if our file name is this, then it will make like this. Now at the end, we will mix this timestamp and
original file name. CB first we pass null for error, and second, we write back tags, here we add dollar
Cully Brackets, timestamp, dollar
Cully brackets, original name and done. We define destination and also file name for
our uploaded file. Now we can simply pass this storage variable here
in the Multi function. Our first issue is solved. Now we move to issue two for applying the
filter for file. In the multer function, we have another property
called file filter. Here also, we have to pass callback function so we can define it in
separate variable. Cost, file filter is equal to callback function with
three parameters, request, file, and CB error function, and in this function, we decide whether the file should be accepted or rejected. Here, we first define a
variable, allowed types, which is the array, and here we add all types
which we accept. First, images GPG, which is
dot JPG and dot JPG both. Then image png, for dot png, and image GIF for dot GIF. Here, we can simply
put condition. If While mime type is from this, then we allow it, else
we will reject it. If allow types, dot includes
and in the parenthesis, we pass file dot Mme type. Now you might ask
what is Mme type? Mime type stands for multipurpose Internet
Mail extensions type, which is a label used to identify the format
of the file or data. Applications or browsers
know how to handle it. If we upload PNG file, Mme type will be
image slash PNG, or if we upload PDF file, then Mme type will be
application slash PDF like this. If Mme type is available
in the allowed type, then we call the CB
function, and first, we pass null for error and pass through
for except the file. Now, if M type is something
else, then these types, then we adhere as condition
and call Cb function, and at the error,
we pass new error, and here we pass error message, invalid file type, only JPEG, PNG, and GIF are allowed. And to reject this file, we add here false. D. Now we have to add here, file filter to file filter. Here, we solve both issues. Let me show you one more
useful property of ulter. Here in the multi function, we have limits which help
us to limit the file size. Imagine someone
bumstakally upload here 10 megabytes icon. We don't need 10
megabytes for small icon. We can limit its file size. Here we pass object file size. Here, we have to pass
file size in bytes. For our category icon, we can set limit of two MB, that's more than enough, and to convert it into bytes, we add into 10 to four. By this, we will get kilobytes
or KB and again into 104. By this, we will convert
that KB in bytes. And done. Now let's test this API. So go to Postman and
here we have already our data for laptops.
Send this request. See here we don't get response. But if we check our
uploads category folder, we get new file with
our unique name. Extension, our image is storing. Now let's check the error and
if we check our terminal, then here we can see we get duplicate key error
for name laptops. Remember, in the
previous lesson, we store the laptops
category with that random file name and
also in the category schema, we set name property to unique. We have to remove that
previous category from Mongo Di B Compass. Here, simply remove
this document. Also, we remove both
images from our server. We don't need it. Now we
again send the request. See, here we get
success message. We get good image name and also image saved
properly in our server. Great. Now let's add
some more categories. So second category
name is smartphones. And for that, we upload icon
of mobilephone dot png. Make sure you
remove first image. Otherwise, it will send to
images. Send the request. Next, we have category, smart watches, and icon we
select here watch dot png. And send it. Next, we
have headphones and icon to headphone dot PNG
and send the request. Last, we have gaming
consoles and for icon videogame dot PNG
and send it and done. Now in the next lesson,
you will create API for getting all categories
from our collection.
117. Getting all categories API: Let's create API for
getting all categories. It is really simple. After the post API, we create a new API, router dot Gate forward
slash for endpoint, callback function with request
response, arrow function. And inside this function, first, we get all
categories from the model. So C categories is equal to
a weight, category dot find. And if you want to send category
short by category name, then we add here shot method, and here we pass fill name
by which we want to shot, which is name and simply send response dot
JCN categories. Also, here we have to
make this function acing. Save the changes, and
let's taste this API. So go to the Postman
or tender client. We add here new request. Yet all categories,
in the endpoint, we write Local host
3,000 API category. Select the Get method and
simply send the request. See, here we get categories shorted by
their names. Pretty cool. Now if you need to add more APIs like update
or delete the category, then you can also do that. It really depends on you for
now we don't need it here, so I don't create them.
118. Sharing Static Images from the Server: Currently for categories data, we store the image in our
upload slush category folder. But how can our browser
access these images? They are available
only in our server. For accessing the images
from the browser, we have to share the static
image files from our server. Have already done this in the
Section five, Lesson four. It is really simple. So index dot js file here after express
dot J and middleware, we add app dot U. Here we had the prefix
of the static file path, so we set it to upload category. Now for sharing the static
files from the server, we have to use another built in middleware called
express dot static. And inside this function, we have to pass the folder
name which we want to share. Upload less category. Just remember this is
the prefix for URL, and this is the folder
path of our static files. Save the changes, and let's try to access this laptop image. Copy this image full
name. Now go to browser. Here in the URL, we write our backend, Local host, Column 3,000. Here we have to add the
prefix for static file, which is upload slash category. We pass here images category, then we have to also write Images category in browser URL. For now, we stick to Upload
category, and here also, we pass Upload category, and here we paste the
full name of the image. See, here we get the image. That's how using this URL, front end will
display these icons. Now in the next
lesson, we will create a new model for products.
This will be fun.
119. Exercise - Defining Products Model: Now it's time for
little exercise. You have to define
a product's model. In this model, we
have the fills, and this is the sample
data of single product. You have to design schema by
your own for these fills, take your time,
solve this exercise, and then what's the solution. I hope you solve this
exercise or try to solve it. Give yourself credit for that. Now let's solve this exercise. For product model, we
create a new file in the models folder
called products dot js. Good. First of all, cost mangos is equal
to require mangos. After that, we define cost product schema is equal
to Nu Mongoose dot schema. Inside this, we pass object
in which we define schema. First property is title, which we set to type to string, required to true, and also we adhere max
length to hundred. Now next description
which we want to set to type string required to true, and it's min length
to 50 characters. After that, we need seller
who list this product. Here, we store the seller ID, so object type to Mongoose dot schema dot Types
dot Object ID, ref to user required to true. Now here you might ask why we don't use here
hybrid approach? Why we don't embedded
data for seller? Why we store seller ID. Here on the front end, our products card
will look like this. In the list of products, we don't want to display
the details of seller. If we want to display the
seller information, here, then we can also add
here, seller name, and some required details, but that is not the case here. You have to think
like this when we want multiple data
in single API call, multiple data means t products
data in single API call. In that data, we need to display the reference
data or not. If in that data, we want
to show reference data, then it's better to embed
that data in the collection. By that, our API don't need to run populate query for
each embedded data. At that time, we can
use hybrid approach. After that, we need category. Now tell me which
approach we use here, embedded or reference. Right, we use your reference because we don't want to show category with multiple data. We just need category
for filtering products. So we again, use your
reference type to Mongos dot schema dot
types dot ObjectId. Ref to category and
required to true. Next, we have price. Here, we set type to number, required to true
and mean to zero. Obvious. Next, we duplicate this line and change
this price to stock, which is the number of products
available in the stock. Next, we have images, which is the array, and in this also, we store
image name as string. The reason we adde array because products can
have multiple images. As we required images to true. This will make sure our product
has at least one image. After that, we have
last property reviews, which is the array
of review object. Now this is interesting. In the reviews array, we want multiple reviews object. We have to define the
schema for that object. Here we simply pass
object, and in this, we can define schema
for that object, same as we are doing previously. In this object, we want three information user
who submit this review. So I object, we pass type, Mongos dot schema dot
types dot Object ID, f to user and required to True. Next, for the review,
we need rating, so it's type to number, and also we required to true
and mean value to zero. At last, we need command,
which can be string, but we don't add
here required to true because not all
users will add command. They just like to add stars. Now you might have
question when we add a new product,
is it compulsory? We need to add these reviews? Because here we
pass require two. So the answer is no. We don't need to add
reviews compulsory. This required for
this single object. If we try to enter
new review object, then it should have
user ID and its rating. Otherwise, it will not need it. Basically, we are adding
schema for that single object. Also, you might ask, can we embed users data here? Yes, we can embed data here, but imagine users
change the user name, then it will really hard
to stay consistent with that data and don't worry too much about
designing the database. Almost all companies have team for designing the database, or some senior developer
will design the database. I'm just explaining
you why we use reference and why we don't
use embedded approach. I think it will
clear your doubts. Here, our schema is complete. Now we can create
model from this. Cost product is equal
to Mongoose dot model. Here we pass the
singular name of our collection,
which is product, and after that, we
pass product schema, and at last, we
export this model. So module exports is
equal to product.
120. Role Based Authorization: Now let's build API for
creating new product. In the Routes folder, we create a new file called
products dot js. Now we have to create a
new router using Express. SecstEpress is equal
to require Express. Next, cost, Router is equal
to express dot Router. Also at last, we have
to export this router. Module dot exports
is equal to router. Now, let's add this new router
in the index dot js file. Save this file, go to
index dot js file. Here, we import cost product
routes is equal to require. We go to Routes folder
and in that products. Now at the bottom, we
add app dot g. Here, we define the prefix, which is API slash Products. And at the second parameter, we pass products out, save the changes, and back
to our products routes file. Here we want to create an
API for adding new product. So router dot post, API and point to forward slide. Here, we make sure only log
in user can access this API. So we can add here Osmalware. See here I get auto suggestion, and also it will auto input
Omdalware at the top. Now we add request response,
and arrow function. Good. Now before we handle
how to store products image, we have to check only
sellers can add product, not simple users, only sellers,
and how can we do that? We will use middleware. In the middleware folder, we create a new file
called cheseller dot js. Now, as we know,
middleware is a function. We define const, check seller is equal
to request response, and next and arrow function. In this function,
we have to check if the user rule
is seller or not. If it is not seller, then we return error. I request dot user
is not available, or request dot user dot role
is not equals to seller, then we return
response dot status, 403 for forbidden Xs dot JSON. And here we pass object with mesetPperty excess
denied sellers only. If you surpass this condition, only then we can pass them to the next middleware
or API callback. Now let's export this
function from here. So module dot exports is
equal to check seller. So the changes. Let's set this middleware after
the orth middleware in our products API. See, again, auto input works. Great. Now, here are
two things for you. First one is in our user schema, we only set two values
option for role. Let me show you open
users dot js file. Here we can see we have
only user and admin. We need to also add here, seller, and save this file. Now the second thing
is, how can we get user role in the request
dot user dot role? Because in the request dot user, we get only those data which we pass while
generating the token. Remember, we pass
only two properties underscore ID and name.
Let's see this also. W users dot js route here
in the sinnup at the end, C, we only pass
underscore ID and name. Here we have to also pass role. Roll to new user role. Good. Also in the login at the end, we also add here role
and here we have to use user dot role because we
already have user here. See the changes. Now what about Google and Facebook
based authentication. There also we pass only
underscore ID and name. We have to also pass the rule. Otherwise, our cellar meter
ware will give them error. So pothjs file where we
need to change here, we create this common
function. That's nice. See, at the top, we
are finding the user, so that's easy for us. Here at the bottom, we are generating the token, so we add here role
to user dot role. And done. We made
all changes here. Save the changes
and let me close other files in the products API, we simply return here response
dot SN seller is here. Let's test this API. First of all, let's
add login API in Postman because I don't
think for products also, we will use Tundar client. In our Cardwih collection, we add a new folder
called users. And we add here request
called login a user. First, we select and
point to API user login. Make sure this method
is set to post. Now for data, I simply copy it from the Thunder
client back to Postman, select here body, raw and simply the data here
and send the request. See, here I get fresh token, copy it and for tasting
the products API, you will create a new folder
in cartwdg called products. I think we have to permanently
switch to the Postman. When I started research
on the scores at the time File Upload feature is available in the free
version of Tender client. But now what we can do, we switch to Postman.
Sorry about this. Here we had request called
create a new product. Select the endpoint to
API slash products, request is post and
send the request. See, here we get error,
authorization token required. Sorry, we didn't pass token
in the header, go to headers. Here we add authorization,
and in the value, what we have to
pass first we add error space and we paste
our JWT token here. Now let's send this request. See, now we get
another error message, access denied sellers only. So to quick recap, we
add one middleware for seller after the Othmdalware
because by orthomdalware, we get the user's data
in request dot user. If token is verified and
user rule is seller, then only the seller
Moware function will allow us to enter
in the product's API. This is how we can perform
rule based authorization. To enter in this API, we have to make our current
account to seller's account. So when Mongo Di become pass here in the
user's collection, find your account
by which you are login and change
its rule to seller. Make sure seller, not sellers. Now we have to
generate a press API token because we
change our data. Send the login request copy this token back to products API. Here in the header, we replace the old token
with Barrer space new token. Now let's send the request. See, here we get seller
is here. Lovely.
121. Custom Role Based Authorization: Now previously, we
added categories API. Here, we didn't
check the role of the user for adding
new category. Here also we want. Only Admin can add categories. So there are two options for us. We can create a new middleware, same as this check
seller middleware, or we can create
a new middleware which checks any
role of the user. If in that middleware, we pass admin then it
should only allow Admins. We pass seller, then it
should only allow sellers. Both options are good. It really depends on you
which you want to choose. Let me show you both. For Admin, we simply copy whole code
from the check seller file, and in the middleware folder, we create a new file
called check admin dot gs, and simply paste this code. Now here we have to
do little changes. First, we change
this function name. Select this and press F
two and write check admin. After that, we have to change this condition, role to admin, and also in the error
message admin only, and we are ready with
our admin middleware. We can add this
middleware in our API after the Osmalware same
as we add check seller. Now let me show you how can we create general rule
check or middleware. It is really simple. In
the middleware folder, we create a new file
called check role dot js. Here also, we can simply paste the same code and change this function name
to check rule. Now you might ask, how can we
pass rule in this function? Because as we know, at
the place of middleware, we just have to
add this function and that we'll call
this callback function. Here we can do simply one thing. We can add here one
more arrow function. So after equal sign, add role parameter and
then arrow function. So before adding
this arrow function, for accessing this request
response callback, we have to call
Jack rule function. Now, after adding
this arrow function for accessing this request
response callback, we have to call
Jack rule function. Here, we pass the rule of
the user, let's say admin. And after that, we simply
call it one more time. And by that, we can access this request
response callback. This is called higher
order function. A higher order function is
a function that returns another function or takes
another function as argument. Here, we are returning this callback function in
this check rule function. In simple syntax, it looks
something like this. Here is the check rule function, and it simply return
this callback function. That's why we call
check rule first time, and then we have to call it again to access this
return function. I hope it clear your doubts. Here, we can replace this seller string
with rule parameter. And here, for custom
error message, we convert these
codes into back digs, and at the place of seller, we add dollar role
and that's it. Now we can use this custom
rule checker middleware with any type of role. So it really depends
on you if you like to use check seller and
check and mid middleware, or you like to use
check rule middleware. I like to use this
check rule middleware. In the products API at the
place of check seller, we have to call check
Rule middleware. And here we pass rule seller. This check rule seller is same as check seller middleware, both call this request
response callback. Now for the categories also
want to check role as admin. In the category route, we want to add middleware for this create new
category route. Now here is one
question. Why should we add check role middleware? Before this multer
upload or after it? Right, we add it before this multer upload method because first we check the rule. If it is admin, then we will store the
icon image in our server. We add Jack Roll middleware
after the ulter method, then it will first
store the image in our server and
then check the role. So we add check roll
middleware before this and pass here role admin. Also, before check
Roll middleware, we have to add Auth middleware
because without that, how can we get the
user information in the request dot user? Save the changes and let's
taste our products API. Send this request,
and here I get error. Let me check. It is error
in the check Role file. Oh, sorry, I forgot to
remove this function call. See the changes, and let's
send the request again. See, here we get seller is here. That means our
middleware is working. So that's how we can use this check rule middleware for any type of rule
based authorization.
122. Handling Multiple Images of Products: Before saving the actual
products data in the database, let's create multi upload method for storing multiple
products images. So here, seller can upload minimum one image or maximum
eight or ten images. So from the categories route, I simply copy this
storage file filter, and upload variables and paste it before
our products API. Good. Now, first of all, we need to import
Multar package. So cost ulter is equal
to require multer. Also, we can remove this jag seller middleware
import. We don't need it. Now here, we have to do
little bit of changes. First, we change the destination to upload slash products. By that, we can make sure products images are separated
from the category icon. And as we know, multer don't
create directory by itself, so we need to create it. In the upload folder, we create a new folder
called products. Here we will store
our products image. These are small details
which we need to keep in our mind while
working with nod Jas, as you work more in nod Jaz, you will know all these things. Don't worry about
that. Now, after that, file name is okay. File filter is also okay. If you want to
also store videos, then we have to
add these MM types in the allowed types array. For now, we don't want it, and also size limit is two MB, which is also okay. Here you might ask
this two MB limit is for all images combined size. No, it is for each image size, not for combined size. Now let's add this upload
method in this API. After jackrll middleware,
add upload dot, we want to store
multiple images, so we can use here array dot
single is for single file, and dot array is
for multiple files. Now here, we have to pass
fil name for these images. Here, we set it to images. So from the front end,
they have to send all products images in
the single images filled. We will see that when
we taste this API, now after passing
the field name, we can pass the
limit of the files. What are the maximum number
of files we want to store? For ecommerce application,
eight is more than enough, but you can also adjust
this count by your needs. Also, if you don't want
to set the maximum limit, then we don't need to pass
the second parameter. But here we want eight image. Now, as we have
seen, previously, we get the file information in the request dot file because
there have single file. But here we are dealing
with multiple files, so we will get
this files data in the request dot files plural. By that, we will fetch each
file name in database. Also, we can create here
common ultter function, but that will create
unnecessary confusion. That's why we leave it as it is. Now in the next
lesson, we will store the products data
in the database.
123. Create new Products: Let's store the product
details in the database. So first of all,
in this callback, we get all fields which we are going to pass in the
request dot body. So cost CLI packets is
equal to request dot body. Now, first of all, in
this body, we get title, description, category, which is the category ID,
price, and stock. Now after that, we have
to create images array, which is the array of image names, and how
can we get that? So we get all images details in the request dot files array, and each image data is available
in the object like this. And here we get a file name. So we can do this. Cost images is equal to
request dot files dot MAP. Here we get each image
object arrow function and we simply return
image dot file name. This filename is what we
generated at the top. Now by this map method, we get the array of image names. Also, sometimes sellers
forgot to add images. We can put here condition if images dot length
is equal to zero. If it is true, then
we simply return response dot status,
400 dot JSON. And here we pass error in
object with messed property. At least one image is required. Now here we have
all information. Let's quickly store
this information in the products collection. Cost, new product is
equal to new product. See, here we get authorization, products from model, good, and here we pass
new product object. In this object, title to
title, and as we know, if object property and
value name both are same, then we can simply remove this. Both works the same. Next,
description to description, category, price, stock, images. And now at last, we have to store the seller. How can we get that seller? Right, we can use request
dot user dot underscore ID because our Osmitalware is setting the usage details
in the request dot user. Let's count the fills to see if you are
missing fills or not. One, two, three, four, five, six and seven. The product schema, also, we have one, two,
three, four, five, six, seven, and 80s reviews, which we will not add now, so we are going on
the right track. Now, let's save this data. Await new product dot
c for using await, we have to make this function
is now at the bottom, let's return response dot
status to 01 dot GSN, and here we send
new products data. Save the changes,
less is this API. This is really exciting. In the resources folder, I added project to folder, and in that, you will
get Products folder, and inside that, I added
products images by category, and also you will get
the data dot GSN file. In which I added title, description, and
all other details. Open this data dot
JSNFle in the VS code. See here I added category
name at the top, and in the products array, I added all products data
with title, description, price, stock, image is name, so you can search image by that. The reason why I don't add
here category ID because for each category ID can be
different because previously, we added category manually. Here is my smartphone
category and here it's ID. You can compare it with your category ID.
It can't be same. Copy this ID, go to Postman, here, first go to form data. Here, we simply
add key category, and in value past the
category ID of smartphones. Next, we have to add title, then description,
price, and stock. I copy these all details one by one from the JSON file
and paste them here. First, select title, paste
it here, description. Price is 1299 and
stock is eight. So pas the description, price 1299, which is
USD and stock to eight. Now we need to add only
images for this product. At the bottom, we add
another key images. Here, we select type to file, and now we select our
iPhone 14 images. So go to Products folder, smartphones, and we go
to iPhone 14 Pro folder. Here, select all images 1-4. Now let's send this data. See, here we get this
new products data. Here we have seller category, image names in array, and also in our back end in
the upload products folder, we get these images. We did a great job. I am adding all these products
details in my database. When we run the GD product
query with pagination, then we get more data. You can also add all products in your database if you want to. Otherwise, you can
simply skip this part. Now I copy the next product
title, paste it here. Don't touch the category ID because we are still
in the same category. Description. Paste it here and price is 399 and stock to 50. Now let's select images
for Samsung a 54. Send this request.
Good. Like this, you can add all products
or at least ten products. Just change the category ID when you change the
category of the product. I know this is boring, but we can use this data to
taste our get products API. So put some chilling music
and create these products. I am also doing this, see
you in the next lesson.
124. Getting all Products Data: So in the previous lesson, we added products
in the database. See, I added all 24
products in the database. Now let's get these
products through API because that's how front
ten will get this data. So after the post API, we add router dot Get and
point to forward slash. Now here we don't
add any middleware because we want any user
can see all products, not only logged in users. So we directly add here final callback with
request and response. Good. Now here, first
we get all products, so const products is equal
to products dot find. We don't pass here any condition because we want all products. And here we have to add
await for the query. And for await, we add here
async and at the end, simply return response
dot JcNPducts. S the changes let's test
this implementation. In the postman, we create a new request in products
collection, G A products. Here, we enter the
endpoint to APIs Products, select Method to G and
simply send the request. See here we get all
products data in array. And in that array, we get object for each product
with all fields. Now my question is, do we
need all details here? Can we reduce size of the data we are sending
from the back end? If we hover over this size, we get the response size with
header and body separate. Notice the current
data size 14.39 KB. Now let me show you how our products page should
look like in front end. See, here we have the
list of all products, and it is almost similar to Amazon or any
ecommerce application. If you pay attention
to the products card, we don't really
need all details. Let's write the fills which we want to display in
the products card. First one is ID for identify
title of the product price, stock if products is in a stock, only then we display
head to card button. Next, of course, display image, which will be the first
image from the images array. Next we need details about
reviews, and that's all. In our query, we add select method for
selecting the fills. Now, instead of adding
all fills name, we can write which
fills we don't want. For that, we just add or
minus before that field name. We don't need description. Also, we don't want
to display seller. Also not category, and we don't want underscore underscore
V. It is not necessary. Here's the changes
and take a look. Let's send the same request again and let's check the size. See, it is reduced by one third because description has more
data than any other field, and that's why we remove
unnecessary fills. Now let's make this
data more narrow. Here we also don't
want all images. We just want first image
to display on the cover. Now there are two
solutions for that. First one is we can
create a separate fill in our products model like
display image or cover image. When we are storing the new
product, then in that field, we will store the
first image name. That's one solution. Another solution is we can
extract first image name from each product and add it in the same product object which we are sending
from the back end. By this way, we don't have
data duplication problem. See both solutions are great. Here, we can't apply the first solution because
we added all products. I will show you the second
solution, which is also great. First of all, here we
write products dot Map. Now here we are getting single product object,
and arrow function. Now as we know, whatever we
return from this function, it will be the item
of the new array. The logic is we will return all data which we have
in single product, and then we don't want to
send the images array, so we will replace that images property with the
images first element. We want images array, when we don't add a new
property with the same name, we can make it display
image or something else, but here we don't want
to send images array. That's why we are
giving the same name. Let me show you. Here
we return object. Inside this, first of all, we will add all properties from the single product
wide spread operator. We add all properties of
single product as it is. After that, for replacing
the images property, we add here images
and here as value, we set product dot Images, first element, which
is index zero. Done. Now, this map
method will return a new array with all updated
single product object. We store it in variable
called updated products. At the place of products, we simply return updated
products in response dot JSO. Save the changes, and let's see we are getting
this correctly or not. See, here we are getting some weird object and at
the end of each object, we are getting this
images property, which means our map
method is working. But why we are getting
this weird object. Let's simply const
dot log this products array and Al in the
response dot json, we again send products object. Save the changes, and
let's send the request. See, here we get the regular products array
and back to VS code, open terminal, and see here we are getting the
same products array. Now let's try to move this console line in the map method. And at the place of products, we console dot log
the single product. Save the changes, and
let's send the request. A terminal, S, here we get again the regular
single product object. I think when we are
adding spread operator, that when we are getting
that object properties, instead of single product, we wrap it in the object and
add here spread operator. S the changes and send the
request. Back to VSCode. See, here we are getting
this weird object. What is happening here. So this happen when we are
getting data with mangos, we don't get simple
Javascript object. We get Mongoose document object, which includes both the raw
document data in the dog and additional properties
and methods like dollar underscore
underscore, dollar is new provided by mongoose for
managing the document. Now the problem is we don't want those properties
and methods. Want simple JavaScript
object from the database, not mangos document object. To solve this, we simply add an property at the
end of this query. This removes all the extra
mangos specific properties. Now we can return here updated
products in the response. Save the changes
and take a look. Send this request and see, here we replace images array
with the first image name, and if we check our
data size, reduce more. Lovely. That's how we
debug and find the error. Now here is the one thing also. Here we are sending
full reviews array in that we have object
who has usage details, the rating, and also comment. Currently, for all products, we don't have any reviews. That's why we can't
see that large data. Also on front end, we just want to
show rating average and total number of rating. We can also replace the reviews array with only two properties. Let me show you. We remove this console. We don't need it. First of all, create a new
variable cost number of reviews is equal to
product dot rev dot LNT. Here, Jack, you have
reviews or review. I have review, so I stick
with product dot review. Now, how can we find average? Average is basically,
we have to sum all rating numbers and divide it with total
number of reviews. We define a new
variable Cast sum of ratings is equal to
now to sum the ratings, we use reduce, which
is array method. Badu dot review, which
is array dot reduce. Now here we get two parameters. First one is total, or we can say sum, and second one is current value, which is single review. And what do you want to
do in this callback? Simply sum plus
review dot reading. After that, in the
second parameter, you can set the default
value of this sum, which we set to zero. This reduced method will
take two arguments. First one is callback function, which we use to do some
mathematic operations, and second one is the default
value of the sum parameter. Now we have sum of ratings, cost average rating is
equal to sum of rating, divided by number of reviews. Now, here's one thing. If number of reviews
are zero, then what? 0/0 will give us undefined. If number of reviews is zero, AsiR or operator one. This will prevent that error. Now let's replace review array
by these two properties. After images, we return review to object, number of reviews, to number of reviews,
or to simplify, we remove the second property, average rating to
average rating. Save the changes
and take a look. Send the request again and see now we can review as object. So from the query, we just send what
front end needs. So that's how we have to think when we are sending
data from the back end.
125. Pagination or Infinite Query: Now currently we are sending all 24 products
from the database. Imagine if in our database, we have 100 or
thousand products, then sending all products in single response will
make our API slow. Tell me what should we
do in this situation? Right, we can send
data page by page, so our front end can implement pagination or infinite
scrolling features. We have already seen
this in Section seven. Let's implement pagination
or infinite scrolling query. Backend is same
for both features. First of all, we will get the current page number
from the query parameter, and how can we access it? Right, by request dot query. Cost page is equal to
request query dot page. Now, this request dot
query dot page is string because
whatever we get from the query parameter,
it will be string. We have to convert this
page into integer, we wrap it with parenthesis
and parse integer. Next, we have to define
how many number of products data we want
to send in one page. So const per page
is equal to eight. Now we have to simply add
two methods in our query. First one is dot Skip
and here we pass parenthesis page
minus one per page. If page is one, then one minus one,
zero into eight, which is also zero. Our query skip zero products. If page is two, then two minus one, which is one into
eight, which is eight. So our query Skip
first eight products. Now, after Skip, we have
to add Limit property, and in that, we pass per page. So limit is how many data we want to fetch
from the database. Simple as that. Let's test this implementation,
send the request. See, we only get eight products. Now you might ask, we didn't pass page in the
query parameter. Still we are getting
eight products. Yes, we are getting
eight products because this request dot query
dot page is not defined. So the skip method
will skip zero items, and then we set limit to eight. So that's why we are
getting eight products. What if we want to make this query more
customized for front end? Imagine our front end want to ascend per page products
in query parameter. Suppose ten products
on single page. We can do something like this. Instead of this hard coded Ed, we get request query dot
perpge query parameter. Again, we convert it into
integer using parse integer. Even if front end don't want
to pass per page parameter, pass here default value,
so operator eight. And we can handle
this page by adding operator and default
value will be one. See the changes, and let's
get data of page two. So we question mark at the end of the URL for
passing the query parameter. Page is equal to two. And also, we pass per page
to ten and send the request. See, our query, skip first ten records and
then send page two, which is 11 to 20 products. Now here is one thing. On the front end,
many times they need more details like total
number of products. By that, they will decide
how many pages they have to display or how many times they can get the data
from the back end. So they want current page
number and some other details. Let's quickly get these details and pass them in the response. First of all, after
these update products, we can do something like this. Cost, total products is equal to await product
dot cow documents. This will count all products. Now after that, we can also count a total number
of page, cost, total pages is equal
to total products, divide by per page. Suppose we have 50 products
and per page is eight, then we get 6.25, which means full six pages, and we need one more page
for display two products. So it's better, we
always seal this number. For that, we have to just wrap this equation with
math dot SL function. This will convert
this 6.25 to seven. Now at the place of sending
this updated products array, we pass here object. First properties products
to updated products, total products, to
total products, or we can also remove this. Also, we pass total pages. Also, we can send page, which is current page to page
and post per page per page. I added here all information, but you can add it
according to your needs. Save the changes, and let's
send the same request. See, we get products array and with that
we get these details. That's all we have to do for pagination or infinite query.
126. Sending Products By Category: From getting products,
we might want to get only products by category. For that, we don't need
to create new API. We can simply implement
that in this query also. First, we get the category
name in the query parameter, cost query category is equal to request query dot category. If we don't pass category, then we pass here or null. Now, after that, for query, we add variable let query. We set it to empty object. Here, we pass this query
object in this fine method. Now we can fill this query
according to our needs. This query is the
comparison object. We pass here if condition
query category is available, then first have to add category property to category
ID in this query object. Now, if from the front end, we directly get category ID, then we can directly pass
that ID in the query object. But most of the time we get category name from
the front end. We have to find category
ID from our database. Cost category is equal to a
weight category dot Fine Vn. Here we pass comparison object
name to query category. Make sure we import this
category model at the top. So cost category is
equal to require. Here we go one folder
up models category. Now, what if we don't
fund the category? Here we check if category
is not available. We simply return response
with status code 404, not found in Jason method, pass object with message
property, category, not found. The end, we simply set query category is equal to
category dot underscore ID. Now our query object
looks like this. If we pass that in
our fine method, then it will act as
comparison object. Now, let's test this
from Mongoi become pass, go to category collection,
copy any category. Suppose I copy the
smartwatches category. Now back to Postman in the URL, we add another query parameter and category is equal
to smart watches. Make sure you write the
same name as we have in the categories collection
and send this request. And here we get empty array. Oh, here we pass page
is equal to two, and in this category, we don't have that much data which we can show
up on the page too. So we change this page to
one and send the request. See, here we get
all watch products.
127. Sending Product By Search: In our application,
user can also search the products and want to see all products according
to their search. We can also implement that
in this single query. It is not compulsory to add
all features in this query, but I'm showing you the
real world practice. After this category, cost
query search is equal to request query dot SRG
or we set it to Null. After this category condition, we add another if condition, query search is available then we set another property
in the query object. Query dot title is
equal to object. Here we add dollar jx, to query surge and to make
this query case insensitive, we pass another property, dollar options to string I. Also, at the bottom, in this tutor products, we have to pass the query object in the count document method. I forgot to pass it in the previous lesson,
and that's it. Save the changes
and take a look. Add here another
query parameter, and surge is equal to here we add app and
send the request. See, we get only one data because in its
title, it has Apple. That's how we can customize this Gtquery for
getting all products. Also, I notice one thing. We successfully store
our products images in our server folder, but we can't access it. We need to set
those static files like we did for
categories images. In the index dot js file, after this category static, we add app dot ug. Here, we add the prefix
for the static file path. We pass uploads products. Now, after that, we use
Express dot static Middleware for sharing the
static files from the serfer and inside
this function, we have to pass the folder
name which we want to share. So uploads products. Remember, this is
prefix for URL, and this is the folder path
of our static files and done.
128. Exercise - Getting Single Product Data: Now when on the front end, someone clicks on
the products card, we should show them all details about that single product. Here is the exercise for you. You have to define a new API for getting single
product details. So our API should
look like this. Slash API products,
slash product ID. This ID, you have to
find the product. Also, try to get seller
actual data like seller's ID, seller's name and email ID, not simple seller ID. Try to solve it and if you
forgot about populate, then you can watch six
and seven populate lesson and then try to solve it. So I hope you solve this
exercise or try to solve that. Don't worry if you get error
or stuck at some point. It is the part of learning. I also used to stuck a lot where I learned
node first time. Don't worry about that. Now
let's see the solution. So we write router dot Gt. What do we pass in the URL? How can we know which
product details we want? Right. Here we add column
ID as root parameter, and this is the unique
ID of single product. Now, again, we don't
add here middleware because any user can see
single product details. We directly pass
callback function with request and response. First of all, we need this ID, so Const ID is equal to
request dot VMs dot ID. Now by this ID, we can
find this product. So C product is equal to
await product dot find by ID. Here we pass product ID. Also we make this
function async. After that, we can put condition I product is not available, then we return response with status code 404 dot JSNObject with message property,
product, not found. Now, if we get the product
from the database, then we simply response
dot Json this product. Say the changes, and
let's test this API. Copy this first product
ID because we need it. In the postman, we create a new request called
Get single product. Good, select the
URL API product. And here we paste
our product ID. Make sure the request is
G and send the request. See, here we get the full
single product data. Now if we pass here some random
ID and send the request, then we don't get the data. And if we check our terminal, see our application got crashed. It happens because we don't
handle error in our API. Don't worry, we will do
that in the next section. Currently, we have to restart this application with node mod. Let's again pass original
ID and send the request. Nice. Now here we
have little issue. In the single products page, we want to show the
seller's information like the name and email. But here we get seller ID, so we have to populate that data from the
user's collection. Also, we don't want
this category filled. Other things are okay. First of all, after fine method, we pass dot populate. At first parameter, we pass which field we
want to populate. Right, it is seller, in which data we
want to populate underscore ID, name email. Now, also, we want to
populate the user, which will be available
in the reviews array. Here we have to
populate NASD data. It is also really simple. We pass another populate method. At first parameter,
we add review, which is the array
for accessing user, we have to write dot user. Now at the second parameter, we again want the same fills. Underscore ID, name and email. Good. Save the changes and
take a look. Send the request. See, now we get Seller's
Object with ID, name and email filled. Currently, we don't
have reviews. That's why we can't see it. Also, we forgot to remove
this category fill. At the end, we pass
dot select Method and here minus category and minus
underscore underscore ID. So the changes and take a look. Send this request, C, we remove category and underscore
underscore V property. That's how simple node js is. At the beginning, you
feel it is tough. But if you do step by
step implementation, then you can master it properly. And that's why I explain
you code line by line. Now in the next
lesson, we will create API for deleting
the single product.
129. Exercise. - Deleting the Product: Now let's create API for
deleting the single product. So here we write
router dot delete. Here, we again get ID
as route parameter. Now here we need
middleware or not. Right, we need
middleware because only logged in user can
delete the product, and also we want to get
details about that user. First of all, we pass
oath middleware. Then we pass API
callback function with request and response. Now, first of all, we get product ID from the
route parameter. Cost, product ID is equal to
request dot params dot ID. Now we have to find the
product from this ID. Cast product is equal to await product dot find by ID in
that we pass product ID. Now from this query we
only need seller's ID. Add select method, and here we pass sellers filled
and also at the top, make this function acing. Now we put here condition
for product is found or not. So we simply copy
this condition from the previous API
and paste it here. Good. Now if we found product, then we check user is admin or user is the
seller of that product. We write I request
dot user dot rule is equal to admin or
request dot user, dot underscore ID is equal
to product, dot seller. If any of this
condition is true, then we can delete this product. Now the question is, how
can we remove this product? In this product object, which we get from the database, Mongoose also pass
one method with it, which is dot delete one. And that's it. By this, product will remove
from the database. Also, we use here
delete one method because previously we get the
product from the database. If we have to directly
remove that product, then we use find by
ID and delete method. Also, we have to add here a wait because this is
asynchronous operation. Now, after that,
we simply return response dot Json with message property, product
deleted successfully. Now currently, we comment out this delete logic to check
this implementation. We will enable it
after some time. Now, if this condition
is not true, then we return here response
with status code 403, 44 Biden dot Json with mesa
property, excess denied. Only admin or seller can
delete this product. Now let's taste this
implementation, copy any product ID
from our products list. I copy this last ID, now in the post win, create
a new request called tilt, a product in this
method to delete, here we write URL, API products, and here we pass our product ID and make sure method is delete
and send the request. Oh sorry, we need to also
send JWT token in header, go to Login API, login with your user data, who is the seller
of that product. See, here I get JWT
key, copy that, and in delete API, we go to headers and add
here authorization in key. As value, we pass error, space, and paste JWT key here. Now, send the request. See, here we get access denied, so we don't get into
this I condition. That's why we get this error. The problem is in
this condition, let's simply consol dot
log this booth condition. Copy this first condition
and paste it here, comma, copy the second
condition and paste it here. Say the changes, and
again, send the request. Now back to VS code, opened a terminal here
for the first condition, we get false, which is true
because we are not admin. After that, for a
second condition, we also get false. Why this account is
seller of this product. Select separately consult dot
log, these two properties. Remove this first condition, and at the place of
equals, we add comma. See the changes,
and let's again, send the same request
back to VS code, and in the terminal, see, first we get object
ID in string, and then we get new object ID, which is Mongoose object ID. That's why the string and
object ID are not matching. So either we have to
convert this both into object ID or we can convert
this both into string. Both will work. So we convert
these both into string. In JavaScript, we have dot two string method to
convert data into string. Pass that dot two string for both IDs and remove
this console. Save the changes, and let's
send the request again. See, now we get product
deleted successfully. Now, if you want, you can
enable this delete one method. Now, after we delete the
product from the database, it's better to also delete those products images
from the server. For deleting the file
from the server, we can use FS module. So at the top, Fs is equal
to require Fs promises. And we also need path module. So const path is equal
to require Path module. Now at the bottom, after
this delete one method, we add fs unlin. Now in this method,
we have to pass the full path of the file
which we want to remove. But in the images array, we only have file name, but we need here full path. For that, we need
to use Path module, which is another built
in module of node jazz. Let me show you that
Const full path is equal to path dot join. First, we pass and disco
and Discord their name, which is the whole directory
path of our project. Next, we write our folder in
which we store the images. Here we go one folder up because currently we
are in the routes folder, upload slash products, and
then at the third argument, we add our image name. Now here we don't have
only single image. We have arrays of image, so we have to run loop for this. Before this, we add
product dot Images. So for accessing images, here in the select method, we have to pass images. Good. Now product dot
images dot for each. Here, we get each image name, arrow function, and in that, we will move these two lines. Now, here in the fs dot unlink, we pass this full path, and this as dot unlink is
an asynchronous operation. So we have to adhere a
weight, and for that, we have to make this color
function async. Good. Now before running this page, it's better to check we
have images array or not. I product dot
Images is available and product dot images dot
length, greater than zero. Only then we run this loop. Move this code here. In this unlink operation,
error might happen. Before this fs dot unlink, we add try and cache blog. Move this unlink
method in the tr Blog, and in the cache blog,
we get this exception. We simply consult dot error
in metics, error, deleting, file, dollar C brackets, full path, comma,
add error object. See the changes and take a look. Let's test this implementation, copy the ID of the last product. Also see its image name. These images should be deleted. Replace this product ID with this ID and send
this delete request. See, here we get product
deleted successfully, and if we check our
products folder, see in this folder, we don't get those images. That's how we delete
product and its images. Now let's also add that
product which we just deleted. So and create a new
product API and here I change this title
to new product name. Select here images. And simply send this request. Here, I get valid token because this
previous one is expired. We go to login API, copy the token, and
in the headers, we paste this token. Now, let's send this request. See, here we get new data. Lovely. See you in
the next lesson.
130. Searching Product by Title [OPTIONAL]: Now on all ecommerce
application, we have the searching feature where at the bottom
of the search bar, we can display suggestion. So we have to define
API for that. Also, this API will call when each character
enter in the search bar. So outer dot cat, endpoint two slash suggestions, async callback function
with request and response. In this, first of all, we get what user is searching. So cost search is equal to request dot query
dot Sarge here, we have to use regular
expression for comparing and finding
the string in the title. So Const products is
equal to await product dot find passier condition
Object, title to object. Here, we use dollar regex, which is the latest way to write regular expression in Mongo Dib. Here we pass our search text and to disable case sensitive, we pass here dollar
options to string I. Here, we compare our search
string with our title. This will give us products which have that word or string. Also, in the sucesion, we don't want to
show all details like description and all. So we can add here
Selec method and we only get underscore
ID and title property. Also we can limit this data
to ten on the front end, we only suggest ten products at the bottom of the search bar. At the end, we simply response
dot Json these products. Save the changes, and
let's taste this API. Open Postman, create a new request called
Get suggestions. URL to API products,
slash suggestions, question mark for
passing query parameter, search is equal to IP. Make sure we select the Get
method and send the request. See, here we don't get products. Let's see what we
get in the terminal. See, here we are getting
some issues with object ID. But in our suggestion API, we don't have any object ID. So why we are getting
here, object ID error. Actually, this API call is not reaching to
the suggestions API. It is moving inside this
get single product API. Let me explain to you this. Here is the first API URL
for the single product. We have API products
slash product ID. We add another API API
products, slash suggestions. Now Express is getting confused. This suggestion string
is object ID or what. Because of that, our single
product API is running. What is the solution here? Nothing, we just have
to move our suggestions API before this
single product API. By that, Express compare our
API with suggestion string, and if that doesn't match, only then Express will move
to the single product API. Phase changes and make sure
server is running properly. In the postman, let's
send the same request. See, now we get
product suggestions. I intentionally created
this error to show you what can happen when you
create project by your own. Now in the next
section, we will learn how to handle errors
like professional. C in the next section.
131. Section 11 - Why we handle errors?: Currently we are running our application in
the ideal world. Everything is working perfectly. But in the real world,
anything can go wrong. Any error can occur. For example, some file isn't found or our connection
with Mongo Deb server doesn't get succeed
or user don't pass the valid information or
anything can go wrong. In those cases, we
have to handle errors, and here are some reasons
for handling error. First of all, when
we handle errors, we can send friendly error
message and we can display that friendly error on the web page like
server has some issues, please try again
later, like this. Second reason for
handling errors, we can log or in simple words, we can store the errors
in the separate file, and then look those errors, which errors are
happening very often, and by that, we can solve that
errors in our application. So handling errors can also
improve our application. Now let me show you one error. Let's run our application
using nodemon index dot js. Now in the postman, we are also getting
suggestions by Sarge. Now suppose Mongoib
server get crashed. So to demonstrate,
we can comment out the connection code from
the index dot js file. Save the changes, and let's
go back to the postman. Send the same request again. See here we get loading,
loading, and loading. And after 10 seconds,
we get error. And if we check our
application terminal, see here we get Mongoose error operation products dot
Find Buffering Time out after 10,000 milliseconds. And after that, our
application might get crashed. Suppose in the real world, our Mongo Deb server gets off
for even two to 3 minutes, then our app can
crash in production. After some time, Mongo Di
B server again comes live. Even if it is live, our app will stay crashed and we can't
send data to the client, it's important to handle
these types of error. Also, handling errors doesn't
mean error not happen. Handling errors
means by that error, our server will not get crashed. Currently, this
application is running, but before this version
of node Jaz previously, node applications
are getting crashed. During this section, we will see how to handle
errors and log them.
132. Handling Rejected Promises: First of all, let's
handle errors which happens when we are
dealing with API promises. In this suggestions API, we call here this
product collection. And as we know, this is
asynchronous operation, that's why we use await
for the response. So we have here promise and
that promise gets rejected. But here we didn't handle errors in the
dry and catch block, which we learn in section six. Remember, so here at
try and catch block, here in the cache blog, we get error object. Now, what do we want to
do in this cache blog? In the cache blog in real world, we first log the error or exception somewhere in the
file or in the database, and after that, we will return response with relevant status
code and error message. Currently, we are just logging
the error in the console. In future, we will save that error or exception
in separate file. Now, after logging the error, we return response
with status code 500, which is server error. After that, we also
return JSON object with message property called
internal server error. Or you can write something
went wrong on the server. We can write any error
message we want. Good. Now we can move our
whole code in this dry blog. Let's see what happens, see the changes, and here our
application gets restart. But still, our Mongoib
database is not connected. Now let's send the request for suggestions. Let it loading. After 10 seconds, see, here we get the response with 500 status code and
with our error message. Also, let's see what
we get in the console. See, here we get this mongoose
error, same as before, and also our application don't
get crashed by this error. Still, our application
is running, we successfully handle our
rejected promise error. Here we don't consult
or log this error, then in our terminal, we
don't even know we got error. Now, if we try to
access our other APIs, for example, we send this
all products request. After 10 seconds,
we get here error, and in the terminal, our
application don't get crashed. So as we can see
how we can handle promise rejection error by
using try and catch block. By this way, our application
don't get crashed, and that's why handling
error is important. For all routes, we have to wrap our whole code with
try and cache block. But here in our application, we have almost ten
to 15 API routes. We have to wrap all
our API route code in try and catch Blog,
which is repeating. Also, if one day
we want to change the error message or
change the logging logic, then we have to update
it in all our routes. Now you might ask, is there
any shortcut for that? Yes, there is shortcut and we will see that
in the next lesson.
133. Create Error Middleware: In the previous lesson,
we seen we have to repeat this logging error and returning the error response
in every API route. In this lesson, we will create a common middleware in which we will write code for this logging error and
returning error response. Don't worry, it
is really simple. First of all, in our index dot gs file at the bottom,
after all routes, we add app dot g here
we pass function, and as we know, here we
get three parameters, request response,
and next function. Now in this function, we will write all logic
for error handling. First of all, we add
here console dot log. Error, middleware is running to just make sure
this is working. Now back to our route, cut this code inside
the cache block and simply paste it in our
new middleware function. Now if we want to change
the error message or any logic related
to error handling, then we have to do
changes here at the single place. Now
here is one thing. How can we get this error
object in this function? We get this error object as first parameter in this
callback function. Make sure we get the error
object as first parameter. Now let's see how can we
call this middleware? So vector out and here in the cache block for calling the next middleware,
what we have to do. Right, we can use next function. Here, we get next function as parameter and simply call the next function
in the cache block, and we pass error object
in this next function. If you are a little confused, then let me show
you the code flow of this error middleware. As we know in our back end, we are only running single file, which is this index dot js. So node, start
running this code. First, this middleware
and static code will run. After that, we have all routes and after that
add error middleware. Now when we run next function
in any of these routes, that next function will
run this error middleware. In all routes, we will
call next function, and as the first argument, we pass error object
from the cache block. If in our triplog
something goes wrong, this case method
will run and that cache method called
this next function, and that will run this
error middleware function. Simple as that. See the changes, and let's check this
is working or not. Currently, our
application is working, good, open postman, and send
the suggestions request. And after 10 seconds, we get error, and
in our terminal, we get ramiddalware is running, which means our
ramiddleware is working. And after that, we get
Mongoose error, great. So if we want to change anything in routes
error handling, then we have to do changes
at only the single place.
134. Remove try catch blocks: Now our current
implementation is good. If you're okay with this, then
you can use this approach. But a lot of developers
don't like this approach. You can see we have here
try and cache block, and we have to repeat
this try cache block in every route handler
or callback function, which looks a little bit messy. In the ideal world, we should
write only this logic. So how can we do this?
It is really simple. I just check this
Express fi update. In this Express five version, express automatically handle
promise rejection error or these errors which happen
during the Async operation. Express automatically call this next middleware
with error object, which will run our
global error middleware. Let me show you that.
So now here we don't need this dry and cache
blog. We can remove it. So here we are back to our
original route syntax. Save this file, and
let's check this. Open postman and send
the same cugion request. After 10 seconds,
we get this error. And if we check our VS
code terminal, see, here we first get error
middleware is running, and then we get our error, which means our global error
middleware is running. This is pretty cool, right? In the older version,
like Express four, this automatically error
handling is not working. In that, we have to wrap every route logic in
dry and cache block, but now we don't
need to do that. Express does this automatically, we have to define
global error middleware in the index dot js
file. Simple as that.
135. Log errors in file: So in this lesson, we will log our error messages in
separate log file like this. It is really interesting.
Let's do this. So currently, we are only logging the error
message in the console. Now it is time to store that error messages
in a separate file. So in future, we can see the errors which
happening frequently, we can encounter it and
improve our application. So for logging the
errors in a file, use another NPM package,
which is Winst. This is one of the
most popular library for logging the errors, and it makes it very
simple as well. So open up terminal
and write NPM, install WinSternF using
the exact same version, we write at direct 3.17
0.0 and hit Enter. Now configure the Winston
is really simple. First of all, we
import Winston, const, Winston is equal to require and we pass our
package name Winston. Now, this Winston by default, give us a logger. This logger is sufficient for small and medium
types of application. So we can customize
this logger as we want for large and complex
types of applications. Now this Winston or
logger has transport. This transport is like a
delivery vehicle for your logs. It decide where
the log should go. A transport takes the
logger messages created by Winston and send them
to a specific destination. Now the destination
could be Console which prints the log to the
terminal or command line. Next, we have file for saving the logs into a
file on our system, SDTP for sending the
log to an API and next, we have database for storing the logs into a
database like Mongo DB. At last we have cloud
services for sending logs to services like AWS,
Datadog, et cetera. These are all transports
provided by Winston. Let me show you some of them. This Winston package by default, use Consult transport for
printing the logs on terminal, but here we want to also save
logs in the separate file, and for that, we have
to configure it. So here we write Winston
dot Create Logger. Now in this function, have to pass
configuration object, or we can say what we
want to customize. Now the first
configuration is level. This level property define which types of messages
we want to store or log. For example, we want
to only store errors, or we want to log
warnings and errors, both, or we want to log
all types of messages. So in Minston we have
many levels of logs. First one is error, which is the highest level
of log for serious problems. Example, database
connection failure. Next, we have war for warnings, info for informational
messages like server running on port or one
GTB connected, et cetera. Next, we have SDDP
verbs debug, Ci. This is the maximum to
minimum level of logs. Error is the highest level and CLI is the lowest level of logs. Now, if in the label, we pass info we get log messages of info
upper level of that, which is warning and also
we get log of errors. If in label, we pass silly, then we get all messages
of upper levels. For better practice,
we pass here info because we don't want to store silly messages in our log file. Now after the label,
we have transports, and here we have to pass all transports which we
want to add in array. In simple words, where we
want to send our logs. We want to show it in the console or we want
to store it in any file. So first of all, for the
info warning and errors, we want to show logs
in the console. So we add here new
Winstn dot transports. Make sure it is transports, not transport and dot Console. For now, let's only console it. I just a minute, we will store these logs
in separate file. Now to use this logger, we store it in a
variable called logger. And now we can use
this logger in our application. But
here is one thing. How can this logger know which level of message
we are sending? Is it an info or warning
or error? Which one? Suppose here at the bottom, we have this simple console dot log server running message. This is an information. So at the place of using console dot log,
we can use logger, which we just created, and this logger has all
methods according to levels. So if we want to send
message as info, then we use here
logger dot info. If we have warning, then we use logger dot one. Now we want logger dot info, and let's see we are
getting this log in Console or not. See
where the changes. And if we check our terminal, see here we get this
object level to Info and message server
listening on port 3,000. Great. Now, this is not a
good format for the log. In the real world, we don't
need only label and message. We need much more
information about the log like
timestamps, et cetera. So in the configuration, we have one more
property called format. Here we define how
log messages appear. Here we write winston
dot format, dot Combine. And in this, we can pass
some Winston's format. Like we add Winston dot format, dot T Stem, and after that, winston dot format dot JSON. Don't worry. We have to set
up Winston one time only. After that, we just use this logger to print
and send logs. Share the changes
and take a look. See, now we get timestamp also. Now in our error
middleware, here, see, we use console dot
log for this error object. So we can use here now,
logger dot message. We have to pass
the error message, which is error dot message, and we also send the
whole error object. Let's see what we get. Save the changes, and let's
taste this back to Postman. And we send here get
suggestion request. Now after ten second, back to Vas code, open terminal. See, here we get level to error message to
the error message, and in the stack property, we get the whole error object, which we pass in the
logger dot error method, and at the end,
we get time stem. Now let's make this
log more advanced. Here we are just getting the
log message and log stack, but here we don't get
which route caused this error or which method
created this error. We want to add that in our log. That will give us
specific information. It is really simple. So in the logger dot error, at the place of this
second argument, we can pass object. In this object, we can define which are the other
properties we want to show. So first, we add stack
to error dot stack. Next, we want the API method, so method to request dot method. Next, we want path to
request dot original URL. Make sure here you write
the right property name. Also, we can order
these properties. Suppose we want to show
the stag at the end, and first, we want
method and then path. So in this object, we can also set the order
of properties. But for that, in our format, we have to add this JSN format. Otherwise, it will not work. Save the ins and take a look. Send the request again from the suggestions after 10 seconds back to VS code in the terminal. See, here we get
error message method to get path to our API, which caused this error, full stack of this error and
timestamp of this error. Lovely. So we are done with
the formatting of our log. Now, let's store the log
in the separate file, not only in Console. So here in the transport, we add another transport new Winston dot
transports dot file. Here we pass the object
inside property, file name, logs, smlogs dot log. Make sure we use your
file extension dot log, which will help us for
understanding what is in the file. Now say the changes
and take a look. In the terminal, we
get this log info, and if we check our application, our log file is created
at logs slamlogs dot log. And if we open this file, see, we get information
added in the file. If we send again,
get request from the Postman and
after 10 seconds, we get here error and if
we back to our VSCode, then we get new log
level to error. Now some developers like to
log only errors in the file, not informations and warnings. I think that is much better.
We can also do that. Back to Winston. Here
after the file name, we can also specify
the level to error. This means only store
errors in this file, and also we can change the
filename to errors dot log. So if in any of
these transports, we don't specify the log level, then that transport will
use this global log level. And if we specify the log
level in the transports, then it will override
this global log level. Suppose for console transport, we add object level to debug. So now for all levels
who are about debug, we'll show log in Console and only errors will store in
the errors dot log file. So that's how we store
logs in separate file, and then we can improve our application
according to that. So to summarize, console
dot log is not bad. But by using Winston, we can store our logs into a separate file which makes our application
more professional.
136. Log errors in mongoDB: Now in this lesson,
we will store our logs in our Mongo
DB database like this. It is really simple,
let's do this. For storing the
logs in Mongo DB, we need another Winston
package, open up terminal, and here write NPM
install Winston Mongo DB, at the rate 6.0
0.0 and hit Enter. Minimize this terminal, great. Now to add this
package at the top, we require Winston des Mongo DB. Now in the Winston
configuration, we need to add another
transport for Mongo DB. So after this file transport, we add new Winston dot
transports dot MongoDB. And in this transport, we have to set some options. First one is DB. Here, we have to
add database URL. We simply copy this Mongo DB connection
URL and paste it here. Also, we can pass
the log level by level property to
error, and that's it. See the changes, and let's simply check this
implementation. Let's run our
server if it is not running and send the same
suggestions request. After 10 seconds
back to Vas code, here we get the error in the console and if we
check our database, here we get new log collection, and in that we get
our latest error. Here we get
timestamp, log level, which is error, error message, and last, we get metadata. This is the same object
which we pass at second parameter of
logger dot error. See, here we have method, path, and full
stack of the error. That's how simple to log
errors in the database. If you want to store
logs in the database, then you can stay
with this transport. And if you want to store
logs in separate file, then we can stay with
this file transport. Show you the both
way, you can use any of them according
to your choice. It really depends on you.
137. Uncaught Exceptions: So till now in this project, we have handle errors occur
in the route handler and the route handler
will pass error or exception to the
global error middleware. Now, what if we get error in the rest of
node application? We didn't handle it, right? So to demonstrate this, I remove the comment from the Mongo DB connection and simply throw a new
error from here. So throw new error, and here we pass error message, something fails in
the node application. So the changes, and
in our terminal, stop our application, and we run our application using
node index dot js. See, here we get error, something fails in
the node application, and also our
application is crashed. This is called
uncaught exception. An uncaught exception is like a surprise
guest at a party. You are not prepared
for it and it cause chaos because there is
no plan for handle it. In simple words, whenever
node application runs into a problem or an exception that it doesn't
know how to handle, it called an uncoded exception. These are errors in our code, those are not grabbed by a proper try and catch
or not handled properly, and as a result, Node
just doesn't know what to do and our
app get crashed. As our app get crashed, our front end will not get
the data from our back end. Our API will not work. So it's important to handle
those uncode exceptions. Now the question is, how can we handle uncode exceptions
in our node application? Because these exceptions or
errors can happen anywhere. How can we handle
it? So for that, we have to add listener for our node application.
Let me show you. So here after the Winston, we write process dot on. This will help us to add
listener for specific event. Now, on which event,
we want to listen. Write, it is uncoded exception. Make sure you write
the same event name. Otherwise, it will not work. Now what we want to do when uncoded exception happen
in our application? That we add callback function and it has error or exception. In the callback
function, for now, we simply consult a
log this error object. This process dot on is
like a watch person. It is keeping eye
on our application, and if in our application, any uncod exception occur, process dot on will run
this callback function. So at the place of
using console dot log, we can use logger dot error. And in that, we have to first pass string uncaught exception, and then we pass
whole error stag. Let's check this
is working or not. Save the changes,
and in the terminal, let's screen the terminal
with CLS command, and then we run our application
using node index dot js. See, here we get our error in the console and our app
is not grassed by itself. But as we can see,
we also don't get the info of server running
on the port 3,000, which means server
is not running. So our application is
hanging in the middle. Server is not running, and application is not grassing. So our application is
not in the stable state. So what we want to do now have to exit from this
unstable state. So after the logger dot error, we simply write
process dot exit, and here we pass
one as exit code. One means error. Also, we have exit code zero, which means everything is fine, but still we want to exit. And if we pass exit code one, which means some error occur, and that's why we exit. So the changes, and let's run our application
one more time. Node, index dot Js and C, now we exit successfully
from our application. Beautiful. Now, you might ask this question if after
handling the exception, still we are shutting
down our application, then what is the point handling
the uncaught exception? So handling uncaught exception is not about
preventing set down. It is about ensuring the set
down happens in an orderly, safe, and informative way. When an uncaught
exception occur, our app state is unpredictable. After an unaccepted crash, parts of our app might
not work as we want. For example, we got broken
database connections, corrupted memory, incomplete
request, et cetera. Restarting the app guarantees it starts fresh without leftover
issues from the error. Also, in production,
tools like PM two, Docker or Kubernets
monitor our app. When our app shut down, these tools automatically
restart our app. Handling the error
ensures the app exit clearly by using
process dot exit V, and also it making it easier for the monitoring tool to restart our application without
pending issues. Also, one thing I encounter here is when we are doing
process dot exit, our logs are not getting stored in the file
or in the database, but you can see the log in the console. What is wrong here? So when we do process dot exit, our node application gets
terminated immediately. It is not waiting for
asynchronous task like logging to a file or
a database to complete. In simple words,
process dot exit one does not wait for these
operations to finish, so the log part can be partially
or completely skipped. Now, how can we solve this
issue? It is really simple. Have to exit the process after our logger completes
its logging process. So after this logger dot error, we write logger dot on, and you guessed it correctly, this is also
listener of Winston. Here, we pass our event name, which is finish, and at
the second argument, we pass Callback
function, and in that, we can simply move this
process dot exit one. For better practice,
we also logger dot. Save the changes, and let's
see it is working or not. Run this application, and if
we check our logger file, see, here we get new
log, so it is working.
138. Unhandled Promises Rejections: Let's suppose in our
node application, we have some promise which
gets rejected and we forgot to handle that error using try and cache Blog
or cache method. That rejected promise
needs to be handled. So to demonstrate that,
we remove this through error and we create
here a new promise. Let's say cost rejected promise
is equal to new promise. As we know, here, we have to pass the
callback function with two parameters,
dissolve and reject. Now to reject the promise, we simply call here
reject method, and here we create a new
error and pass error message. What do we write for error? Let's say error in the promise. Sorry about this error message. Now let's consume this promise. Reject a promise dot then and in that error function
and we simply consult dot log,
promise is working. Can even use here OD, but for that, we have to
wrap it with a sin function. That's why I use then method, and here we don't handle the
error using cache method. Now let's see what will happen. Save the ings and let's run our application with
node index dot js. Here we get log for this error, and as we can see uncaught
exception is again called. In the node 15 and
above 15 versions, unhandled promise rejection is treated more like an
uncout exception. If we didn't handle
promise rejection, then it may flow into
uncut exception handler. To store separately
unhandlePmise rejection, we can duplicate
this code and simply change the event to
unhandle rejection. Also in the logger, we pass here error message as unhandled promise rejection. See the changes, and let's run this application
one more time. See, now we get here error message, unhandled
promise rejection. So that's how we can
handle uncaught exceptions and unhandlePmise rejection
using global listeners. Now, in our application, let's change this
console dot log for Mongo Di B connection. What do we write here?
Logger dot info. And in the cache method, we replace this console dot
log with logger dot error. And also, after the
failure of connection, we can exit from our
application because our whole application
depends on this connection. So we wrap this code
with curly brackets. Simply from the
global listeners, copy this logger dot on, and logger dot,
and paste it here. It will let our
application to store the log in the
file and database. Now, also, we don't need this error promise and
also this then method. So that's how we handle
errors and log them for improving and keep
tracking of our application.
139. Recap of Error Handling & Logging: Let's quickly recap
this section. So before this section
in our application, we didn't handle errors. So first, we handle errors
for API route handlers. Any error occur in
the route handler, that error will send to the next middleware
by latest Express, and after all routes in
the index dot js file, add error middleware which handles all errors
of route handlers. Now, after handling errors, we can store that errors in
the files or in the database. So for logging the errors, we use Winston and Winston
Des Mongo DB package. Here, we configure the Winston
logger in which we specify the global log label format of our log and some transports
for send our log to console, file, and even in Mango Di. By these, we can store
errors of route handlers. But what if something goes
wrong outside of the express? So we define two
global listeners, one for uncaught exceptions, and another for
unhandled rejection. Remember, if we don't add listener for
unhandled rejection, by default, node will treat promise rejection as
uncaught exception. Also, in these global listeners, we log the errors and then gracefully set
down our application. At the end, this logger dot
will stop writing new logs, terminate any pending logs, and close transport
streams like file streams, SDDPRquest or database
connections for logger. That's all about handling
and logging errors. Now in the next
section, we will add more features in our
ecommerce application.
140. Section 12 - Creating Cart Model: Welcome to another
important section of the ultimate No Jz course. In this section, we will move forward in our e
commerce project. First, we will add
some card features, and then we will integrate payment gateway in
our application, which is really important
and fun to create. I'm really excited
and hope you are too. So let's start this section. Till now in our project, we have added users
categories and products API. Now, when user want to
purchase any product, he will add their
product in the card. So card works like basket
or trolley in supermarket. Add all products which
we want to purchase, and then we pay for
those products. We already know this, right? So we will create a new
model for CAT data. So in the models folder, we create a new file
called cart dot js. Now for defining the schema, we first Cost Mongoose is
equal to require mongoose. And after that, we define st CAT schema is equal to
new mongoose dot schema. Here we add our card schema. In this card model, we will store all
users card details. So first of all, we need to
store user to object type to Mongoose dot schema
dot types dot Object ID, ref to user and we simply
make it required to true. Now after user, we want which products user added in his card. So we add products filled, and as we know in CAT, user can add multiple products. So it will be the
array of products. Now for each product, we store products object, and in that object, we store first product ID. This is again reference, so I simply copy this
schema object for user. Paste it here for product ID, and make sure you change this
reference user to product. Now, after the product ID, we need quantity
of that product. So we add quantity
to object, type, to number required to true
because that is necessary. Man to, also we can add
default to one for safety. Now, if we imagine
our front end, we want to show data like this. First, we want to show the
product name or title, then price, then
quantity, and total. If we only store the product
ID in the card data, then we have to run populate to get product details like title, price, image, et cetera, but it will take more time
for get the card data. We can use here hybrid approach. If your application
needs are different, then you have to stick to
the only reference approach. So if you want performance, then you use hybrid, and if you want consistency of the data where your
products title, price images are
changing more often, then you have to use
reference approach. So after the quantity,
we add title, type to string, and
required to true. After that, we need price, type to number,
required to true. After price, we might need image which will be the
cover image of the product, type to string and
required to true. At last, we need total price, which is the total price
of this current product, type to number and
required to true. This total price, we will count by price into quantity, right? This is all we need in
the products object. Now, what we need
more in the cart? Let's see the GRT page again. First of all, in any
ecommerce website, we get the number of products
in the Nepar or somewhere. And also for the CRT page, we need to display the
final price of the product. So we need two more fields
in our card schema. First, total products to
type number default to zero. If in our card, we have two iPhones and
three smart watches, then our total
products will be five. And after total products, we need total card price. We can associate as final price, but total card price
looks more cool. So total card price, type to number, and
also default to zero. Now here you might ask why we need to store
the total card price? Why can't we calculate the
values when we send card data? The reason why we store the
total card price is because, number one, faster display. So when user views their card, the website can show total price instantly without
recalculating it every time. This save time, especially if there are many
products in the card. Second, reduce work
for the server. This is because we
don't need to calculate the final price
multiple times in lieu. So imagine our user has ten different
products in his card. Without total card price, our server has to look through all ten products and add up their prices every time
you open card page. Now with total card price, our server just fetches the
already store total price. This will save time and efforts, and that's why we store here total card price
in the database. Now we have our
card schema ready. If in the future, we want to add more fields or we want
to remove something, then we can also do that. There is nothing
wrong about that. Also, don't stick
to an approach. As a developer, we have to always think what
makes our application or product more useful and fast for the end
users. Simple as that. Now let's create Cart model, seconst card equals
to mangos dot model. First, we add singular name, which is cart and
at second argument, we pass cart schema. Finally, at the end, we will do module dot
exports is equal to CAT.
141. Defining API list for Cart: Now before we start
building the APIs for card, let's see which and how many
APIs we need to create. This will give us clarity. So here is my process for
defining the API list. I started to imagine the front end from the
user's perspective. In simple words, I put myself
in the shoe of normal user, what the user want
to do with card. First one, they want to simply
add product in the card. So this is our first API. After adding the
product to card, they want to see their own cart, which products they added and how much money
they need to purchase. So we need API for getting
the current user cart. Also, on the New bar, they like to see the number of products available in the cart. So we can create separate
API for only card numbers. After that, on the cart page, they can increase
the quantity of products or decrease the
quantity of the products, and also they can delete the entire products
from the card. Here we need three more APIs, one for increase,
second for decrease, and last for the delete product. So here we have the list
of APIs for the cart. Of course, we can add or
remove APIs from this list. It really depends on us.
142. Adding Products to Cart: Let's start with our
first API for CAT, which is adding single
product in the card. So in the routes folder, we create a new file
called card dot js. Good. Now inside this file, we create Router for API. So Const Express is equal
to require Express, and after that, cost Router is equal to express dot Router. And as we know, at the end, we module dot ports
is equal to Router. Now before we define the API, let's set this router in
our index dot js file. Otherwise, our cart
APIs will not work. Save this file, move to index dot js file and
simply here const CAT routes is equal to
require periods routes CART. Make sure you write here,
require not required. Recently, I make this mistake. Now at the bottom,
we add app dot g. Here we add prefix
API slash CAT. Here we add card routes. Lovely. Now, let's start
with head to cart API. So here we add Router. And can you tell me which
method we will use? Right, we will use post method. So router dot post endpoint is forward slas here after that, we add route callback with
request and response. Now, what we want
from the front end? We mainly need two things. First, we need product ID which user wants to
add in the card. And second, we want to know the quantity of their product. At a time, user can only add one product
with its quantity, and if he wants to
add another product, then he has to call
this API again. Simple as that. So first, we get details in the
request dot body. We can restructure it here and get product ID and quantity. Also for more convenient, we get the product ID
in the query parameter. So remove these from here
and in the endpoint, we add colon product ID. And to get this product ID, we add here cost product ID is equal to request dot
PRAM dot product ID. Good. Now, after
that, what we want? Yes, we need user ID also. And how can we get that yes, we can add orthomidalware here, and for getting the user, we can write const
user ID is equal to request dot user
dot underscore ID. Now, here comes a
logical part what we really want to do in
the head to cart API. First of all, we will check front and cent product
ID and quantity or not. Always start with validation. We add if condition product
ID is not available, or quantity is not available, then we return error. So return response, dot status, 400 for missing fields, and dot JSON Object with Menset property,
missing required fills. Good. Now, after that, we check, front end pass the
valid product ID or not. There any product with
that ID in our database? For that, we write cost, product is equal to a weight
product dot find By ID, and here we pass product ID. Also, if auto input
doesn't work, then we have to manually
input this product model. So for using await, we have to make this
function async. Now at the bottom, we can
put here if condition, if product is not available, we return response which
status 404 dot Json. And in the messet property, we pass product not found. Now again, I am asking what we want to do in the
Add to Cart API. Let's go step by step. Don't get confused. We want
to simply do one thing. We create a new card
for the user and simply add current product in the products array
with required fills. But there is possibility for that user card is
already available. Here, we don't want to
create duplicate cards, so we check const card
is equal to await card. See auto input works, card dot Fine One. And here in the
condition object, we pass user to user ID. Now, as we know at a time, one user have only
one card because if you user check
out the products after payment successfully, we will delete the user
card from the database. If this user has its card, then we get it in
the card variable. What if user has no cart? It can first product for card. So we check condition. If cart is not available,
then what we want to do? Right, we will
create a new card. So new card. And here we pass Object. First, we add user to user ID, product to empty array for now. Total products to zero and
total card price to zero. Now, this will return the cart. Here we can simply use
this card variable and replace its value because
if card is not available, only then we will
create new CAT. Otherwise, we get card object
in this card variable. We use here card is
equal to new cart. So for replacing
this card variable, we have to define
it by using let. Otherwise, we will get error. Are you clear till this point? And also, if you're a little
confused, then don't worry. When we complete this API, we will recap this
API from the scratch. By that, your all
confusion will go away. So we have cart and we just have to push product
in the products array with quantity and other fields which we
define in the schema. So we can do cart dot
products dot push. And here we pass product
object, which we want to push. So object, which are the
fields, I really forget. Let me check in the CAT model. Okay. So first, product ID to product ID, quantity
to quantity. After that, we have title. Now here, how can we
get the product title? Because front end will
only pass the product ID. Think about it. So we get product details from
this product variable. See here we also check
product is valid or not. If product is valid, then we get its detail
in the product variable. So now this makes sense
why we find product. It will validate the product ID, and also we will get
other details as well. So title to product dot title. Price to product dot price, which is the current
product price, image, which is the cover image, so product dot Image, which is array, and we
simply set the first image. Now you might ask why we don't get these details
from the front end. Imagine we get this price
detail from the front end. If you surpass zero
for any product, the price will be stored
as zero for his car. So we clearly can't rely
on the front end data. It's better we get real
data from the database. Now, after that image, we have total price
for that product, which is current
price into quantity. So product dot price
into quantity. And that's all we need
in the products array. Now we have to do
just two things. We have to count
total products of the card and then
total card price. These both are very simple. And do you know which method we will use for
counting the sum? Right, we will use
reduced method. We write cart dot total products is equal to cart dot products, which is array dot reduce, as we know, we have to pass two arguments in
the reduced method. First one is callback function
in which we will calculate the products quantity and then default value of the
quantity which is zero. Now in the callback,
we get two parameters, total and product, which is
a single product object. From the callback,
we simply return total plus product dot quantity. In short, this reduced
method run the loop for each product and give us the
sum of product quantity. Great. Now we have
to just calculate the total card price and we will again use reduce
method for that. Card dot total card
price is equal to cart dot products,
dot reduce. Here we again pass
two arguments. First one is the
callbeck function with total and product
object arrow function. And after that, we simply at default value of this
total, which is zero. What we return from
this callback function. We simply return total
plus product dot price into product quantity. Or for simplicity, we can
return total plus total price, which we calculated here. Also, I think we don't need
this total price field. It is not really making
impact. What do you think? Yes, so let's remove
it from push method, and also we have to remove
that from the schema. Move to car schema and remove this total price filled
from the products object. Simple as that. Now we have all card fills
filled with details, so we can simply save the card. Await card dot C.
After saving the card, we simply return response, dot status, 201 and 200 because here we might create a new card or simply
add the products. Also, dot Json
object with message, product added to
card successfully. And after that, we simply return full cart
in the response. If you don't need to send, then you can remove
this as well. Now let's test this
API implementation because testing is
really important. See the changes, and let's
go back to the postman. Here in our project, we create a new
collection called CAT. And in the CAT collection, we create a new request
called ED products to CAT. First, we change the method
to post APIURL to local host, Column 3,000 API slash CART SLS and here we
have to pass product ID, which we want to add in the AT. For now, I just pass one
and send the request. See, here we get
authorization token is required because in our API, we added orth middleware. Let's generate fresh token, send the login request, and here we get the
token. Copy this. Now in the card API, we go to headers and add here
authorization in the value, add beer space and paste our token without
any double codes. Now send the request. See, here we get
internal server error. And if we check our
VS code terminal, here we cannot
destructure property, quantity of request dot body. This happens because we don't pass quantity in the
request dot body. So head over to
Mongoi B Compass. In the products collection, simply copy this iPhone 14 ID back to Postman here at
the place of this one, we paste the object ID. And in the body, let me
remove these two properties. Now we see raw and simply pass your object
with one property, which is quantity to two. And let's send this request. See, here we get product
added to card successfully. And here we can see card as
well. Let me increase this. See, here we get user, total products to two because
we pass quantity to two, total card price to 2598,
which is also correct. Also we get here products
array with all product object. Here we can see we
get current quantity, title, price, and image as well. Now let's try something more. Let's change the quantity to one and simply store
the same product. See, here we get product
died successfully, total products and total cart
price, both are also right. But if we check our
products array, see, here we get another
product object, even that same product is already available in
the products array. In our API, we have one issue. As we can see here, we directly put the product
in the products array. What if this product is already available in
the products array? That case, we have to just increase the quantity
of their product. So here, before card
products dot push, we have to check the
products which we are adding is already available
in the products array or not. It is really simple. So for finding the product, we add CRT dot products,
dot Find index. Here, we get single
product object, arrow function, and here
we pass the condition, product, dot product ID, dot two string is equal to
product ID dot toString. Now as we know, this fine
index method will return the index value of that product which
passed this condition. So we store that in variable called existing product index. And if product is not found
in the products array, then it returns
minus one as index. So we can use that
in if condition. So I existing product index
is not equal to minus one, which means product is already available in the products array. That case, we will just increase the quantity
of that product, and how can we find
that product object? Right, by using this
existing product index, so cart dot products
in square brackets, we add existing product index. By this, we get product
object dot quantity. Plus equals to current
quantity. That's it. And if product is not found
in the products array, only then we push whole
product object in the array. So we adhere s and move this
push method in the s blog, and that's it. See the changes. And now before final testing, let's remove full card
from the database, and let's create a fresh card. Back to Postman,
send the request. Nice, we get one product. Now let's change the quantity to two and simply send the request. See, now our quantity
only gets increase, and also total products and
total card price is right. Now there is one little issue
in this implementation. Product might have
enough stock or not. We need to check that before
adding product to GAT. So here, after we get product, we pass condition like this. I product, dot stock
less than quantity, then we simply return
response dot status, 400 dot Json object
with message property. Stock is not enough. Now here is one more case. Suppose our product
stock is four and we want to add the product in the cart with quantity three. This will pass
this if condition. Now, what if this product, which is only four in stock, we already added two product
quantity previously and now we want to add
three more quantity for the same product. In that case, we have to prevent our product quantity
to increase. So tell me where we
write our condition. Write, in the existing
index condition. I cart dot products in square
bracket, existing products, index dot quantity plus
quantity which we want to add is greater or equals
to product dot stock. If it is true, then we
return the same response with status code 400
and in Jasen method, object with message property, stock is not enough. And finally, we have completed
our head to cart API. So let's quickly recap this API. First of all, we
check if product ID is quantity is passed
by front end or not, which we can say it
like validate inputs. After that, check product is available in our
database or not. If it is not available, then we return error in the
response, product not found. After that, check product
has stock or not. If it has no stock or less
stock than our quantity, then we return
response with message, stock is not enough. After that, check
user has card or not. If he has no cart, only then we will
create new cart. After that, we check
product which we want to add is already available
in the cart or not. If it is available, then we again check
the inal stock. If it is also available, then we just increase
the quantity. Also, if product
is not available, only then we put the product
object with product details. And finally, we count
the total products and total cart price using reduced method and save
the cart simple as that. That's how we create
head to cart API.
143. Getting the User Cart: Now it is exercise time. I want you to create
a new API for getting the current card details of the log in user. It
is really simple. I know you can do this. Now, let's see the solution. So router dot gt and
point to forward slash. After that, we add Async callback function
with request and response. Now in the callback function, const card is equal to
await card dot find one. Here we pass Object with user to request dot user
dot underscore ID, and at the end, simply
response dot json, this card. Also, for getting the
user in the response, we have to add Orth middleware. And that's it. Let's
taste this API, open postman in the card, create a new request called
Getting the user card. URL to SDP, Column
double forward slash, Local host, Column 3,000
slash API slash CAT slash. And now here we
need to pass token. So go to headers and here
we add authorization. In the value, we
pass bearer space. Now go to the login
API, send the request, get here the new token, copy this and simply in our API, we paste this token. Now let's send the request. See, here we get card
data. Sounds simple. Also, it is possible current
user might don't have the card because he never
had any product in the card. If that is the case, then we have to return
different response. We write if card
is not available, then return response
dot status 404 were not found dot Json
object with message property, user card is empty. At the end, if we
found the card, then we send that card as it is.
144. Increase the Product Quantity: Now, as we know,
in the card page, we might need to increase and decrease the product
quantity by one. So let's define API
for this feature. So Router dot, which
method we will use. So as we know, in our card data, we just need to update a little part of the
data which is quantity, total products, and
total card price. So for updating little data, which method we will use, right, we will use patch
method 0.2 increase and here we need the product which user wants to increase
the quantity in the cart. We will also add here product ID as route parameter,
same as before. Also, we need
orthomidal ware because only login user can increase
the quantity in the card. After that, we add
ACN callback function with request and
response arrow function. First of all, we get
the product ID from the request dot
PRMs, product ID. Now, let's come to logical
part of the query. Many students ask me, how can I understand the logic of the
query or any feature? Let me give you my trick. Whenever you want
to apply any logic, then first of all, describe that logic in simple
human language. For example, here we
want to find logic of increasing the quantity by one
for this given product ID. It simply means we
need to first find the current user card because
that we want to update. After that, we will find that product in the
card products array. And then after we
find the product, we simply increase the
quantity field of the product. We can also increase
total products by one and total card
price increase by product current price. At last, we simply save
that updated card. See how simple this become after writing
the logical steps. First, we find the
current user card. Const cart is equal to a
weight cart dot Fine one. In the comparison object, we add user to request dot
user dot underscore ID. Now it's possible we
don't find the card, so it's better to return
response with error. I cart is not available, then we return response
dot status 404 dot JSON, Object with message
card not found. Good. Now let's
move to next step, which is finding the product
in the CAT products array. We already did that in
the head to cart API. Remember, yes, here we
use fine index method. We write CAT dot
products dot fine index. Here we get single product
object, arrow function, and here we return
the condition, product dot product ID. This is an object ID, so we have to convert it into string is equal to product ID, which we get from
the query parameter. This expression will return the index of the product
which we want to update. So we store that in
variable called index. Now we get the index. Next, we need to increase the quantity of
that index product. So cart dot products
in square brackets, we pass index dot quantity
plus is equal to one. This means increase one
in current quantity. After that, we
will also increase the cart dot total products, plus is equal to one, and also card dot
total card price, plus is equal to cart dot products in square
bracket, index dot Price. And at the end, we will
await card dot CV. And finally, response dot Json pass Object with
message property, product, quantity
increased successfully. Also, we send the card just
for testing, and that's it. See how simple and
clean our API looks. Now let's test this API, see the changes,
and open post Van. Here we create a duplicate
of this postcard method and rem its name to increase
product quantity by one. Now we can addheCRT increase, slash product ID, which
you want to update. Also, here we have
header authorization, and also we change
the method to patch. Now let's simply
send the request. See, here I get invalid token because my
token gets expired. So go to log in API, generate a new token. Good. Copy this token. Simply in the increase API, pays the token in the header. Now let's send this request. See, here we get
success message, and previously we have
three iPhones in our card, and now we have four,
so it is working. Also, total products and total card price is also working. Let me ask
you one question. What if we pass products ID, which is not available in the products array. We
didn't handle that. So here, after
getting the index, we pass condition I index
is equal to minus one, which means we can't find the
index in the card products. Then we return response dot
status 404 dot Json Object with message product
not found in Card. Let's taste this. So at the
place of this product ID, we just pass one and
send the request. See, here we get product
not found in the cart. Great. Now, which thing can go wrong in the API.
Let's think about it. When we increase the
product quantity, ideally, that product
should be in the stock. For example, if iPhone 14
is only six in the stock, and we already have six
iPhones in the cart and we try to increase one more iPhone
14 ideally, we can do that. We can't increase
iPhone quantity to seven because we have only
six iPhones in the stock. We can also put
one more condition before updating
the card details. If card dot products
in square packet, index dot quantity, equal
to product, dot stock. Then we return
response dot status, 400 dot JCN Object with message, product, run out of stock
Gant increase product by one. Now the question is, how can we get this product dot stock? Right, we need to find a product from the
products collection. At the top, after
this product ID, we can const product is equal to at product dot Fine By ID, and here we pass product ID. Now, this might happen this
product ID is not valid, so we can put here
also condition. We already did that
in the post API. See here. So let's copy this condition and paste
it in our increase API. A at the command before
the find method, check if the product exists. Now, let's taste
this implementation. So go to Postman,
send the request. See, now we get
internal server error. Now select the endpoint
and press Control plus D or Command plus D. Getting
the original product ID. Send the request, C, quantity increase by one. Again, send the request. C, we get six iPhone, our stock is eight, so we send the request
two more times. See, we have now eight iPhones and let's send
the request one more time. See, here we get error, product run out of stock, can't increase product by one. Now our API is working well.
145. Decrease the Product Quantity: Now let's quickly define an API or decrease
the product quantity. It will be also the same. Let's copy this increased API
and paste it at the bottom. Now, first of all, we
add here top comment, decrease the product quantity. Also, here we change the
endpoint to decrease product ID. Now let's check this
API step by step, so we make sure we
don't forget anything. First, we get the product
which we also need. After that, we find
the cart good. After that, we find the index, good now here is one thing for decreasing the
product quantity to one, we don't need to
verify the stock. But also, we need to
check one more thing. If currently in our card, we have only one
product quantity and we try to decrease that
product quantity to one. In that case, we have to remove complete product object
from products array. Don't worry, we will write logic for this after we
complete update. Just remember, I'm
adding here comment, check condition
for quantity one. After that, we have to decrease the product quantity to
minus is equal to one. Also for the total products, minus is equal to one,
and here as well, cart total cart price
minus is equal to, here we minus the
price from the total. Then we save a
card and we change this message to product
quantity decrease successfully. Now we have to do little
change in this API. Let's move to our comment. Here we add I condition, cart dot products,
square bracket, index quantity is greater than bun then we decrease
the quantity by one. We move this line in the
I block and after that, else, we have to remove complete product object which
is available on this index. For removing the item from
the array in JavaScript, we use Plis method. So card dot products dot Slic Index which is
index we want to remove. And then at the second argument, we pass one, which means we
want to only remove one item. By this expression, that
whole product object will remove from
the products array. Now here is one thing. After removing the whole product object from the products array, we can't table to minus this total card price because here we need
the product's price. So we have to do
something like this. Card total card price minus is equal to
product dot price. This product price we will get from the
products collection. So in the increased API, we change this card
dot total card price plus is equal to
product dot price. So the original product price
will added in the total. Save the changes,
let's is this API. Duplicate this
increased product API and change its name to decrease A change the API endpoint to card decrease
and send the request. See, now we have
only seven iPhones. Let's decrease it
two more times. See, now we have
five iPhones and total products and
price is also working. That's how we decrease the
quantity of the product.
146. Removing Single Product from Cart: Now in our card page, we might have the option to remove the complete
product from the card. We don't want to decrease the product quantity one by one. So let's define API for removing the
product from the card. So we start with
router dot page, endpoint to slash remove
slash column product ID. Also, we need current
user details, so we add Orth middleware and then an callback function
with request and response. Now there are very similar
steps as our decrease API. Let's see the API, what we need in the
remove product. See first, we need to get
a product, then cart. Then also, we need to find
the index of the product, and also we need this condition. Copy this code till here and
paste it in the remove API. Now go back to decrease API, and as we can see for
removing the whole product, we just need this line. We copy this and
paste it in our API. Good. Now after that, we have to update cart
dot total products and cart dot total cart price. Let's go one by one. Cart dot total products
minus is equal to, here we need the quantity of that product which we have
in the product object. Suppose we have total products seven and we have four iPhones
in the products array. Now if we try to remove full
phone data from the cart, then for total products, we have to minus seven minus
four is equal to three. So we have to find
the quantity which is available in the
products array. So before this splice method, we can do something like this. Cart total products, minus is
equal to cart dot products. Here, we access that product object
which we want to remove, square bracket, index, and then we grab the quantity
of that product. You guessed it correctly. We can do the same for the CAT dot total
CAT price minus is equal to card dot products, square bracket, index
dot quantity into cart dot products in square
bracket, index dot PCE. The reason we remove the
whole product object at the end because if we
remove that object before, how can we access the quantity
and price of the product? Now, at last, we simply await
card dot C and after that, response dot Json object with message property to product
removed successfully, and also send the card with it. Now before we taste this API, we can add one more
condition in this API. So imagine we have
only one product in the cart and we remove
that product as well. Now, instead of storing the
blank card in the database, it's better to remove
that card for that user. User wants to add new
item in the cart, then in the head to cart API, we already put code for
creating the new cart. We can check whether
that product is the only product or
not in the cart. Here, after this
index condition, we can add one
more if condition. Here we check if cart dot products dot
length is equal to one, and CAT dot products
in square bracket, index, product ID is
equal to product ID, which we get from the perms. If this condition is true, then we can remove the full CAT. So await CAT dot Fine By ID and DLT and passe GAT
dot underscore ID. Then we return, response, make sure we add here return. Otherwise, bottom
code will also run. So make sure you
add here return. Response dot Json object with message property, cart
removed successfully. Also, you have to convert this card product
ID, dot tostring. Otherwise, our condition will
not work, and that's it. Let's taste our implementation, see the changes,
and open postman. Duplicate this decrease API, change its name to remove
product from cart. Good. Now let's send
the API endpoint to slash cart remove product ID, and send the request. And because we have only
one product in our cart, we get cart removed
successfully. So as you can see, defining
the API is not so difficult. Just take steps one by one, and if you get confused, then write comments for each step that will clear
many of your doubts.
147. Creating Orders Model: Now before adding
the payment gateway, let's create a new orders
collection for storing the information about all
orders and with their status. So in the models folder, we create a new file
called orders dot js. Good. Now, first of all, we import const mangos is
equal to require from mangos. After that, cost order schema is equal to nu
Mongoose dot schema. And here we define our
collection schema. We already know this, right? We created schema many times. Now in the orders collection, we need all things which we
added in the card collection. So we copy full
schema of the cart. In the order schema, we paste our schema. We have user who order products, total products, and
also total card price. Now you might ask why we
need this all products data? We need this products data for storing the history
of the user order. If user want to see which products they
ordered previously, we have to show them
these products list. So card and order is
two separate things. In card collection, we store all products which user
wants to purchase. Products in the card can be
added, updated, or removed. But once the user checks
out and make payment, the card data is
converted into an order, and we remove that card data
from the card collection. In order collection, we store data permanent for
completed transactions. And by that, users can see
their previous orders. Now in this collection, we have to add some more fills. First, we make this total
card price to total price. Next, we need payment ID, type string and
required to true. This is the ID of the payment. We will get this ID from
the payment gateway. And by this ID, we can see which payment method
user use for the payment, UPI or card or Net banking
or something else. Next, payment status to object, type to string required to true. And in this status, we can mention payment paid or filled. Next, what we want
is shipping address, type to string, and
required to true. As we move this below the total price for
convenience. It doesn't matter. Now, after that, we want
order status, type to string, and here we can add possible
values for this field, which is enum to array. First value can be pending. Next, we can processing next, CB Also it can be delivered or it can be
canceled by default, we will make our order
status to pending. Here it is not payment status, it is order status. Now next, we can store the date on which user
place this order, created at in object type to date and default
value to date dot now. As we want date on which
this order gets delivered, delivered at in
object type to date. For now, these
fields are enough. If in future we
need more fields, then as we know, we can easily add fills in
this collection. Now let's create collection. So cost order is equal
to Mongoose dot model. Here, we pass singular name, which is order, and here
we pass order schema. After that, at last, we will module dot exports is equal to order collection,
and that's it.
148. Workflow of Payments: Previously, we have done adding and updating
the card features. Now, when our user wants to buy the things
available in the card, we need to integrate
payment in our application. So before diving on
the code, directly, let's first understand the
full workflow of the payment. So imagine this is our user, see added products to card, and from the card page, click on the checkout
or pay button. Step number one is, we will call our checkout API on
this button press, as we know, we have our card
details in our database, so we get total amount
of price of her card. Also, we can pass in which
currency she wants to pay. Now in the step
two, the details, price and currency, we will
send to Payment Gateway. Now step number three,
Payment Gateway will create a payment form and display
it on her browser or phone. Step number four, see
enter her payment details like card or UPI or
wallet see want to use, see enter that details in the payment form
and click on pay. Remember, user payment details
goes to payment gateway, not to the back end, and
that's why it is secure. Now step, payment gateway, send the payment
details to bank, Bank validate the payment data. If it is true, bank
will return success, and if it is false
or our user has not sufficient balance
or anything goes wrong, bank will return fail. So bank will send this success or fail status to
Payment Gateway. Now step number six,
payment gateway, send the success or fail
status to our back end. If it is success, then we will perform some task
in the back end which we want to
perform like add the card data to
orders collection, set the payment status to paid
or anything we want to do. Now after completing
the all process at step seven,
from the back end, we show the user payment is successful or payment gets
failed, simple as that. So payment gateway is like a middleman who takes
care of secure payments, mode of payments, and directly transfer
money to our account. Now there are many
payment gateways available like stripe, paper, razor pay,
and much much more. But these three are top. Now, how can we decide which payment gateway
we want to use? So if we are targeting
the global audience, then stripe is good option because it allows
international payments, and it also allows UPI. Now, people also allow
international payment, and people is also good option. Now, if our business
based in India, then sorpay is best for us. Resorpay can also handle
international payments, but it is only suitable for businesses based in India
foreign transaction. In simple words,
in simple words, business is registered in India and also wants to
get foreign transaction. For globally,
international businesses, gateways like stripe or paper might be more
better choice. So in this course,
I will show you both reser p for
Indian businesses, and if your business
is out of India, then I will also show
you paper integration. The reason why I can't show you the stripe because for Demos, stripe don't allow some
countries account, but it doesn't matter which
payment gateway I show you. If you understand the
logic of payment gateway, then you can apply any
payment gateway by your own. And that's my main focus. So just see these lessons. I will clear the logic
behind the payment gateway.
149. Implementing Razorpay Payment Gateway: Let's set up resupe for our
Cardwig node application. We will create and taste payments like this.
It will be fun. Also, if you are
outside of the India, then you can skip this lesson because you can only register in resupe if you are in India or your business
registered in India. After this lesson, we
will apply paper payment on which any country
user can create account. Now let's roughly see how
this payment will work. So when user clicks
on the PayNw button, front end will call one
API. Let's check out. In that API, we will
create order for aserPay by giving some
information about our payment. Like how much amount we
want to collect from the user and in which currency
we want to take payment. Now when RSR PE get
this information, it generates order, which is the object of information
with unique object ID. And then we need to pass this
object ID to the front end. That's the work of first API. Now when front end
gets the object ID, front end will open the
sor P payment window. Pass the object ID
and some details. Now you might ask why we
need to pass the object ID. By this object ID only reser PA gets the information
about the payment. It will also help us in the payment verification
and many more benefits. Now user will enter the payment details
and click on pay now. If that is successful payment, then reser Pay generate a
unique signature payment ID, and then we will
call our second API, which will verify our payment. In the second API, we
will verify the payment. Reason we need to verify it because that API is calling
from the front end. Anyone can easily manipulate that information and make
the payment successful. So we will verify the payment
in a very secure way, which we will also
implement in this lesson. Only after the verification
process, if it is success, then we will create a new order
in our orders collection, and then remove that user card. Think of sor pay is like
a movie ticket counter. For API one, you go to the
counter, which is razor pay, tell them which
movie you want to watch by telling them
amount and currency, and then they give you ticket, which is unique order ID. Now, for API two, after you
show the ticket to security, you watch the movie, The
counter verifies that the ticket was used correctly
and update the records. So for razor pay in the backend, we need to create two APIs. First, for creating
razor pay order, and second API is for verify
the payment of razor pay. If we successfully
verified the payment, after that, at the last step, we will create a new order in the order collection
and remove our card. Now let's start with
creating first API, which is for creating
ser pay order. In the route folder,
you will create a new file called orders dot gs. Now for quickly
adding the router, we open card Route file, move this router
line at the top, and copy these first two lines. In our orders dot JS
file, we paste it here. Now at the bottom, we have to do module dot ports is
equal to Router. Now let's at this router
in the index dot JS file. So at the top, const, order, Rowe is equal to require. Here we go to the routes
folder and orders. And at the bottom,
after the card routes, we add app.us slash
API slash Order and pass here Order Routes. Good. Now we can focus
on building APIs. So router dot post, point to slash checkout. Also, we need moth middleware because we want only log
in users can check out, and at last acing
callback function with request and response. Here in this API, we want to create ser pay order. And for that, we need
ser pay instance. So in the terminal, we
install reser pay package, NPM install ser pay at the rate, 2.9 0.5, which is the latest version while I'm recording this scores. Good. Now at the top, we input cost ser P is equal to require ser P. Can you tell me why I give
this capital letter? Right because it is class. Now in our API, we create Cast
reserp Instance is equal to New reserp inside this, we need to pass object
with two properties. Key underscore ID. This is the key ID of
our reser pay app. In our application, we
will store that key in the dot ENV file because we
need to keep that private. So process dot nv dot reser py underscore key underscore ID. Don't worry, we will
define them in some time. After key ID, we need
key underscore secret to process dot nv dot ser pay, underscore, key,
underscore secret. Now, let's set these two
variables in the dot ENV file. Sor pay, underscore, key, underscore ID is equal
to leave it blank. And next ReserpUnerscore,
key, underscore secret. We will add them when we
create resorpay account. For now, let's complete our
checkout API and after that, we will create
reser pay account. After creating the instance, we can create order. Cost order is equal to await reserpy instance dot
orders dot Create here we have to pass options
object for this order. First one is the amount. Let's say we will pass here 500. After that, we need to
pass currency property. This will specify in which currency we want
to proceed payment. Suppose we want to do
payment in the Indian rupee, then we pass here INR, and if we want to do
payment in the US dollar, then we pass here USD. But make sure if you use
other country currency, then your reserve pay accounts internal payment
should be active. For now, we simply pass INR. And last, we need to
pass Unique receip. This is as a name
tag for the order. It doesn't affect the payment, but help developers or business to recognize
which order is which. For example, if we
have multiple orders, we can use this
receipt ID to find a specific order in RSR Pi
dashboard or in our database. So we pass here backticks received underscore
dollar Calibackets, date dot now for generating
unique receIps that's it. This will generate order for us. Here we simply return response dot Json here we pass object with
few properties. First one, success to true, which means we
successfully create order. Second, order ID
to order dot ID. Next amount to order dot amount and currency
to order dot currency. That's it for the first API. This is not the final API, we will improve it later. Now before going
to test this API, let's also create second API so you don't get confused
when we do testing. I know you have many questions, but don't worry at the
end of this lesson, everything will be clear. After this API, we create a new post API and
point to slash payment verify also we need Os middleware for
this API and at last, ASN function with
request and response. Now in this API, we need three things
from the request body. Second object is equal
to request dot body. First one is ser pay, underscore order, underscore ID. Next, ser pay,
underscore payment, underscore ID, and last ser
pay underscore signature. With the help of this
order ID and payment ID, we have to create one
type of signature. If that signature and ser P
underscore signature will equal when we consider our payment is verified.
Don't get confused. It is really simple. Secst generated signature
is equal to here, we have to use one built in nodejs module which is crypto. At the top, we import const crypto is equal to
required crypto. At the bottom in our second
API crypto dot create HMAC. This is the Nojs
method to create has base message
authentication code, HMAC. Now at the first parameter, we have to write the algorithm
for creating the HMAC, which is a such a 256. At the second parameter, we have to pass resorp secret. So process dot Env dot resorpUnderscore, K,
underscore secret. Now, after this
create HMAC method, we add another method
called update. This takes which
data we want to has. Here we pass back ticks, dollar in Cali packets, reser p underscore,
order, underscore ID. Here we add Bar symbol, dollar, Cully packets, sor pe, underscore, payment,
underscore ID. After that, we add another
method dot digest and here we pass X. I get this code from the
reser P documentation. You can read the documentation
for more details. This code will
generate a signature. Now we can compare it with this reser underscore signature. I generated signature is not equals to ser pay
underscore signature. If they are not
equal, then here, we return response dot status, 400 dot GSN with Object, success to false and message to invalid
payment signature. And after that,
we simply respons dot Json success to true and message to payment
verified successfully. So if from the front end, someone pass fake order
ID or payment ID, then by this process,
we can verify it. If payment is not verified, then we return response
with filled message. And if it is verified, then we return response
with success message. So this is not complete code. We will update it
after the tasting. Now before tasting this API, we need key ID and key
secret of reser pay. Remember, we leave empty
variables in the dart NV file. Let's create an account of ser pay and generate this
key ID and secret key. These are very similar to the
ID and secret key which we generated while implementing the Google and Facebook
authentication. Go to the reserp.com website. And as you can see here, we have only India, Malaysia and Singapore option, which means only these
country based businesses can set up reser pay. But also, we can accept
payment from any country. So glicon sign up, enter your email address. Then create password. Make sure you follow
these password patterns, and click on Continue. This will send OTP to email, so I quickly add the OTP
here and click on Verify. Now, here is the thing.
Select the country, your business is
incorporated, which is India. Can continue, and now we have
to follow few more steps. Obvious, we are
accepting the payment. It is not simple setup as we are calling another website API. We have to provide our details. Also, these payment platforms can change their
interface very often. So if in the future, you don't get the same
interface like I'm getting, then don't get confused. Just provide the
details they are asking and create
account on reser pay. After creating an account, we can create key
ID and key secret. Here it is asking for, where would you like
to accept payment? For now, select online
and start on boarding. Now, next, where are your
customers paying from? If your customers are from
India, then select India. But here, I select outside
India and start on boarding. It will take some time
Now we write our name. Make sure you write
your legal name. Continue. Next, verify
your contact details, write your mobile number, and it will send ODP, write that ODP, and
click on Continue. Now accept payment on here
I select on my website. If you want for app or anything, then you can also select
them and continue. Here, we have to add
our website link. Also, ser pay verify
this website. Is it legal or not? So if you have your website, then add it here. For now, I don't have, so click on add Letter
and add Letter. Now select your business type. I select unregistered. Also, if you select
unregistered business type, then you can't accept international payments
in the live mode. For that, you need
registered business. Currently, we don't have
any registered business, but if you have then
select registered. Also, don't worry for accepting
international payment. We don't need to do
some extra work. Just we need
registered business, and Reserpy will allow you to accept
international payment. Now add your
personal Pan number, which all of us have edit
here and click on Continue. Here, I get error in my name, so I write my legal name, which I have in the
Pun card and continue. Now upload your personal
PN card, select, upload, select your
document, and open it. It will take some
time. And continue. Now they are suggesting
us to complete the QIC. Select your business category, I select your education. If you have another category, then select according to that. Next, select your subcategory,
select what you want. Next, what is your
business model? Also, select this according
to your needs and continue. After that, we have
to add bank details. Here, we need account
number and IFSE code of your account branch. Both you get on your passbook. Make sure you write the account number in which
you want to accept payment. I fill these details
and click on Continue. It will verify these
details. And done. Next, we have to
select purpose code. Let me try to directly continue. It is giving error. We have to select purpose code. We can search it by the group. But for testing, I
simply select this p00 14 and continue. Next, do you have an
importer exporter code? No, I don't have accept the
terms and click on Continue. Now it is time for
business policy. Here they ask for many important questions like cancellation and refund time. I select these to seven days, you can select according
to your needs, refund processing time
to three to five days, which is good, but
nine to 15 for safety. Also, skipping time,
eight to 14 days. Now we have to also provide
support contact number. I also try to skip this, but it is giving error. So I write my number
and also write my email address and click
on Create policy pages. Here you can see pages,
click on Continue. Here we have policy
links. Continue. Submit your application. Here, I try to complete
the video KYC process, but here I get error, so I try multiple times,
but nothing happens. I decide to skip this. I close all pages and
try to open serpy.com. And try to login with the
account which I just created, but it is loading and loading. So open incognito
window and openerp.com. If you like the login,
enter the email, continue, and enter
your password. Good. Here we get SRP dashboard. For now, we are in
the taste mode. We can change it from here, but don't change it for now. Now to get the APIkey, we go to the account
and setting option. And here, in the website
and app setting, we go for API keys and
generate Taste key, it will send us OTP, write the OTP and
simply click on Submit. See, here we get the
key ID and key secret. Now, first, we copy key
ID back to VS code, and in the ENV file, we paste the key ID
for this variable. Again, copy the key secret and paste it in the ANV
file secret variable. Save this file, and
from the website, you can simply click on
Download for backup. Now, let's test our both APIs. It is working or not. So as we know, we need front
end for tasting these APIs, because on the front end, we will open RSRPEPayment page. So here I created one simple SDML page called RSRPFront end template dot SGML. You will get this page
below this lesson, and it is also available in the Resources project to folder. Download it and simply d it
in your current project. Now, let's run this STML file. So right click on this
and click on Copy Path. In our browser, pace this path. See, here we get the title
and one simple button. If you want to see what happened when we
click on this button, then you can watch the STML
file, it is really simple. Let me show you also, we have to change some values. So here at the bottom,
in the script tag, I added some variable which
you can pass for your data. First, we have order endpoint, which is the endpoint
of our first API. And second, we have
verification endpoint. You have different endpoints, then you can replace them
according to your endpoint. Next, we have shipping address. Here you can write your address. After that, user currency, which I set to INR, but you can also
pass USD or Euro, et cetera, if your ser pay
has international payment on. After that, we have SRP key ID. Here, you have to
write your key ID. I copy my key ID from the
ENV file and paste it here. Make sure you write
your own key ID. Otherwise, this will not work. Next, we have togon. Here, you have to
pass your JWT token. Now, currently, in
our application, we set only 2 hours expiry time, which is little
annoying for tasting. Because we have to create
JWT token again and again. So we can remove the
expiry from login and simply add expiry at the
final stage of the project. Open user routes,
and at the bottom, we remove this object
with expired property. Now let's generate token
which never expired. Open Postman and open login API and simply send the request. Here, we copy this token
and in the SDML file, we simply paste it here. Next, I added this on click
event for this pay button. In this, first of all, we call this order API using fetch method and
with this token. As we know from the order API, we will get the order
data. We get that here. If the data has not success, then we show alert
with fail message. Now, what if we get reser pay order successfully
from the back end? Then we will open
resorb payment window. Here, from the front end, we have to pass these
options with reserp. See, here we have key amount, currency, name, order ID, and at last, we have handler. This handler is really
important, which is function. RSR P run this handler
function when user enter correct information and
payment turns successfully. So after a successful payment, we call our second API
for verify the payment. If that verification
is successful, then we show success alert, and if it fails, then we show field error. And at last for opening resorpe, we create new reserp instance and pass whole options here. Then simply by using
reserp dot Open, we open ReserpPayment
page with that options. At very last, we have this payment dot
field which will run when user enter wrong
payment details or if they have not
sufficient balance, then this function will
run simple as that. Now let's simply taste
our implementation. I'm very excited about it. Save this file and
back to our browser, refresh the page, and simply click on Taste
payment button. Thing happens, that's not fair. Let's check what happens. So on inspect using F
12 here in the console, we can see we get error, access to FechtO
checkout API from origin null has been
blocked by course policy. This is very popular error
for full stack developers. It happens because by default, our Express application can't accept API calls
from any origin. Have to enable course
in our Express app. So back to VS code, open a new terminal and write, NPM install course
and hit Enter. Now in the index dot
JS file at the top, we import cost course is
equal to require course. Then at the bottom, before this express dot JS
and middle where we add app dot Ug here we call this course
function, and that's it. Save the changes and make sure our server is running. Good. Now refresh the page and let's again click
on payment button. See, RSR P payment
window is here. Here we get these types of
payment methods like cards, net banking, wallet,
bill letter, et cetera. I think UPI is not here because my business
is not verified. Now at the left side, we
get the price summary, which is five Rupee in the
back end in the amount, we pass 500 here we
are getting pi rupee. Why? So in the amount filled, we have to pass amount in the smallest unit
of the currency. And what is the smallest
unit in our country PSA, which is 100 PSA is
equal to one rupee. And here we pass 500 PSA, which converts to Pi rupee. So if we change
this to, let's say, 50,000, save the changes
and refresh the page. And if we again click
on the payment button, see, here we get 500 rupee. So make sure about the
amount of the payment. That's why I created
this front end template, so you can understand whole
process very clearly. Also, at the top right side, we can see we are in the
tasting mode because we created key ID and key
secret for tasting mode. Now, let's taste the payment. So for tasting the payment, first, we go to the cards. Here, for tasting purpose, ser pay provides
some card details. Also, they provided whole
details on the documentation. I will give you
this page link at the right side below
of this lesson. So here we go to cards. Let's test Indian card, copy this card number, and in our page, paste it here. Now pass here any
future date for expiry, like 12 slash 30 and CVV number, let's pass 111,
make the payment, secure the card, maybe later. It is processing and which scenario first
we want to test, success or failure, let's go
for failure. It is loading. And see, payment failed, and also we get Alert
payment failed. Now let's try success. Again, at that card number, see it is loading,
select success. And here we get this
beautiful animation, and then we get
payment successful. And also, we get here payment
successfully verified, which we get from
our second API. So our both API is working well. If you want to try
another payment method, then you can also do that. Just check documentation
for that testing method. Now let's update our
API for real use. So as we know, our
payment is working. Now we can improve our APIs
and make it realistic. It is really simple.
Let me show you. So what do you want to
change in the order API? Number one, currently
we are passing static amount here,
but that is not right. We have to pass the total
card price of the user. Second, we are also
passing your currency IR but user can decide in which
currency they want to pay. So we want to do these
two changes only. Let's start with getting
amount from the card. So before this reser
instance, we write const, cart and await cart C,
autoiput not works. So at the top, cost, cart is equal to require we go one folder up
models and in that cart. Now in our API
cart dot Fine one, and here in the object, we pass user to request dot
user dot underscore ID. Now, this will
return cart Object, but we have to pass condition
if CAT is not available or CAT dot products dot
Length is equals to zero, then we return response with status 404 dot JCN
message to CRT not found. Now at the place of this amount, we have to pass cart
dot total CRT price into hundred because we have to pass amount in the smallest
unit of the currency. Let's taste this,
save the changes, and refresh the page and
click on Taste Payment. If we check the console, see, here we get error. It is the 404 error, which means card is not found. Oh, remember, in the
remove cart lesson, we remove the cart by removing the last product from
the products array, so we have to add one
product in our cart, go to Postman, open
at to cart API. Here we have to update Togan, copy it from the login API, and paste it in the
authorization header. Good. Now send the
request, product added. It is iPhone, nice. Now back to browser
and refresh the page, and again, taste the payment. See, here we get our card price. Now, as we know, in database, we store all products
price in US dollar value, and that's why we are getting
two iPhones in this price. But if you are using razor pay, your business will
be in India and most probably your target users
will be Indians as well. So in that case,
in the database, you have to store price
in the Indian currency. Now, let's suppose
you want to target global audience and you store
price in the US dollar. In that case, you have to
convert that dollar price to Indian currency and
then pass it in the amount let me show
you how we can do that. So for converting
the dollar value in the Indian currency, we need current exchange rate. We can't rely on the
static exchange rate. So we will call one API for getting the
exact exchange rate. So head over to groom and
search exchange rate api.com. This is the popular
exchange rate API. Now to get exchange rate, we need API key for that. So here we enter our email and simply
click on GetVree Key. Here, we have to
create password, accept terms, and generate
APIKey. What is this? Complete the
verification, and then they will send API activation
key in your email. Open that link, and here
we get the API key. We can see we can send
1,500 exchange request. If you want to get more request, then you have to pay for that. How all exchange
rate APIs works. For now, don't worry about that. Simply copy this key
and in our ENV file, we add the exchange
score rate score API, underscore key is equal to
paste your API key here. Save this file and back
to exchange rate page. Here, we have to find the API, go to Doc's overview. Here we have APIs, one for getting
all exchange rate, and one for getting the pair. We go to pair conversation. Here, we get the
API, so copy this, and in our API, let's create separate function for getting the exchange rate. So Const, watch exchange rate
is equal to arrow function. Now in this function, we will call that
exchange rate API. So const response
is equal to wedge, and here in the backticks, we simply pase the IIURL. Now we have to change
a couple of things. Here at the place of your APIE, we have to write
dollar Cully brackets, process dot nw dot Exchange, underscore rate, underscore
API underscore key. Make sure you write the
right ENV variable name. Now at last, we
have two currency. First one is the base currency, which is the currency in which we add price
in our database, and second one is
the target currency in which currency
we want to convert. So our base currency is
USD because in database, we added products price in USD and we want to convert
that currency into INR. So our target currency is INR. Also at the place of passing static value of the
target currency, we can make it variable. So in the parameter, we get target currency and we pass that dollar calibract
target currency. Now, as we know, fetching
API is Async runous task. We have to await here
for the response. And for using await, we have to make this
function async. Luly. Now this response will
return data like this. Here we need this
conversation rate. After getting the response, we have to convert
that data into JSON. Cost data is equal to
await response dot JSON. Now from this function, we can simply return data dot conversation
underscore rate. Good. Now in our orderyPI we simply count here
amount, count exchange, underscore rate is equal to
weight, patch, exchange rate, function, and as argument, we pass our target
currency, which is INR. Now after getting
the exchange rate, we create const amount
is equal to card dot total card price into
exchange underscore rate, and we have to round this value. So wrap this expression
with parenthesis and simply add dot to fixed
and pass here two. Now in the amount, we
pass amount into 100. Now let's check this
implementation. Save this file back to browser, refresh the page and
click on Taste payment. See, now we get the value
in the Indian currency. Great. Now, let's suppose we want our user can
pass target value. So we get the user target
value in the request dot body. Secst object is equal
to request dot body, and here we get currency. And for default value, we pass here our database
currency, which is USD. Now at the place of this
hard coded target currency, we pass our currency variable, which our user pass in
the request dot body. Also, here is the one thing. If our base currency and
target currency is the same, then we don't need to fetch
exchange rate, right? So let's do modification
in our code. Before this exchange rate, we pass I condition
currency is equal to USD, which is our price
value in the database. If both are equal, then we have to do
amount is equal to card dot total card price. Else we can calculate
amount like this. So we just move these two
lines here, remove this const, and at the top, we define
let amount is equal to zero. So we replace this amount value according to our condition. And also in the order options, we have to pass this currency, save the chinges and let's
taste our implementation. Refer us the page, click
on taste payment see, here we get INR because
from front end, we are sending currency as INR. If you want to change
that, then open STMLFle scroll to these variables. Here, in user currency, we pass CAD for Canadian dollar. Save this file, refresh the
page, and send the request. See here we get value
in the Canadian dollar. Also, if we don't pass your currency field
in the EPI call, save this, refresh the page, and send the request again. See, by default, it peaks USD. Great. Also, if your razor pay account has no
international payment feature, then you can't make
international payment. It is the policy of the ser pay. If you want to check
the international payment feature of your account, then go to your
accounts and setting option of your ser Bay account. Make sure you are
in the live mode. Here, I can't able to move to e mode because this
account is not verified. If you can go in live mode, then go to the international payments in the payment blog. That you can see your international payment
feature is on or not. Currently, we are in
the testing mode and also test will field in
our international payment. So we improve our first API. Now let's move to second one. Now let's improve
our second API. Don't worry in that we don't
have to do much thing. We will just create
a new order data in the database and remove the user card if
payment is successful. If payment is not verified, then we are running
this code block. I payment is successful, then we can write code here before we return
success response. Here we create cost, new order is equal to new order. Make sure we input
this order model. So at the top, cost
order is equal to require we go one fuler up
models and in that orders. Good. Now at the bottom, we have to pass new
order object here. First of all, in this order, we add user to request dot
user dot underscore ID. After that, we need
products, and in that, we will store all
products which we order, how can we get this information? Right, we can get it
from the back end. Before this order, we find cost, cart is equal to eight
cart dot Fine Vn. Here in the object, we pass condition user to request dot user
dot underscore ID. Simply in the order products, we pass products to
CAT dot products. Next, total products
to CRT Total Products. Next, we have total price
to CRT total CRT Price. Next, we have shipping address, which we have to get
from the front end. For now, leave it as it is. Next, we add payment
status to paid, payment ID to reserve pay, underscore, payment,
underscore ID. And as extra information, we can store reserpeOder ID, to reserpe underscore,
order underscore ID. But we have to add this
field in our order schema. After this field, we add
reserpeOder ID to string. And that's it. We pass all
information which we needed. Now we can store this new order, so await new order dot CV. And after saving the new order, we can remove our card
from the database. So await card dot deleteB
here we have already card. That's why we directly
use card dot delete one. If we don't get here card, then we have to use CRT dot
Fine one and delete method like this, and that's it. Now we just have to pass shipping address in
the order property. So tell me from where we
get the shipping address. Right, we get it
from the front end. In the payment
verify request body, we can get shipping address
because from the front end, we already pass the
shipping address. See here I pass the
shipping address. Now let me give
you one situation. Imagine user forgot to
pass shipping address. Now, how can we deliver
the product to that user? We have to make sure user
pass this shipping address. So here we can write if shipping address
is not available, then we return response with Status Code 400
for bet request, and in the JSON object, we pass message to please
provide your delivery address. Now here is one more thing. We know this payment verify API will run after the
payment is done. We can't say now user, they don't provide
sipping address, so we can do one more thing. We can simply put this condition at the very beginning
of our first API. And here we get zipping address from
the request dot body. So our front end needs to pass Zipping address in
these both APIs. Also, in our order data, we set zipping address to
sipping address, and that's it. Here, our implementation
part is over. Let's see we are passing zipping address in
both APIs or not. So on ser P template file, we can see here in
the second API, we are passing zipping
address as zipping address, and also in our first API, we are also passing zipping
address to zipping address. And if you want to
change your address, then you can do that
in this variable. Also, in the first API, we remove the currency
field for tasting. So we again addit here
currency to user currency, and currently, I set it to INR. Nice. Now let's taste
or implementation. Save the changes, and
let's open Browser, refresh this page, and then
click on Taste payment. Here, we don't get some option because amount limit is
high for this payment. Here, we have a
lot more payment. See, here we get iron
currency and we can taste this payment using net
banking, select any bank. And see, we get payment filled because amount exceed
maximum amount allowed. Now let's try with cards, copy the card number for
tasting and paste it here, add any expiry date, CVV to one, save the card,
and finally continue. Maybe later Still, we are
getting the same error. So the maximum amount is one leg for Indian currency
in single payment. In live mode, there is
five lag for upper limit. It is just for testing, they set it to one leg. So to test this, we
go to our database, Mongo Di become pass
and simply lower the USD total card price to
500 and click on Update. Now, let's repress the page, click on Test payment. See, here we get low price, and here we get
wallet options also. You can use that
also for tasting. Here, we select card, paste the card number, select expiry date, and
CVV and click on pay. Select here success. And see, payment successful. And here we get payment
successful order placed. Now, if we refresh our database, C, card is removed from here. And if we refresh our database, we get orders collection. See, here we get our order, which has two products
and we pay $500 for that. Also we get address, payment ID, and
other more details. Also, this order status
is for business, which is pending,
our order is in pending mode and
payment is set to paid. Don't get confused by that. That's how we can implement
reser payment gateway. This is very important and fun to create.
What do you say? I know this is long one, but you can see what we have
implemented in single go. We implement whole
payment with testing. You can take 15 to
20 minutes break, enjoy this moment, and
then continue this course. So if you want to apply
payment in the real world, then from the reser pay, you have to switch to Live mode and then generate key ID and key secret and use them in our back end and also in the
front end, and that's it. You can accept payments
from the users.
150. International Payment using Paypal: Let's see how we can implement international payment gateway
in our node application. So as we know, for
global payment, we have two very
popular options. We have stripe and
we have paper. In this lesson, we will
implement paper like this. We will implement
it in our back end, taste it with the front end, and also here we can
see the taste payments. So it will be fun. This video is for any country
people. Let's start this. Also, currently, stripe is request only option
for my country, so I can show you the
demonstration of Stripe. If in future, they allow, then I will create
lesson on that also. Now let's understand
the workflow of the Paper payment gateway. So when user click
on Checkout button, we will create one API, let's say, create order. API will create order
for the paper payment with some details like how much amount we
want to get pay, in which currency we want
user to pay USD or Euro. Remember, this order
is a paper order, not our order collection. Now, this API will
generate order ID by using paper and simply return
that order ID to front end. By using that order ID, paper will open payment page where user can enter
details about payment. Now, after user enter
payment details, we'll call another API for
capturing that payment. Basically, by this API, we will allow users payment, add in our paper
business account, and then we create order data, and after payment successful, we create order data and
confirm the user order. So for implementing
paper payment, we have to create two APIs
in our nodejs backhand. First for creating order, and second one is for capturing the payment
in our account. I know you have many
questions, but don't worry. After completing this lesson, you will understand
whole workflow better. So let's create these two APIs. Also, if you implement SRP payment gateway from
the previous lesson, then you have to
repeat little stuff like removing token
expiry, et cetera. I have to repeat so we
all gets on one page. Sorry for that. I hope
you can understand this. Now in the routes folder, you have to create a new
file called orders dot js. I have created this file with two APIs because in
the previous lesson, I show SRP integration, but those two APIs is separate from this
payment integration. You can totally ignore that. Now for quickly
adding the route, you can simply copy first
two lines of code from the card routes and paste it
in the Orders route file. At the end, you have to do module dot Xbards
is equal to Router. Also, you have to add this router in the
index dot js file, so Const Order
routes is equal to require Got routes
folder and Orders. At the bottom, after
the card routes, you can add app.us slash API slash Order
and pass Order Routes. Now you can focus
on building APIs. So in our file,
router dot post and point to slash People
slash Create Order. Here we also need Os
middleware because we want only logged in
users can access this API. Also make sure you input Osmddleware at the
top and after that, is in callback with
request and response. Now in this function, we know we want to
create Paper order. For that, first, we
have to configure P. In the configuration folder, we have to create a new
file called paper dot js. First of all, we will define
variable for some details. So const paper is
equal to object. First of all, client ID, which is our
application client ID, then client secret, which is the secret key of our
paper application, and at last, we
also need base URL. This is the base URL
of the paper API. These all details
we will store in the NNV file because
for security reasons. So here we pass process
dot w dot paper, underscore client underscore ID. And at the second property, we write process.nw dot
paper, underscore secret. And at last, we write
process dot E and paper, underscore, base,
underscore URL. Now, let's set these three
variables in the ENV file. So PPL underscore client underscore ID equals
to leave it blank, and next, paper underscore, secret is equal to, and last. Paper underscore base
underscore URL is equal to. We will add them when we
register paper business account. Okay? For now,
leave it as it is. Now in paper when we want to create paper order or
we want to capture the payment in
bootstep we need to create a unique token like JWT. So for generating that token, we can create function for that. Const G Xs token is
equal to arrow function. Now for generating the token, we will use API of
official people. So for calling API
we will use Axos. Axios is the package for
calling API in JavaScript. It is much easier than
using fetch method. Pen terminal, NPM,
install Axios. Good. Now to use this
Axios package at the top, we import cost Axios is
equal to require Xos. Now, in our Gate
aces to confunction, we await axios dot here, we have to add the SDDP
method, which is post. And in this method, at
the first position, add API endpoint in backticks, dollar Cali Brackets,
dot BRL V one, O oth, two, slash token. Now at the second place, which is the body, we have
to pass double codes. Grant, underscore type is equal to client underscore
credentials. And at the third place,
we pass Configure Object. First, Oh, to object, use a name P dot client ID. And next we have password
to ppt client secret. Now after Oath,
we add headers to object in double
codes content type, two in double codes, applications XWWWFm URL encoded. And that's it for this API. This API will generate
excess token for us, so we store that in
variable response. And then we simply
return response, dot data dot Xs
and as code token. Now for using await, we make this function async and also maybe this code
can give us error. So it's better to wrap this
code in try and cache block. So a try and catch block and move this
code in the Try blog. And in the cache,
we console dot log, error in fetching access token. And we write this
error dot message. Simply at the end, we do module dot exports
is equal to object. And here we add paper to paper, or we can write only paper and get access token
to get acessTken. Save this file and back
to our orders route. Now for creating a
new paper order, we will use another P API. You can get all these APIs in the paper documentation.
Don't worry about that. So cost response is equal to await Axios for
sending requests, and which request we
want to send post. So post at the first argument, we have to pass and point
Batis dollar Cali Brackets, paper dot aRLlasV two. Slash checkout slash Orders. Also, for using this
paper variable, which we define in the
paper config file, we have to import it at the top. Const CibacketsPaper, and
we also need gat access token is equal to require we go one folder
up, config and paper. Also, we import const Axios
is equal to require Axos. Now in our API in Axios
at the second parameter, we can pass the body
of the request. Here we pass object
with some properties. First one is intent. This sets the intent of
the order to capture. This tells people that
the payment will be immediately captured when
the user approves it. Because of this, we
will immediately capture the payment
in second API. Next property we add is
purchase underscore units. To array. This
represent the items or services the user is
buying and how much it cost. For now, we just
pass one object, and in that we pass description
to shopping cart order, and another property
for amount to object. Inside this, we have to pass in which currency we want
to accept payment. So currency code to USD
and then value to ten. We want from user
tenuous D value. Now, after purchase units, we pass application,
underscore contexts. This provides paper
with instructions on what to do after the user
done with paper payment form. So in this object, we have to pass two properties. First one is written
underscore URL. This is the URL on which
we want to redirect our user after payment
successfully verified by paper. And second property is
cancel underscore URL, this is the URL on
which we want to redirect user after payment
rejected or cancel. For now, leave this
field as it is. We will fill this when we
taste this implementation. Now, after the body
of the request, we can pass other configures
like header to object, content type to application, ZSN and we also have to pass authorization header
to Batis beer, space, and here we
will add people token. Can you tell how can we
generate that token? We can use GxssTkenFunction. At the top const token is equal to a weight because
this is AN function, get excess token in the
authorization header, we will pass dollar
Calibrakets token, and that's it for
this paper API. Now this API will create a
new order like written data, like order ID, and some links. Here we just need
to return link, which is the paper link. If we open that link
in the browser, then we will get
paper payment page. So we simply return respons
dot Json Object approval URL, two respons dot theta
dot Links dot find. Here we get single
link arrow function, link dot L is equal to codes, approve the outside dot
HRF, and that's it. Our first API is complete. Now let's quickly define our second API for
capturing the payment, and then we will test
this implementation. So router, dot post, and point to slash
paper capture Order. After that is in call back
with request and response. Now, capturing payment
is very simple. First of all, we will
get order ID from the request dot body
because from the front end, we have to send it and we need this order ID to
capture payment. Also, for verification, we
can put condition here, if Order ID is not available, then we simply return
response dot status, 400 dot Json Object
with message property, please send Order ID
or provide Order ID. Provide sounds good.
What do you say? Yes. Now if we have order ID, then we need token
for PL capture API. So Gs token is equal to await, G excess token, and at the end, we will simply call one API. Await axios dot
post for endpoint, we pass Bc taxes, dollar CLacketsP dot BRL two, slash checkouts Orders dollar
Cali brackets, Order ID. As capture. Now at the second parameter, we need to pass body. For this API, we don't
need to pass anything, so we pass empty object. Now we get the status and some details about the
capture from this API. So we can store that in
response, and at the end, we simply return response dot Json object
status to response, dot data, dot status. This status will
indicate that payment captured successfully
or not, and that's it. You can see how simple it is. We just need to call People API. Now you might ask, why can't we call these APIs
from the front end? Suppose we directly call this create order API
from the front end. Now, as we know, user have
excess of the front end. They can simply modify this amount value and
make it zero or 0.5. Imagine how much money
company will lost, and also some people will
misuse some informations. So that's why we
have to implement these payment features
in our back end. Here we complete our two
APIs for p. Don't worry, this is just a basic
implementation. We will improve these APIs after testing because in
the second API, after capturing the
payment successfully, we have to create
a new order for that user and remove
the card details. For now, we don't
want that complexity, so we will do that later
in this lesson, right? Before testing these APIs, we need paper client key, People secret, and
People base URL. Remember, we use empty
variables in the ENV file, we need to add these
three fields first, and for that, we have to set
up People Business account. Now let's set up Paper
Business account. Let's create a new paper account and taste this implementation. In this process, you may need some legal documents
and your bank details. So head over to paper
Developer page, which is developer.paper.com. If you already have
paper Business account, then it is good, but most of students don't have
paper business account. Here I am talking about
business account, not personal. You can also convert
your personal account to business account
from this setting. Here we click C sign up
for creating new account. Make sure here you select the business account
and get started. Enter your email
here and submit it. Now here, create your password. Make sure you follow this
instruction and submit it. Now, describe your
business type, I select your individual, and here we have to also
give this information. Product or service, let's
say, market places. I'm writing random details, but in the real world, you have to write your original details. Purpose code, let me
see what they have. Select your
entertainment services. After that, I have to write
my identity card number. For your country, it
might be something else, so you have to write
that correct details. Statement name to, let's say, God bless you and
submit the form. Now here, we have to fill the information about
personal and business, so I quickly fill these details. So these payment platforms change their
interface very often. So if in the future, you don't get the same
interface like I'm getting, then don't get confused. Just provide the
details they are asking and create business
account on paper. After creating an account, we can create client
ID and client secret. Also, here I select primary
currency to US dollar. You can choose whatever you
want to agree and continue. Here they are asking for
verify your identity. If you are planning to make your payments in the live mode, then complete this process
as soon as possible. For now, I just want to testing, select, do it later. This will move us on
the paperboard page. Now at the top,
click on developers, and as you can see, now we
are in the Sandbox mode, which means testing mode. Now to create client
ID and client secret, we go to apps and credentials. Click on Create App. Write your app name, anything. Let's say Cardwish node. We are creating account for merchant and simply create app. It is loading and see, here we get client
ID and secret. So first, copy client ID and paste it in our ENV
file client ID. Now, copy secret key and also
paste it at secret value. For paper based
URL, we pass TDPs, colon, double forward slash
api.sandbox.people.com. Good. Save this file. Now for testing with payment, we also need sandbox testing
account for sending payment. So we go to testing tools and se like sandbox accounts see, here we get two
testing accounts, one for business, and
one for personal. Right now, we don't need
to create a new account. If we need, then we will create. Now, as we know, we
need front end for tasting this API because
on the front end, we will open paper payment page. Now, as we know, we
need front end for tasting these APIs
because on the front end, we will open paper payment page. So here I created one simple SDML page call p
tasting dot SDML. You will get this page
below this lesson, and it is also available in the resources project to folder. Download it and simply add
it in your current project. Now, let's run this
SDMLFle in Browser. So go to SDMLFle in
the file Explorer or Finder and simply open
it in the Chrome. Here we get the title
and one simple button. If you want to see what happened when we
click on this button, then you can watch
the SDML file. It is really simple. See, currently it
is running from local files of our machine.
Now, here is the one thing. Remember, in the order API, we have to pass application
contacts. See, here. Basically, we have to pass the Success page
and cancel page. In the order API, we can't pass Local folder path, we have to pass URL. So what is the solution here? We can host our SDML
file on Local server, and then we will
pass that path here. Let me show you it
is really simple. So in the extension tab,
search Live Server. And install this extension. By using this extension, we can run our application
on local server. Back to files and right link on the paper tasting SDMLFle and select open
with Live Server. And see it is open
in the browser, but now it is running
on this port. Simply copy this URL, back to Vas code, and simply paste it in the written URL and
also in the Cancel URL, paste the same URL. But for checking, this
is working or not, we pass here cancel. In the real world to ask front end developer
which page they want to show on success and cancel and change the
path according to that. I pass this path
just for testing. Save this file and make
sure server is running. Here, I get error,
duplicate parameters. Oh, here I pass response
at the place of request. And also, let me
check another API. Yes, here also request. Save this file and make
sure our server is running. Good. It is working. Now back to room and simply
click on pay with papal. Nothing happens.
That's not fair. Let's check what happens. So pan inspect using FL, go to Console, and here
we can see we get error. Access to fetch or
create order API from origin Null has been
blocked by course policy. This is very popular error
for full stack developers. It happens because by default, our Express application can't accept API calls
from any origin. We have to enable course
in our Express app. So back to terminal and write NPM install CRE. And hit Enter. Now in the index js file at
the top, we import cost. Course is equal to
require course. Then at the bottom, before this express
DJs and middleware, we add app dot g
course, and that's it. See the changes. Make sure
our server is running. Good. Now simply click on
pay with paper button. Here I get error
in creating order, which means we are
getting error in API one. Let's check what is happening
here. So open Console. Here I get 400 bad request. So back to VS code
in the terminal, C, we get token as bearer. Enter your JWT token, which means we pass wrong
token from the front end. So open paper testing dot SDMLFle here we have
to enter our token. Open Postman, open login API, generate a new token. Good, copy this token, and in our STML file,
pass this token. Save this file, less this. Click on pay with paper. It will take some time and see, here we get login page of paper, which means our first create
order API is working well. Now before login, let's
cancel the payment. See, we redirect to cancel URL. Now go back two times for our front end paths and
click on pay with Paper. We get Paper login page. Here in the real world, our customer will log in with their paper
ID and password. But here for tasting, we will use Sandbox account. Think of paper Sandbox account is like Dummi
account for tasting. So we go to our Paper Dashboard. Here we have personal
account. Click on that. Here, see, we get login info copy email and on our front
end, add that email. Now back to Dashboard
and copy password and paste it in the password
and click on Login. Here we get written
to merchant error. We are not able to process your payment using your
paper account at this time. Please go back to merchant and try using a different
payment method. So let me try again. See, still, we are
getting the same error. Now let me try something else. Currently, my application
is based in Indian account, and here I am trying to
pay for India to India. Paper does not allow domestic payments within
India via paper wallets. Even in the sandbox account, we try a transaction between
two Indian accounts, then it will also give
us error for India to India payment have another payment gateway
like raiser pay. Let's create a testing
account for another country. Account type to
personal and here, select any country
but not select the same country as that business account.
Create account. Now let's try to log in
with this new account. Back to our front end. Here, I'm getting the same error because my account is logged in. So let's open this front end in incognito tab and
pay with paper. Here, I get login page. Make sure you log in with another country
account, Addhe email, and also add here password
and log in with this account. See, here we get the
paper payment page. At the top, we get
the user account, and here we get price to pay. Also, here at the bottom, we get various payment
methods like paper Balance, Credit Union by using card, A paper credit, and you can
also add your own card. This is testing account, and that's why we
don't do anything. Also, here we get the
address of this account. These are details of
the tasting account. Now let's click on
Continue to review order. S, here we get payment failed. Let me check what is wrong. Open Console using
F well and see, here we get error in
capture order API. So in our VS code
in the terminal, here I get this long error, so quickly scroll to the top. Here we get error, cannot read properties of
undefined reading status. So in our second API, we are not getting status. Let me check what is
wrong. Oh, sorry. Here, I forgot to write away. Save this file and
back to our front end. Let me try to remove
this token and pair ID from the URL and run
this simple SGML file. Now click on pay with Paper. See we get the papal payment, simply click on Continue
to review order. Here I again get payment fill. Remember, whenever we
apply any new features, errors will definitely come. Don't get frustrated, focus on the solutions, not on problems. See, here I get
internal server error. Let me check the terminal
request filled with Status Code 400 in our
capture order API, which means we are not
getting the order ID. Oh, here I only write
request dot body. Have to add dot order ID, save the changes, and in our
front end, pay with paper. Continue to review order. See, we move to
our success page, and here we also get
payment successful, which means our second API capturing payment
is also working. See, in our URL, also we get Token, which is the order ID, and we will fetch this
parameter in our front end, and then we call second
capturing payment API and pass that order
ID in the body. And by that we capture the
payment in paper account. Let's check our
business account, get the payment of $10 or not. For that, we have to
go Pap dashboard. In the testing tools, go to sandbox notifications. Now from this drop down, we select our business
account and click on search. And at the bottom,
we get the payment. If you don't get here payment, make sure you wait for
two to 3 minutes because sometimes it takes time
to show the payment here. Now if we open this, then we can see received
payment $10 from John, which is testing account name. Here we can see
transaction date. This is PST format, which is specific standard time. Don't get confused by that. Now you might ask why we get payment in this
business account. The reason we get payment in this business account
because using this account, we create our application and then we get client
ID and paper secret. That's why we get payment
in this business account. Now here we taste
payment for US user. See, here we get
the country code. Now for tasting another
country payment, we can create a new account. Select here personal,
which is buyer account, and here we can select country. Let's say I select here country, Canada, you can select any
country and create account. This is really fun.
See, at the top, we get the new account
for Canadian user. Open that user and simply
copy the email ID. Now back to our front end and here we go to simple
paper testing URL. Make sure we remove this token because it is the after page
of the successful payment. Now click on pay with paper. Here we again get the previous account because
we have logged in with it. So to remove this account, we click here on this JD icon. See at the bottom, we get Logout. Click on that. Now, click on this
change user email and paste here this
new account email. Now back to deskbard, copy the password and paste
it here and log in with it. Good. Now, see here at the top, I get the amount in order, but here from my account, I can pay in Canadian
currency, which is CAD. Also, we get limited
payment methods, and see here we get
people's conversation rate one CAD equals to 0.6 9549 USD. Here, people at their
transaction fees, but we can pay in our
country currency. Let's continue to review order. See, here we get
payments successful. And if we check our
sandbox notifications, search here for the
business account. It may take little time, and here we get
another $10 payment. At the bottom, as we can see, we get the location of Canada. Also, this business account
get their payment in the USD, even customer pay
in other currency. That customer needs to pay
the papal transaction fee. So as we know, our
payment is working. Now we can improve our APIs
and make it realistic. It is really simple.
Let me show you. So what we want to
change in the order API, so currently we are passing static amount here,
but that is not right. We have to pass the total
card price of the user. So let's get this
total card price. So before this get access
token, we write cost, cart is equal to a weight, cart Make sure you input this
card model dot Fine one. And here in the object, we pass user to request dot
user dot underscore ID. And also, for this user ID, we need to add Osmidalware here. Make sure you input
this as well. This will return card object, but we have to
pass her condition if cart is not available or card products dot
length is equal to zero, then we return response with status code 404 dot Json Object, message property
to cart not found. Now at the place of this amount, we have to pass card
dot total card price. And here we don't
change the currency because we want to get
payment in the USD. If you want to get payment
in another currency, then you can pass
that currency code in this currency code. But must check that currency
is accepted by paper or not. For now, let's taste this. Save the changes and go to Paper tasting dot SDMLFle let me explain you this
code really quick. So here we pass JWT token
for which we add product. Currently, in our application, we set only 2 hours expiry time, which is a little annoying
for tasting because here we have to create JWT
token again and again. So we can remove the
expiry from login and simply add expiry at the
final stage of the project. So on user routes
and at the bottom, we remove this object
with expired property. Now let's generate a new
token which never expire. Open Bostman and open login API and simply send the request. Good. Here we copy this
token in the SDML file, we simply paste it here. Also, you can pass here
your shipping address. Now, after that, here I added this on click event
for this pay button. In that, first of all, we call our order API using fetch method and
with this token. And as we know from
the order API, we will get the approval URL. So we get that here. If that data has not success, then we show alert with
error in creating order. What if we get approval URL successfully from the back end? Then we will redirect
user to that page. Now user enter
correct information and payment done successfully. Now people pass token
in the URL parameters, which is our order ID. So after a successful payment, we get the token in
the URL parameters. After that, we call our second API for
capturing the payment. And if that payment
captures successfully, then from the back end, we
pass status in the response. Here we check the
status is completed, then we show success
alert and if it fails, then we show failed
error, simple as that. Now, let's simply taste
our implementation. I'm very excited about it. Save this file and
now refresh the page, and let's again, click
on the payment button. We get error in creating order
if we check the console. See, here we get error. It is 400 errors, which means cart is not found. Or, remember, in the
remove cart lesson, we remove the card by removing the last products from
the products array. We have to add one
product in our card, go to Postman open
head to cart API. Here, we have to update token, copy it from the login API and simply paste it in
the authorization header. Good. Now let's
send the request. C, products added. It is iPhone, nice. Now let's refresh the page, and again, taste payment. See, here we get our card price. Lovely. Now here we simply
cancel the payment, and we move to paper
tasting dot STMLPage. Good. Now let's improve
our second EPI. Don't worry in that we don't
have to do much things. We will just create a new
order data in the database. Remove the user current card
if payment is successful. So here we pass condition I payment is
captured successfully, so response dot data dot status, which we get from
this capture API, if payment is equals
to completed, we write code here
in the I block. So here we create cost. New order is equal to new order. Make sure we input
this order model. Good. Now we have to pass
new order object here. So first of all, in this order, we add user to request dot
user dot underscore ID. But here for getting
user details, we have to adde or
middleware Good. Now, after that,
we need products, and in that we will store
all products which we order. And how can we get
that information? Right, we get it
from the back end. So before this order,
we find const, Card is equal to await
cart dot find one. And here in the object, we pass condition user to request dot user
dot underscore ID. Simply in the order products, we pass cart dot products. After that, total products
to cart dot total products. Next, we have total price, to cart dot total cart price. Next we have shipping address, which we need to get
from the front end. For now, leave it as it is. Next, we add payment
status to paid, payment ID to response
dot data dot ID. And if you want to save
some extra information, then you can assess to that
it really depends on you. Also, if you want
to add new field, then you have to add that
field in our order schema. For now, we pass all
information which is needed. Now we can save this new order, so await new order dot save and after saving the new order, we can remove our card
from the database. So await card dot DiltO
Here we have already card. That's why we directly
use card dot Delete one. If we don't get here card, then we have to use cart dot Fine one and delete
method like this. At the bottom, we
move this response in this blog and return
this response. This is really important. Now for safer side, we add condition and simply return here response
with status code, 400 SN Object, status
property to not complete, and message to payment does
not captured successfully. Try again later, and that's it. We just have to pass
zipping address in this order object. From where we get
the zipping address. We get it from the front end. In the capture order
API request body, we can get Zipping address, seconds shipping
address is equal to request dot body,
dot shipping address. Here we are getting zipping address because
from the front end, we already pass the zipping
address in the request body. See, here I pass the
zipping address. Now, in our order, we can set shipping address
to zipping address. Good. Now let me give
you one situation. Imagine user forgot to pass
this shipping address. How can we deliver the
product to that user? So we have to make sure user
pass the zipping address. So here we can
write I condition, shipping address
is not available, then we return response with status code 400
for bet request, and in the JSON object, we pass Message property to please provide your
delivery address. Now here is one thing. We know this
capturing payment API will run after user
pay with paper. We can't say now to user, they don't provide
shipping address. Here we can do one thing. We can also simply
put this condition at the very beginning of our
first API before getting cut. Here we have to get zipping address in
the request dot body. So Const shipping
address is equal to request dot body,
dot shipping address. Make sure you write the right spelling
of shipping address. Now our front end needs to pass zipping address in these
both APIs, and that's it. Here, our implementation
part is over. So currently we have two
iPhones in our card. We can check that in
the Mongoi become pass. See, here we have this card. Now let's test our
new implementation. So back to Browser, refresh the page and click
on pay with paper. We can login with any
personal test account. S, here we get the card price, and here I use Canadian account. That's why I get
this Canadian value. Simply, click on continue
to review Order. And see here we get
payment successful. And if we check our card data, see our card is
removed from here, and if we check the
orders collection, then we get here new order. I'm getting two orders because
in the previous lesson, also, I taste payment gateway
with order collection. And also, here we get address, payment ID, and
other more details. Here, this order status
is for business order, and payment status
is set to paid. Don't get confused by that. So that's how we implement
paper payment gateway. This is very important and
fun to create what you say. I know this is long one, but you can see what we have
implemented in single go. We implement whole
payment with testing. You can take 15 to
20 minutes break, enjoy this moment, and
then continue this course. Also, if you want
to apply payment in real world, then
from the paper, you have to switch to live
mode and then generate key ID and key secret and
use them in our backend. And also, you have
to change the paper was URL to simple SDDPs, call a double forward
slash api.paper.com. We just need to change these three variables, and that's it. You can accept
international payment from users using paper.
151. Getting history of orders: Now let's quickly define
other APIs for the orders. Let's find all orders
history of current user. After this papal API, we add router dot Get and
point to forward slash. Also, for finding the
user's information, we add Os middleware, and then using callback function with request and response. Now here we directly get const orders is
equal to a weight, order dot find, and here
we pass comparison Object, user to request dot
user dot underscore ID. Also, we need to short
this data by date. Dot short method, and
here in the object, we pass created at two minus
one for descending order. Here from the orders collection, we don't want to show all fills. Dot select, minus user,
minus zipping address, minus payment ID,
and at the end, we can send response, dot JSN and pass
here these orders. Let's test this API. So on Postman here
in the CardwzPject, we create a new
folder called Orders. In the Orders folder, we add a new request
called Order history. Now endpoint to SCTP, Column double for our
slash Local host, Column 3,000 API slash
Order and send the request. Sorry, we forgot to
pass the JWT token. So go to headers. First, we pass
authorization to bearer, and here we pass token. So we again login. Here we get token,
copy this token and paste it in this API
and send the request. See, here we get the
data of orders lovely. Now in the next
lesson, we will define our last API for this project.
152. Updating status by admin: Let's create API for admin. By this API, he or she can change the order
status to processing, C delivered or
anything they want. So router dot page because we want to change only one
property, not whole document, and point to slash order D status here we
also need the ID of order, so we pass Column Order ID. So we add Os middleware
for JWT token. And here we also need
to check the user rule. Is it admin or not? So we add here Jack rule, middleware, C auto
input did not work. And inside this, we have to
pass role, which is admin. Now at the top, let's
input this middleware. Const, check role is equal to require we go one folder up, middleware, and in
that check role. We already use this in
the products route. If you forgot, then you
can watch the route code. Now simply we pass acing function with
request and response. Great. Now first, we get the order from the
orders collection. SecctUdated order is equal to
await order dot find By ID. Also, instead of this, we can use fine
by ID and update. Here at the first argument, we have to pass order ID, which is request dot
params dot order ID. And at the second argument, we pass the object
of updated values. So Object order status to
whatever value at mean pass. And how can we get that value right from the request body? Here before the updated order, we had cost status is equal to request dot body, dot status. And here we pass order
status to the status. And after that, at
the third argument, we pass object with new to true. Will tell mangos to return new updated data in
this order value. We already seen that in
the Mongo DB section. Now it might possible
we don't found order. I updated order is
not availlabel, then we return response, dot status 404 dot Json Object, message property to
order not found. At last, we simply pass
response dot json, Object, message property to order
status updated successfully, and after that, updated
order to updated order. Or we can also remove
that and done. If you want to change something, then you can make those changes
according to your needs. Let's test this API,
open up Postman, add a new request in the orders collection
called update order status, select request to page, endpoint to slash API, order. Also select page, order status, and here we add the order ID. From the previous API call, I simply copy this order ID
and paste it in the endpoint. Now in our API, we pass the
header to authorization, and we also copy the value from the previous API
and paste it here. Send the request.
Here, I get error. Let me check the terminal. I know check role error. So at the top, I have typo, so I change this to check role. The changes and
send the request. See, here we get 403
forbidden error, access denied Admin only. We have to change the user
rule for this account. Go to MongoiVCompass, open user's collection and find your account by
which you logged in. Minus this, I simply change
the rule to admin update it. Now we need to generate
the JSN web token again. Go to login and
send the request, copy this token and replace our token
with this new token. Now in body raw, we also need to pass
JSON Object with field status to IB
and send the request. See, here we get success message and also updated
order data. Lovely.
153. Cleaning up code for index file: So currently our
application is clean. Just be messed up with
the index dot JS file. We can see this is
not looking clean. At the top, many, many require
functions for input. After that, we have code for creating logger and
global error handlers. After that, we
have connection to MongoDB then we apply some
middlewares and some routes, and at last we listen
to our server. There are many things happening
in this index dots file. We can make it clean and store each logic in
the separate file. It's not compulsory, but
many developers do that, but that can confuse you. So there is another way which is instead of
separating the code, we can make it clean by adding commands and separate
them from each other. Can choose anyway, it
totally depends on you. Let me show you my way. At the top, we have some config, so adhere, command
global config. After that, we
have this Express, Mongoose, Winston, and course. We addre command,
third party modules. That we have this app. We don't want to touch it now. At the top, we add
all our inputs. Some of these all route imports about this app and adde comment, custom modules, or you
can say routes module. There are no rules
for the comment. You can write the comment,
whatever you want to call. Just keep in mind this comment will be seen by
you in the future. So at that time, you
don't get confused. Now, after that, we
can adde command for this app, initialize
express app. Now here we have this logger
and also at the bottom, we have this port, so we can move it here and
we can call it constant. After that, for this uncaught
exception, we add catch, unhandle synchronous errors that were not caught in
try catch blocks. For unhandled rejection, we add catch unhandlePmise
rejections. By these commments,
we can remember why we add the code even
after the long time. After that, here we have
database connection. Then we have these two
middlewares, so middleware, then for static files, we add serve static files. Then for routes,
we add API routes. After that, we have
error middleware, so we add custom error handler, and at last start the server. Now, if we check our
index dot js file, see, now it looks a
little bit cleaner. Yes, separation of the code
will make this more cleaner, but it can confuse us also. You can also separate the code. I totally depends on you. So here, our ecommerce
project is over. Now, from the next section, we will jump on
our project three, which is social
media application.
154. Section 13 - Introduction of Project 03: Welcome to the new section of the ultimate Node JS course. From this section, we
are going to build a brand new project.
Can you guess? Yes, we are going to build social media application
back end using NodeJS. We will call this
project our Linky Pi. This project is one of
my favorite project. Let me explain to you what we
will cover in this project. In this project, we will create API for followers following, including sending requests
to private accounts, sending email from
our application. Also, we will create APIs for post with like and comments. Then we will create
API for chat, personal chat and group chat. Also, we will apply
real time chatting experiences with socket
and many more things. If you really understand
and build this project, then your portfolio
will really improve. This is going to be
fun. Are you excited? I'm really excited
and hope you are too. So let's start building
this amazing project.
155. Setting up Project 03: Now let's set up
our new project. So in the projects folder, I create a new folder
called our Linky File. Now, let's open this
folder in the VS code. Good. In the terminal, we write NPM in Y for initialization and for
creating package JsnFle. Also, let's create
index dogs file, which is our main file. Now, as we know, we will set up our application in this file. And for that, we
need some packages. So in the terminal, we
write NPM, install, Express, Mongos for Mongo DB, cours for enabling the course. Also, we add Dt NV, we need that and hit Enter. It will take some
time. Good. Now, let's quickly set up our application. First of all, Const Express is equal to require from Express. After that, st app is equal to, here we call Express
for listening this app, we add here dot LISN. Here at the first argument, we have to pass the port. After const app, we define
another const port is equal to process nw dot port or 3,000
or 5,000, whatever you like. In the production,
our application will take port from
the ENV port variable. At the second argument,
we have to pass the callback function which
simply console dot log. In tis, server is
running on port, and here we print our port
dollar calibracets port. Also, we have to add
some app middle wares. At the top course is
equal to require course. And here we add app dot g, simply call here course. And after that, for
passing data in JSON, we use app.us, express dot JSON. Without this
middleware, we can't get data in the
request of the body. Now let's see this
implementation. See the changes and
in the terminal, nodemon index dot js. C, server is running. Great. Now let's also connect this application
with database. For that, we need mangos. So at the top, cost mangos is equal to require from mongoose. Now, after this variable, we add Mongoose dot connect. Here, we have to pass the connection string
of our database. Previously, we directly
pass the string here, but that is little risky. Let's make this safe. In our project, we create
a new file called dot ENV. In this file, we create
a new variable called DVEs equal do, Mongo DB, column, double for slash, local host, column 27017, which is our local Mongo
B connection string. You can get that from
the Mongo Divi Compass, and after that, forwards less. And here we enter our
project name, our slinky Pi. Now to use this
environment variable, we need to configure D E and
V. So in the index Gs file, at the very top, we require
dot Env dot config. In the mangos dot Connect
method, what we will pass? Right, we pass process
dot w dot d. We can see how simple
this become after just creating one or
two node applications. This will improve
gradually and you will get more
comfortable with node. Now, as we know, this mangos dot connect
returns a promise, so we have to handle
that promise. Dot then method,
call back function, and here we simply
consult dot log, Mango Div connected
successfully. Also after then method, we add catch method for
handling promise rejection, and here we get error, error function, and
we console dot log, Mongo Di connection failed and we simply adhere this error. Let's also check
this implementation. Save this file, and
in the terminal, see, here we get Mongo Di
B connected successfully. For now, this is okay. We will add Winston and Lager at the very end of this
project as we need them. Now in the next lesson, we will create our user model.
156. Create User Model: Now it's exercise time. So this is the small
sample of user document. Based on these, you have
to create user schema. You can also watch
previous project code. Don't worry about that. So spend some time and
complete this exercise. And after that,
watch the solution. Now let's see the solution. So in our project, we create
a new folder called models, and in that, we
create a new file called users dot js. Good. Now, first of all,
we import cost, mangos is equal to
require from mangos. And after that, we define const, user schema is equal to new mangos dot schema and here we pass the
schema in the object. Now, first of all, for
our social media app, we need username to object, type, to string
required, to true. Also, we don't want white
space in the username. So for that, we pass
stream to true. Also mean length to three, and max length, let's say 30 or 40, whatever
you want to take. After user name, we add email
to object, type to string, required to true,
unique to true, dream to true because we don't
need space in email also. And we always convert
our email to lowercase, so lower case to true as well. After that, we need
password to object, type to string, and
required to true. We also want to store some
profile details of the user, and these details user can add from setting like Instagram. It should not compulsory. So we can get simple
profile name, which is type to string, and we don't need it's unique and also we don't
need it compulsory, so we don't add here
any validators. Now bio to object,
type to string. And max length to 150. Next, we add accounts, status, type to string, um, to array. Here, we pass values, active, next, disable and bend. We set default value to active. Next, what we can add, is verified to object, type to bullian and
default value to false. Also, after that,
we add is private, object, type to bullian
and default to false. Next, user can add gender
type to string, um to array, and here we pass male, female, non binary, or user can
say prefer not to say. Next, we also take
phone number to object, type to string because user
can add country code in this. And here we also trim to true. Now what we can add, I think
this is enough for now. If in the future, we
need more details, then we can add more
fields in future. Here we have our schema. Now let's create users model. So cost user is equal
to Mongos dot model. Here, we pass singular
name, which is user. And second, we pass the schema, which is user schema. Now, to interact with
this user model, we need to export this. So module exports is
equal to user and done. Now in the next
lesson, we will create API for register a new user.
157. Registering a New User : Let's create our first
EPI for this application. Here, we create a new
folder called Routes, and inside this folder, we create a new file
called users dot js. Good. Now, first of all, we need to create Router. We import Express is
equal to require Express, and after that, cost Router is equal to express dot Router. At the end, we simply module dot exports is equal to Router. Now let's add this route in
our main index dot js file. After these inputs, we input cost user routes is
equal to require. Here we go to Routes
folder slash users. Now, after the middlewares, we add app dot U. Here at the first position, we add prefix for that route. So slash API user. And here we simply
add user routes. Now let's create signup API. So back to users route file. Here we add Router dot post
and point to forward slash, and then async callback function with request and response. Now, first of all,
we need to get fills from the
request of the body. But the question is, which fills we need for registering
a new user. So for social media
applications, companies mostly
take minimum data for registering a new user. Because if we take ten to 15 fills as first
step of the process, then not many people will create account on our social
media application. So it's better we only take that data which is
required for our schema. Also, user can easily
fill those details, and that's why if you notice, almost all social media
application only takes name, email ID of the
user and password. Only these fills. They take other fills later from
the user settings. That's why opening account on social media apps
are really simple. Here we will take only
three fills from the user. Cost object is equal
to request dot body, and here we get user
name, email and password. Now, if we don't
get these fills, then we return error. So I username is not valid
or email is not available, or password is not available, then we return response
with status 400 and we pass Json object with error message missing required form fields. After that, we also
pass success to falls. This success field will help
front end to show errors. Now, also, here we find user is already available in
our database or not. Const user is equal to a weight. Here we add user model, C, auto Input works dot
Fine one Object. And here we need dollar
or operator to array, and here we pass multiple
conditions in separate objects. So our first condition
is username to username, and second, email to email. If any of this
condition is true, then we get that user. So here we pass condition. If user is available, then return response with status code 400 dot
Json object message to here we pass condition. User dot user name is
equal to our user name. If this is true, question mark, username is already taken, else email is
already registered. And after that, we
pass success to falls. Now, if user is not
already registered, then we simply
create a new user. So cost new user is
equal to new user. Here we pass user object, user name to user, or we can simply remove this email to email and
password to password. Now, as we know, we don't pass here password in normal text, we need to encrypt it. And for that, which package
we use, try to remember it. Yes, it is crypt. So NPM install,
crypt, and hit Enter. Good, minimize the terminal, and in our routes
file at the top, const, crypt is equal
to require crypt. Now in our API, we create Cost het pass
is equal to await, we create dot s. And here
at the first argument, we have to pass our password, and at the second argument, we pass the salt
number, which is ten. Now we can simply set
password to st password. And after this, we
can simply await new user dot c. What do we want to do
after creating new user? Right, we generate JWT
token for that user, and for that, we need
JSN web token package. So NPM install JSON web token, at the rate 9.0
0.2, and hit Enter. Good. Now minimize
this terminal, and let's input this JSN
web token at the top. Sacst JWT is equal to
require JSN btgon. Now, as we done previously, we create function for
generating JSN WebTgon because we also need that in our
login API SacstGenerate, token is equal to here we get the data as parameter
error function. And in this, we simply
return JWT dot sign. At first, we pass the data, and after that, we need
to pass JWTs secret key. So process dot nw dot
JWT underscore key. Now we need to add this key
variable in our ENV file. JWT underscore key
is equal to here, we can pass any key
which is secure. For example, JWT security key. Don't use this for production. I pass it randomly. You have to create
your own security key. Also, here I don't
add expiry for our token because for
social media application, token expiry is not good. Users wants to quickly
access our website. Also, imagine you visit Instagram every day and every
day, you have to log in. Should you will use that
app for a long time, no. So as a developer, we need to always think from the
user's perspective. Now, in our API route callback, we simply const token is
equal to generate token. Here at the bottom, I remove Typo from
my function name. Now in our generate
token function, we have to pass user's data we want to add in
our token pay load. So Object, underscore ID to
new user dot underscore ID, and user name to
newser dot user name. And at the end, we simply return response dot status to 01 for
new data creation dot GSN, and we directly pass this token. Now, let's taste this API. So open up Postman. Here we create a new collection, create blank collection for our new project
called Our Linky Fi. And in this folder, we add a new folder
called users, and in this folder,
we add a new request, called register, a new user. Good. Now request type to post, and point to SDDP, Column double forward
slash local host, Column 3,000 slash
API slash user. And send the request.
See, here we get error, 500 cannot destructure
missing fills. Let's pass all form
fills which are needed. Select body, raw, and here
we pass our JSON Object. User name to code,
underscore, bless. By the way, this is my
Instagram handle name. Next email to code at
the red gmail.com. And last, we send password 212-34-5678 and
send the request. See, here we get
token as response. And if we check our database, here we get our
linkifed database, and in the user's collection, see, here we get
also new user data. Also, we get account status to active is verified and is
private, both are false. We set all these values as
default in our users schema. Also, here I forgot to
add unique to true for this username
because here we want this username to unique
for all our users.
158. Exercise - User Login API: Now let's do another exercise. In this exercise, you have
to create a login API, which will verify user name
and password of the user. We already done that in
our previous project, but I want you do
that by your own. You can see this
verify password code, but the rest of the API, you have to create by your own. I know you can do that, so complete this exercise, and then what's the solution.
159. Solution - User Login API: I hope you complete the exercise
or you try to solve it. Now let's see the solution. So Router, dot post, and point to slash login. And here we pass async function with
request and response. Now, first of all, in
this callback function, we get the data
from request body. So cost object is equal
to request dot body. And here we get to user
name and password. Now we can pass
condition if username is not available or password
is not available, then we return error. So return response,
status, 400 Json. Here we pass Object
success to false and message to please provide
username and password. Now after that, we will find the user with this user name. Cost user is equal to
await user dot Fine one. Here in the object, we pass user name to user name, or we can also remove this. Now it might possible
we don't find any user with the
given user name. If user is not available, then we return
response dot status, 400 dot Json Object with success to false and message
to invalid credentials. Now if we found the user, then we need to compare the
password using BCRP Library. Second, valid password
is equal to await, B crypt dot compare. First, we pass input password, which are front ensend
in the request body, and at the second argument, we pass user dot password. We simply pass here
another condition I valid password is
false or not available, then we return response
dot status code 401, which stands for invalid or
missing authentication token. Also, we send Json
object with success to false and message to
invalid credentials. Here, if we specify
password not matched, then it means we found user and just password
is not matching. Also, here I think
we have to pass the same status code
for user not found. Change this 400 with 401 and message is also
invalid credentials. Now, if password is verified, then we can generate fresh token and send it in the response. So const token is equal
to generate token. Here we pass data object with underscore ID to user
dot underscore ID, and username to
user dot user name. At last response dot Json
send this token. That's it. Now let's taste this API. In the postman, we create a new request called
Login a user. Here we select SDTP method
to post endpoint to SDTP, Column double for or slash, local host, Column
3,000, slash API, slash user slash login, and send the request. See, here we get error, cannot destructure
property user name. So here we have to
pass data in body. Select body, raw, here we pass object with
username to code, bless and password to 12345678
and send the request. See, here we get the message, invalid credentials because
I pass a wrong user name. I change my username to
original code underscore bless, and send the request. See, here we get the token, which means our API
is working well.
160. Implement Access Token & Refresh Token [UPDATE]: Now as we have seen in our previous project on the
registration and login, we return to tokens, 14 access token, which is
sort leaf or SOT expiry, and another is refresh token, which is long leaf or
long expiry token. In short, when the access
token get expired, front end send request to
some endpoint like repress. In the repress we verify
the refresh token, and then we return
a new access token. This is the logic. Now let's quickly implement this
in our third project. So at the bottom, we have
generate token function. We can rename this
to generate tokens. Now we need to return
to tokens from here. So we store this first token in the variable called
excess token, and then here we set it expires in property
to let's say 1 hour. In production, we can add excess token expiry
to three or 4 hours. But for easily testing
for this application, I'm not setting expiry
time for excess token. Again, I'm telling you
for the testing only, so we don't need to generate another excess token
our upcoming APIs. Now duplicate this line, change it variable
to refresh token, ta, we change it to object, underscore ID to data
dot underscore ID. And here we add expiry scene to 30 days because this is social
media application at last, we simply re an object with access token and refresh token. Also, we set
different secret key for access token
and refresh token. It's not necessary, but
it's better we do that. Pt NV file at the
place of JWT key, we add access token, key and after that, we define another variable, repress token, key is
equal to our secret key. Save this and back to
our user out file. Here, we change secret
key to access token key. And for refresh token, we change it to
refresh token key. Now in the log in API, change this function
name to generate tokens. At the place of getting token, we get object and we destructure excess token and refresh
token from this function. Good. Now here we re an
excess token in the response. Also, can you tell
me what we have to do before sending the excess
token in the response? Right. We have to store refresh token in the
user's collection, and then sell refresh token
in the SDTP only cookie. So here we await
Bcrt dot s. Here, we pass refresh token, coma we pass here salt to ten. This expression return he token. So we store it in variable
called Nu he repres token. After that, user dot
repress token is equal to New has repress token. Then we have to await user dot
C. Now we just have to set refresh token in the
cookie to remember how we use response dot Cookie. Here, we pass the cookie name, which is refresh token. Here we add refresh token
and at the third parameter, we need to pass
configuration object. Here, first property
is SDDP only, which we set to true, secure to false, but make sure for production,
you make it true. Same site to streak
for same domain, but currently, we set it
to none, and at last, we can pass message to 30
days into 24 hours into 60 minutes into 60 seconds into 1,000
milliseconds and done. Now we have to simply copy this logic from token
generation to sending response and paste
it here in this API. And here, we have to change only user dot underscore ID to new user dot underscore ID, new user dot user
name, new user, dot refresh token, and
also Nwuser dot save. At last in the response, we also add status to 201.
161. Refresh & Logout Route [UPDATE]: Now, let's create
route for refresh, so Route dot post slash refresh and here we add ASN callback function with
request and response. Now, first of all, we need to get refresh token
from the Cookie. So const user refresh
token is equal to request dot Cookie,
dot refresh token. Here we don't get this cookie because in
our Express middleware, we didn't ddt Cookie parser
middleware, and for that, we need Cookie parser
package, open up terminal, and here we write NPM install Cookies
parser and hit Enter. Good. Now open
index dot gs file. At the top, we input const Cookie parser is equal to
require Cookie parser. At the bottom, we add app
dot g, Cookie parser. Make sure you call
this function here. Save this file and
back to our route. Here, we pass condition. If user refresh token
is not available, then we return response
dot status 401 dot Json, and here we pass message. No, refresh token provided. After that, we need user ID
who send request for access token and how we will get
that from the refresh token, we have to decode
this refresh token. Cost decoded user is equal to JWT dot Verify here we pass user refresh
token and after that, pass the refresh
secret key process dot ENV dot refresh token, key. Now, this expression might
return error, so we add here, try and gatchblog simply move
this line in the dry blog. And in the cache blog, we return response
dot status 403, 44 PDN dot JSON, and we pass your message
to invalid refresh token. Also, as we know, when we define
variable using cost, it will accessible in
this dry blog only. So we have to define
it before dry blog. And remove cost from here. Now, after that, cost user
is equal to a weight, user dot fine BYD. And here we pass decoded
user dot underscore ID. And then if user
is not available, then we return response
dot status 404 dot Json. And here we add message
property to user not found. Now, if we found user, then we have to compare tokens, and for that, we simply
use BCRP package. So await, BCRP dot compare. We pass user refresh token, which we get from the cookie and compare it with user
dot refresh token. This expression return the
result for the comparison. Secons is valid. And also here we pass another condition I
is valid is false, then we again return
response dot status 403 dot Json with message to
refresh token is not valid. Now if token are valid,
then what we will do? We will create new tokens, store refresh token in
the user's collection, set refresh token in
the SDDPOly Cookie, and then return
the excess token. These steps we already done in the register and login API. Copy the we simply paste it
for refresh API and done. Now let's quickly define
API for logout also. Then we will taste these
two APIs together. Route, dart post,
point to slash Logout. Remember for this
project in front end, we have to hit API
for slash API, slash user, slash
refresh or Logout point because we add them
in user's route. Here, we add ASN callback
with request and response. Now, can you tell me what
should we do in lockout? Right, it is really simple. We have to remove refresh
token from the cookie, and then we simply remove the token from the
user's collection also. Also, while talking with you
about user's collection, I remember we didn't add refresh token filled in
the user's collection. So let's do that first, and then we will complete
this lockout route. So one user's model,
and at the bottom, we add filled astken to
object, type, to string. We don't adhere required
because when our user lock out, then we need to
remove the astken. If we add required, then it will not
allow us to do that. Simple as that. Now
in the logout API, starting task is very
similar to refresh API. For example, we also need to
get token from the cookie, find user's refresh token, and then make it null. So let's copy code from the refresh API
and paste it here. Good. Now let's check
this code from the start. This is good. This is also
good till we get the user. Here, we don't need to create tokens, so we can remove this. Also, we don't need
to as the token, so we can remove this line, and then here we store
null in the repress token. Now at the place of response, dart Cookie, we use
response, dot clear Cookie. At first, we pass the
cookie name which is okay. After that, here
we don't need to pass this refresh
token variable, but we need this
configuration object, and at last, we simply return message at the
place of access token, locked out
successfully and done. Now let's quickly
test these APIs. So one Postman in
the user's folder, we create a new request
called repress access token. We need post request. Point to SDD P, Column double forward
slash, local host. Column 3,000, slash APIs user, slash repress and
send the request. See, here we get error
because we don't have Cookie, go to Login Route, send the request
with write details. See here we get token and also repress token
is set to Cookie. Now back to repress API, send the request again. See, here we get the
new token. Great. Now let's taste Logout API. Simply duplicate this
request taste and we change the request name to
let's say log out a user, and also change the API
endpoint to slash user, slash Logout, and
send the request. See, we get success message, and if we check the cookie, see, here we don't
get a refresh token, both APIs are working well. Also, I want to tell you this current and previous
lesson are updated lessons. If in the future, you don't see this code in my
screen recording, then don't worry, you can absolutely follow those lessons. I just want to make the coures
as up to date as possible. You will also learn current
best practice for Node jazz.
162. Current Logged in User Details: Let's create another API for sending the logged
in users details. So here we add another router
dot Get point to forward slash and simply pass here ASN callback function with
request and response. Good. Now in this function, cost user is equal to
await user dot find By ID. And here we simply pass user ID. But how can we get that ID? Yes, we need to extract
that ID from the JWT token. And for that, we need
to create Osmddalware. For now, we complete this API, and then we will
add Othmidalware. Here we pass request dot
user dot underscore ID. Now, we don't want to send
password with this data. We add here dot select method, and in the string, we
add minus password. After that, if we
don't get the user, then we pass I condition, user is not available, then we return response
dot status 404. Also pass dot GSN Object with success to falls and
message to user, not found. If we found the user, then we simply return it
in response dot Json user. Now many students might ask, can we see the
previous projects code while applying it
in our new project? Yes, of course, you
can see that code. Just think, is there something which you can
improve in that code? If yes, then improve it, and if no, then stay
with that code. There is nothing
wrong about that. Let's create Os middleware. So in our project, we create a new folder called middleware. And in that folder, we create a new middleware file
called oth dot js. Now, first of all, here
we create a function called OT and we know this
is middleware function. So we will get here
three parameters, request, response, and next. Now in this function, we first get a token from
the request header. Do you remember in which header, our front end will
pass the token? Write in the
authorization header, SecondstO header is equal to request dot headers
dot authorization. After that, we check
the condition. If auth header is not available, or oth header dot starts
with here in codes, we pass Barr space. If this is not true, then we return error. So return response, dot status 401 for unauthorized
Json object, success to force, and message to authorization,
token required. Now if we get oth header, then we need to extract our
token from that header. So cost token is equal to
oth header dot split method, in codes, we pass pace, and here we need second item, so square bracket, index one. We already seen this in
the previous project. Right. So now we have a token. The only thing is we
need to verify the token and set our users data
in request dot user. Secct decoded user is
equal to JWT dot Verify. Here, first, we pass our token, which we get from the header. And then in second argument, we pass process dot EV
dot JWT underscore key. We can simply set this
variable as request dot user. Request dot user is
equal to decoded user, and then we call next function. This is most important thing. Now, what if we don't
get this decoded user? If we don't handle this, then we will get error. So we handle this with
try and catch block. Here we try cache block and simply move these three
lines in the try block. In the cache block, we simply return response,
dot status, 401, dot Json, Object, success to false and message to
invalid token. And done. Now let's export this function. So module dot exports
is equal to OT. Now back to our users route. Here in our GIPI, we add oth middleware, see at input works. Nice. Now let's taste this. So when Postman, here we
add new request called current locked in
user point to SJDP, Column double four
slash Local host, Column 3,000 API slash
user and send the request. See, here we get error, authorization token is required. So from the previous API call, we copy this JWT token, and in our current
API, we go to headers. Here, we add
authorization header. Value to Barr space and paste our token
and send the request. See, here I get invalid token, but why I pass the valid token. Let's check the terminal. See, server is also running. I think error is happening
in the dry and cache block. That's why my server
is still running. So here in the cache block, we add console dot
log this error. Set the changes and
send the request again. Now back to VS code,
back to terminal, and here we get error, JWT is not defined. Oh, I forgot to import JWT from the JSON
Web token package. So at the top const, JWT is equal to
required JSN web token. You can see that's how
we can solve error. Don't get panic if errors
occur in your code. Try to solve it step by step. If many errors occur, we can learn more
about our mistakes, and by that, we can
improve our code. So don't worry about errors. See the changes and take a look. See, here we get the usage
details without password.
163. Resetting User Password: Now let's implement reset password feature in
our application. First, let's understand the
overview of this feature. So when user enter their email and clicks on
the reset password button, then we will call our first
API request Password Reset. Now, this API will
generate new token and send a URL with that
token on their email. When users clicks on
that website link, we will take new
password from the user, and when users clicks on submit, we will call our second
API reset password. In this API, we will verify that token and update the new password in
the user document. If this all works fine, then we will return message, password, successfully reset. Here, we have to
create two APIs, request password reset,
and reset password. So let's start with
creating first API, so Router dot post endpoint
to request Ds password, D reset, and also we add ASN callback function with
request and response. So here we are not
adding orthomidal ware because we want anyone
can reset their password. They are doing reset password
because in the 99% cases, they forgot their password, so they can't log in, and that's why we don't add
here orthomidalware. Now, first of all, we get a user email from
the request body. Secons object is equal
to request dot body. And here we get the email. Now using this email, let's first check user
is available or not. So const user is equal to
await user dot Fine one. And here we pass comparison
object with email to email. Next, we pass condition. If user is not available, then we return response
which status code 404 dot Json Object with success to falls
and message two, this email is not registered, or we can also pass
user not found. It's totally up to you. Now if user is available,
then what we will do? Right, we will generate token. So cost, reset token is
equal to JWT dot sine. First, we pass our
data, so object, underscore ID to user
dot underscore ID. And at the second parameter, we pass JWT key, so process dot Env dot
JWT underscore key. And also at the third argument, we pass object with
expiries in property to 1 hour because here we want to add these
expiries in property, and that's why we don't use
here generate to confunction. We have reset token.
Now we just want to send it to our user's email. Sending an email is
a separate logic, so currently, we
don't implement it. First, we create
our two reset APIs so it will not confuse you. So here we are writing
comment for sending email and then simply
return response Json. Here we pass Object
with message property, password reset,
link, send to email. And after that, we also pass
reset token to reset token. Sending this reset token in response because we
don't send email yet. It is just for testing. After sending email,
we don't need to pass Reset token
in the response. Now, let's define our
second API in which we will verify that Reset token
and save the new password. So router dot post and point
to slash reset password. And here we add ASN callback function with
request and response. First of all, we get fills
from the request body. So Const object is equal
to request dot body, and here we destructure
reset token and also we get the new password which
user wants to update. Here we need to
perform two steps. Step one, verify the token, and step two, if
token is verified, then update the password.
Simple as that. So to verify the token, we can use JWT dot Verify. At first, we pass
our reset token. The second argument,
we need to add our JWT key process dot ENV
dot JWT underscore key. Now, this expression
will give us users data which we pass
when we generate this token. See here at the top, we pass underscore ID to
user dot underscore ID. We store that in variable
called decoded user. Now we find user using that underscore ID and then
update the password. Let user is equal to await
user dot find by ID. And here we pass decoded
user dot underscore ID. Now after that, we
pass condition. If user is not available, then we return response with Status Code 400 dot
Json Object with success to false and message
to invalid or expired token. Now if we get the user, then we will simply
update the password. Here, for password, we need
to first has that password. We can't store it as it is. Await, crypt dot
has here, first, we pass new password, and at the second argument, we pass the Salt, which is ten. This will generate a password, so we can directly store
that in user dot password, and below that, we simply
await user dot CV. And at last, we simply return response dot Json object
with message property, password reset successfully,
and that's it. In the first API, we generate the reset token and
send it with email, and in the second API, user will send that reset token back to us and we
will verify it. If it is verified, only then we will
update that password. Here in the token verification, we have little security issue. Here we are just verifying
the token using JWTkey, but this is not much secure. Let me explain you
with simple example. Suppose users send requests
for reset password. In the back end, we generate a reset token and
send it to the user. Now it might happen, user can again send the request
for the reset password, and we again send the new
reset token to the user. Now these are two
tokens which is validate for resetting
the password. Suppose user pass
the second token and change the password. Now, what if this first
old token hackers get and they change the password using
this old reset token. This implementation
is little risky. Now, what is the solution here? It is really simple. When we generate
a reset token in our first API at the time
before sending the email, we will store the reset token
in our user's collection. Also we store token
expires and time. Now when we verify the token, we will compare the stored reset token with user's reset token. If both are matched and
token is not expired, then we will update
the password. By this approach only, the latest reset token is valid and only for
the expires and time. This approach is more secure
than the previous one. So let's implement this. Here before we send email, we write user dot Reset token
is equal to reset token, and user dot reset token, expires is equal
to date dot now, plus, we want to
add 1 hour expires. So 60 minutes into 60
seconds into 1,000. Converting seconds
into milliseconds. And after that, we can await
user dot c. Also at the top, we convert this const
user to at user. And also, we need to add these two fills in
our user's model. So when user Schema, here, at the end, we add reset token to
object, type to string, and after that, reset token expires to object
and type to date, save the changes, and
back to our reset API. The verification part, we add condition if user
is not available, or user dot reset token
is equals to reset token or user dot
reset token expires is less or equals
to date dot now. If any of these
conditions are true, then we will return
token expired, and token is not verified. Also, after updating
the password, we need to make these
two fills null. Before user dot c, we set user dot reset, token is equal to null and user dot reset token
expires is equal to null. This is more secure. If user successfully reset the password, there are no tokens which are
valid for reset password. Now let's test this
implementation. In the Postman, we create
a new request called request, reset password, method, to post point to SDDP, Column double forward
slash local host, Column 3,000, slash API, slash user, slash
request, reset password. And in body, go to raw and here we pass JN
object with email. Make sure you pass
valid Email and also that email should
be in your user email. Otherwise, we will
get user not found. If you don't pass valid
email in the user's data, then from the Mongo Di become
pass, you can change that. Now send the request here, I get Ken not found. Let me check the endpoint. Oh, here it is request, there's password, let's reset. And in my endpoint,
I write request, let's reset, des password. So I change this endpoint
and send this request. See, here we get a reset
token, copy this token, and we need to create
a new request called reset password method to
post and endpoint to SDDP, Column double forward
slash local host, Column 3,000, slash API
user, slash reset password. Also, here we need to pass body, raw object with reset token, and paste that token. And then we pass
the new password. 123-45-6789. Now send the request. See, we get password
reset successfully. So both API are working. Now, the only thing we want
to do is we need to send this token or reset page
link in user's email, and we will see that
in the next lesson.
164. Ways of sending email in Node JS: Now there are many ways to send email from
node application. First one is by using SMTP, which is simple mail
transfer protocol. Second way is by
using SendGrid API, and third way is by Amazon SS, which is simple email service. Now let's see pros and
cons for each way, which will help us to decide which email service we can
use for our application. So SMTP works best for small applications because
it is simple to set up, we need to pass only email
and password of our account, and then SMTP will send that
email from our account. But SMTP is little slow in compared to
other email services. Also, with SMTP, we can only send limited
amount of emails. For example, if we use Gmail, then we can only send
500 emails per day. If we use Yahoo, then we can send only
100 emails per day. SMTP is not scalable
for big businesses. It is okay for
small applications. Now let's move to SNGrid. In SendGrid, we
don't need to pass email and password
of our account. Instead of that, we
need to generate APIKey and we will send email
using SendGrids API. So this is fast and reliable. It is also easy to set up. Many companies use SendGrid for sending bulk
emails like Uber, AirBnB, app, et cetera. But for sending
unlimited emails, we need to purchase
its paid plan. Now let's move to Amazon SES. This is the most trusted and
secure email service used by big companies like
Netflix, LinkedIn, et cetera. It is also very fast. We can send millions
of emails per day, and also their paid plan is
cheaper than Sandgrad plans, but the only cons is it
is little hard to set up. We can manage that. So here, we can clearly see
that if we need speed, security, and low cost, then we can use Amazon SES. So in the next lesson, we will implement Amazon SES
in our application.
165. Setup Amazon SES for Sending Email: Let's implement Amazon
Simple email survey in our node application. It is really simple. I break
this down in three steps. Step one, configure the SS
in the node application. Step two, creating
an Amazon SS account and verify email for testing. Step three, we will send
testing email from our API. Here I want to clear one thing for setting up the
Amazon SS account, we need payment card details
and identity details. We don't need to pay anything, we need card details. Here we will use pre
version of Amazon SES. So if you don't
have that details, then you can skip this
lesson and you can apply free email sender using
SMTP in the next lesson. To implement Amazon SES, we need to install AWS package. So NPM install at dit AWS sdk client Ss at the rate 3.738
0.0 and hit Enter. Good. Minimize the terminal. Now in our project, we create a new folder called Config
and in that folder, we create a new file
called Amazons dot js. First of all, we need to configure SS client
and for that, we need SS client method. So Const CL brackets. Here we get SS client
is equal to require at the rate aws SDK client Ss. After that, const,
as client is equal to new as client and inside it, we have to pass the
initialization object. Now the first property
of the object is region. Here, we have to
pass our region. We will store the
region in the ENV file, so we add here process
dot ENV Aws Score region. After that, we need to
pass credentials property. Now, this is really important. Without this, we
can't send email. Here we pass object
with excess key ID, and also we need to
pass secret excess key. These both properties we
will get when we create account on the Amazon
AWS. Don't worry. First we set up all these
in our node application, and then we will create
new account on Amazon Aws. So here we also pass
process dot An dot Aws, underscore Xs, underscore key. For secret, we pass
process dot n dot Aws, underscore, secret
underscore key, and done. Now we just need to create Send Email function and
add some email fills. So Const send email is
equal to arrow function. Here, before we forgot to add try and cache
block, we add them, and in the cache,
we simply console dot log, Amazon, SCS error. Here we add this error. Now, let's write our
code in try blog. If anything goes wrong, then our cache block
will console that error. Now for sending the email, we need to provide some
information from email, we want to send email to
which email we want to send, what is the subject, what
is the body, et cetera. So cost params is
equal to object. Here, first we a source, here, we have to pass our
verified email address, which we will verified on
the Amazon SS website. Don't worry, don't write
anything for now. Leave it plan. Make sure you write this
properties name in the capital, same as I write. Otherwise, you will get error. Now, after source,
we add destination, addhee object with
property to address, and here we have
to pass array of emails to whom we
want to send email. We can also send emails
to multiple users. Now, how can we get that email
address in this function? Right, from the parameters. So here we add two, which is the email address. Also, we need subject of the email and also we need
the text of the email. Now, here in the two address, we add two parameter. Here, if we have multiple, then we can add those email
IDs as well with comma. Now after the destination, we add message
property to object. First, we add subject to object
with data property here, we have to pass the subject which we get from the parameter. Now after subject, we
add body to object, text to object, and
inside this data to text, which we again get
from the parameter. I know this is a
little weird syntax, but it is what it is. We need to follow that. Now
we have our perms ready. Now we can create command
for these perams. Const, command is equal to
New send email, command. S, auto input works, and here we simply pass perams. Now we need to send this
command for sending the email. So Ss client dot SN, and here we pass command. Now as we know, sending the
email will take some time, so it will be an
asynchronous task. And for that, we adhere await. And because of await, we need to make this
function async. Now let's do the response
in the response variable, and at the end, we simply
add console dot log, email sent successfully
message ID column, and here we return
response dot message ID. At the end of this file, we simply module dot exports
is equal to send email. Make sure you don't call
this function here. We just need to
pass the reference. Also, here in the sources, we pass process dot En
dot Aws underscore, email, which will be our email from which
we want to send email. So here, our step
number one is done. We just need to add
these three variables in the ENV file and we need to
also pass this source email. Head over to browser and
go to aws.amazon.com. And here we need to
create a new AWS account. Here, we write our
email address, and here we need to pass
our AWS account name. For now, pass anything, we can change it later
from the settings. Click on Verify email address. After that, it will
ask for password, simply create a password and continue here we see
like personal plan, and also we have to
pass our details. I quickly write my details
and agree and continue. Now here, we have to
pass the card details. If you don't have
the card details or you don't want to
give your information, then you can implement SMTP because most of students
don't have card, so you can implement SMTP, which only uses your email. I will show you that
in the next lesson. For now, I pass your card details and
then verify and continue. This will process
small payment like $1 or even smaller for
your card verification. So here I enter my OTP payment is processing and here
it is done payment. Now we have to provide identity. Select your personal use. If you want to use for business, then you can select that also. Next, we select individual, and here we need to
provide identity document. So I quickly fill this form and also upload my document
and click on Continue. Here provides some more
details for verification. I write here my phone
number and send SMS. If you're from
different country, then you might get another fill. Here, I verify my number
with OTP and then continue. Now here we just set
this basic free plan, and finally complete signup Dn go to the AWS
Management Console. See, here we are at the Console. First of all, we will
verify our sender email. So at the top, inserg SEs and
open this Amazon SS page. Click on these three
lines and go to identities to verify
email or domain. Here, we have to
create identities. Now if you want to
use for production, then you have to verify
your domain here. But now for testing, we can use your email. Here, we write our email
address from which we want to send email to users and
click on Create identity. See here we get
verification pending. Amazon has S send email to that email and we
need to verify that. So here I open that email. Click on this link,
see verification done, and if we refresh that page, see here we also get verified. So we have to copy
this email and in our node application
in the sources, we added AWS underscore email. Let's add this variable
in the ENV file. AWS underscore email is equal to here I pass
my verified email. Now we have to create access key and secret key for
our application. Search here IAM, which is
identity and access management. Here we go to users
and create a new user. We write user name, let's say, our Linky Pi and click on next. Here we have to attach
policies, search here, AS full Xs and select the
Amazon SS full cess policy, and click on next
and create user. Now for generating
the Access key, open this user and go to
security credentials. Here we have zero access keys, so we create key, select
other option, click on next. Here it is asking
for description, but it is optional, simply create access key. See, here we get access key, and also we get
secret Access Key. We have to add them
in our ENV file. Make sure you download
this dot CNV file. In our ENV file, first, I add here variable name,
AWS, underscore Xs, underscore key is
equal to copy the aces key from the browser and
paste it in our ENV file. Also, we add another variable called AWS, underscore secret, underscore key is equal to copy the secret
key from the browser, and also paste here. Now we need region. In ENV file, we add
AWS underscore region. Now forgetting the region here at the left
side of our profile, we get a list of region. I'm from India. That's
why I select Mumbai. My region will be AP south one. Here, you have to select yours. If you don't get idea, then you can simply
search your region on the Google and select
Neal location. I add this region
value in the NV file. Now here, our step two is done. So this file unless the sending email function is working or not, which
is our step three. In our request, des
resets password API, we will call Send
Email function at the first argument to
pass the email of user. User dot email comma here, we write our email subject. Let's define new variable for subject is equal
to password reset, request for your
Linkifi account. Also, we define text
which we want to send in the back ticks,
click this link. Reset your password, SDDP cool
and double forward slash, our linkify.com slash
reset password. In the query parameter,
question mark, reset tocan is equal to here we add dollar curly
packets, reset tocan. This URL is for directly
access or a front end. So Instagram sense, right. Also, you can modify this
text according to your needs, or also you can pass the STML code as the
body of the email. Just in the email params
at the place of this text, we have to pass STML. Now in the Send Email function, we pass subject and also text. Save the changes unless this send email function
is working or not. So one Postman, and here is a
request reset password API, which is our first
API for reset. Now before calling
this, let me pass here valid email so I get reset
token on that email. Here, I change my email
to one of my dummy email, update it, and also in
the request dot body, I pass the same email. Good. Now, let's
send the request. See here we get could
not send request. Oh sorry, I forgot
to start the server. So nodemon index dots Nice. Now, let's send the request. Here I get sendemail
is not defined. I forgot to input that. So at the top, cost, send email is equal to required. Here we go one folder
up, Config Amazon SCS. Save this and send
the request again. Again, I get error. Don't get panic. Let's check. It is validation error. So as we know, we just verified our sender email on Amazon SES, but we have to also verify the receiver email for
testing the email feature. It is the condition of Amazon SES for testing
sandbox account. If we move to production
and paid plan, then we will send email to anyone without
verifying their email. For testing environment, we need to verify the
receiver email. So let me check which
email I use for user account in the
Mongo DB Compass. See, this is email. So here, I have to add
it in our user identity. So search Ss like this. Go to identities.
Create identity, select email, and we here your email which is
available in your database. Create identity. They again send the verification
email on this email. So I open that email
here and open this link. See, we get
congratulations. Good. Let me refresh this page. See, it is verified. So let's die again. Send the request still, I get Sandymil is not defined. Let me check in the
Amazon SS file. Oh, at the bottom, I forgot to remove
this parenthesis. I added to show you error, and I forgot to remove that. Save this, and let's
send the request again. See, here we get
the reset token. But if we check
our terminal, see, here we still get one
validation error, Amazon SS error, email. Let's check SandymlFunction. Okay. Here in the destination, we have to change
these two address to two addresses plural. Now let's try again. Here we get our reset token. Now let's check our terminal. Here we get a new error. Email is not verified
in this region. But why as we know, we already verified our email. Still, we are
getting this error. Let me explain you. Here I verified our both email
in the wrong region. Let me also check
on Amazon website. See, here I have Europe
region selected, and in my NV variable, I pass Mumbai region, and that's why I get this error. I select here this umbi region. Now I have to again verify
both emails in this region. Go to identities,
create identity, select here email, write the
email and create identity. Now let's verify this
from email good. Now I have to do the
same for another email, if I eat and done here both emails are
verified for my region. So now let's again test
our implementation. Send the first API call. See, we get reset token. And also, if we
check our terminal, then see here we get
emails sent successfully, and we also get message ID. And if I check my email inbox, here I get the email
in the spam because here we are in the tasting mode and we also not verify domain. In production, you have
to do little changes in the setting and your email
will not go into spam folder. This is the power of Amazon SES. I know setting this SES account is little bit difficult
and boring part. But for better smooth
experience like this, we need to pass from
that sweet paint. Now user can open this front end link and then front end will
call our second API. That's the job of front end. Also front end developer you whether you are a link.
Don't worry about that. Now, also, if we have to use
Amazon SES for production, then we need to request
for production access. If we check our current
status on GetSet page, see currently, our status is
sandbox which means testing. Here we can read the
mention in the cards below, verify an email
address and sending domain to request
production ces which enables you to send email at a production level and leverage the full capabilities of SCS. For production, you have to verify sending domain from here. And if you provide
write information and submit a request, then AWS usually approves
it within 24 to 48 hours. After getting approval, we can send email to email address. Also, I will give some more information in the
article below this lesson. So for production,
you can read that. Okay? So that's how we
send email in most secure, super fast, and
cheapest Amazon SS way.
166. Sending Emails for FREE: Let's see how we can send
email using SMTP way, which is free, but
it has daily limit. We can send only 500
emails from GML SMTP, and if you use YahooSMTP, then we can only send
100 emails per day. But for your local application or college project,
this is okay. Also, if you implement Amazon SS and it is
working for you, then you can skip this
lesson because we will add only sending email
features in this lesson, same as we have done in
the previous lesson. Now let's see the
SMTB implementation. I divide this implementation
in three simple steps. Step one, we will implement SMTV configuration in
the node application. Then step two, we will generate a password for SMTB
last step three, we will test this
implementation. Let's start with
step number one. For implementing SMTP, we need package called node mailer. NPM install Node mailer, at the d 6.10 0.0 and hit Enter. Good. Now here in our project, create a new folder
called config, and in that folder,
we will create a new file called SMTP dot js. Good. Now to start
configuration, we need one node mailer
method from the package. Sconst node mailer is equal
to require node Mailer. And after that, we need to
create transport for setup. So nodemler dot
Create Transport. And inside this, we have to pass the configuration object. Here, we will add properties, and most of their values we
will pass in the ENV file. So host to process dot nw
dot SMTP Underscore host. Second, we need pot to process dot nw dot SMTP Underscore POD. After that, secure to true. This value is true
for only put 465. For other pots, we need
to use false value. After this, we
pass of to object. And here we need to pass user to process dot nw dot
SMTP, underscore user. And after that, pass to process dot w dot SMTP, underscore pass. Don't worry, we will pass
these values in just a minute. Now for sending the email, we create a new function
called Send SMTP, email is equal to
error function. Now in this function, before writing the code, it's better to add
try and cache block. And in the cache block, we
will pass console dot log. Error in sending email
and simply addheemail. Now in the dri
blog, we add cost. Mail options is equal to object. Here we pass some options. From two process dot nvt
SMTP underscore user, then we have two property. Here, we have to pass
receivers email. But here is one
question. How can we get the receivers email
in this function? Right, we will get it
from the parameter. So we addre too. Also, here we need subject. This is the subject of our
email and also we pass text, which is the body of the email. Now in the options, we add two to two, subject to subject,
and text to text. So we have options ready. Now we just need
to send the email. Await, and here we
need transporter, which we get from this
node Miller method. So let's store it invariable
called transporter, and in the send mail function, we use transporter
dot send mail, here we have to
add mail options. This expression will
written the response, so let's store it in
variable called response. At last, we simply
console dot log, email sent successfully,
and we also add message ID, and we adhere response
dot message ID. For sending the email, we just need to
call this function and pass these arguments. We export this function
using module that exports is equal to
send SMTP email. Save this file, and now
in our request reset API, we will simply call here, send SMTP email function,
see atoiput works. Here in the previous lesson, I created these two
variables, subject and text. You can copy that and
simply in this function, pass user dot email, which is the receiver email, comma subject, and text. Now let me comment out
this previous function for Amazon SES. Here our step one is done. Now let's move to step two, which is defining
environment variables. So on ENV files in
the previous lesson, I added these four
variables for Amazon SES. If you don't apply
that, then don't worry. Now here we pass four
variables for SMTP. If you don't remember
their names, then you can simply
copy it from the code. Nothing wrong about that. So first, SMTP and
ace host is equal to here I am using GmlHst
so I write smtp.gmail.com. After that, SMTP underscore
port is equal to 465. Here, if you are using
Yahoo or Outlook, then you can use the
post and port values. But Gmail is good for SMTP. Now we need SMTP
underscore user, and here we pass the email from which
we want to send email. This is the company email ID. So here I add my email ID. I use this email just for demo, in the future, I
will not use it. Now we just need to pass SMTP underscore password
is equal to here, we can pass our Gmail password, but after 2024, Gmail don't
allow this type of setting. So instead of passing
real password, we can pass a password. Let's see how can we
generate a password. So open up google.com and go
to manage my Google account. Here we go to the security. Now for generating
the app password, there is one condition. In our Google account, two step verification
must be unable. If it is disable
for your account, then you must enable it. Otherwise, you will
get SMTP error. Now, after you unable to step
verification at the top, search a password and open
the a passwords in security. It will verify you first, good. Now it app name. Here we can write node, Miller, SMTV or any other name,
it doesn't matter. Only thing is in the future, if you want to remove
this app password, then you can remember
that app name. Now click on Create. See, here we get
16 digit password, simply copy this and
also you can take screenshot or click Photo in your phone because
once we close it, then we can't see the password. Copy this and paste
it in our ENV file. Make sure you leave
the space as it is. Don't change anything
in this. And that's it. Here, our step two is done. Now let's move to step
three, which is testing. So before testing, make sure you pass the valid email
in your user's data. If you don't pass your email, then you can modify it
from the Mongo DB Compass. Okay. Now open Postman, and here we send first
request, reset password API. See, here we go reset token, and if we check our terminal, we get error. Connect,
icon, refuse. Maybe this is error
for one time password. So let me try again. Still, I'm getting the error. So let me create another
app password for nodemiler, SMTP, create and copy this
and make sure you click Done. Now in our ENV file, replace the password,
save this file. Now let's try again. Send the first request. Here we get token
and in the terminal, see, we get email
sent successfully, and we also get message ID. And if I open my inbox, see, here I get the new email. That's so simple to send
email from node application. To sum up, you can use Amazon SES for enterprise
type application, and if your
application is small, then you can use SMTP method, but it will only give us
500 emails in one day, it's totally up to you
which one you choose.
167. Follower and Following Logic: Nowadays, in any social
media application, there are follower and
following feature is must. So let's also implement
that in our application. You might get a little scared by this feature if you are implementing it for
the first time. But trust me, it
is really simple. So let's first understand the logic of follower
and following. So here are, which is the
login user in our application. And here is another user, let's say Harley and his
profile status is public. Now, you want to follow Harley, so you press the follow button. Now what happened
in the back end? First of all, for every user, we will add three new fields
in the user's schema, followers, which is the array of users who follow that user. Here we add user
IDs as reference. Next, we add following field, which is the array of user
who is followed by this user. At last, we add follow request, which is also array, but in that, we
will add all users ID who sends requests
to follow the user. We will add user ID in this follow request only if
user account is private. Now back to our example, suppose you want to follow Hurley and Hurley
account is public. First of all, your user ID will add in the
Hurley follower list. Then Halley's user ID will
add in your following list. Very simple. Now here, Halley's account is public. What if we set his
account to private? In this case, we can't add your user ID in the
Halley follow list. In this case, we will add your user ID in the Hal's
follow request array. Now Hali has two options. He can accept the
request or reject. If Ali rejects, then
we simply remove your user ID from the
alisFollow request. Now, what if Hali
accept the request? Then first, we remove
your user ID from the request list and add
it in the follower list. And also, we add Hale user
ID in your following list. So that's how simple
this follower and following features. Now in the next lesson, we will define AEI for these features.
168. Follow the User: Now let's define API
for adding follower. As we know, the logic
is very simple. So let's quickly implement it. Here at the bottom, we add router dot post
endpoint to forward slash. Here, we add user ID, so Column user ID slash follow. This is the user ID of the
user who we want to follow. Also, we need logged in user ID, so we add here orth
middleware and acing function with
request and response. Now inside this function, first of all, we
get this user ID, so Const user ID is equal to request dot
Perms dot user ID. Also, we need const, current user ID equals to request dot user
dot underscore ID. First of all, we will
check one condition. If user ID equals
to current user ID, then we return response
with status code 400 for bad request dot Json
Object, Message property. You can't follow yourself. First logic of follow
API is we will check other user account
is private or public. For that, we get to
user information. So cons user to follow is equal to await user dot fine buy ID. And here we pass user ID. Next, we pass your condition. If user to follow
is not available, then return response
with status code 404 and Json to Object with message
property, User not found. Also, we need to find
current user info. So const current user
is equal to await user dot Fine BD and pass
here current user ID. And also pass here,
the same condition, so duplicate this condition by Alt plus sift plus
down arrow or Option plus sift plus down
arrow and move this line below withholding option
or alter arrow keys. And here we just change
this user to current user. Now after we get the both user, we can pass your condition if
user to follow is private. If this is true, which
means account is private. I account is private, then we need to add
current user ID in the other user
follow request. But for that, we need to add three fields in
our users schema. So on user schema, at last, we add filled followers to
array, and inside that, we add object type to Mongoose dot schema dot
types dot Object ID, and ref to user. Also, we need following
and follow request, which has same schema
type and user reference. Just here we change following
and follow request. Save this file, and
back to our route. Here, we write user to follow, dot follow request, dot push. And here we add current user ID, and then we can await
user to follow dot save. And here we return response
with JSON message. To follow, request sent. Good. But here's one thing. It might possible user is already available in the
follow request array. Before this push method, we add condition I user to follow dot follow request
dot includes here, we pass current user ID. If current user ID is
available in the array, then we return
response dot status, 400 dot Json Object with MsatPperty to follow
request already sent. And after that, we add s, and we can move this
part in the s block. So here we write
the logic of adding follow request if
account is private, but our other user
account can be public. After this, if we add Ls, if you are getting confused, then you can write
commands for that. I write comment here,
logic for public account. Good. Now, what do we want
to do if account is public? Right, we will direct add current user ID in
the follower list, and in current user
following list, we will add user to follow ID. User to follow dot
followers dot push. Here we add current user ID, and after that, current user, dot following, dot push. And here we add user ID. And after that, we save
these both documents. So await user to
follow dot save, and await current user dot save. After that, we
return response with JSON object with
message property, user followed successfully. Now as we did previously, it might possible
our current user ID is already available
in the follower list. We pass your condition
before this logic. If user to follow dot followers, dot includes here we
pass current user ID. If it is available, then we return response
with status code, 400 for bat request, and in Jason object with message property to already
following the user. After this, we add s, and we will move this
code in the se block. That's it. It is really simple. Now let's test this
implementation. So for testing the follow API, we need another user account. Let's create this
first, open created user API, change username, let's say, Hurley 001 and email to Hali at
the red gmail.com. Also, I will remain
password the same. Otherwise, I will forget the password and
send the request. See, here we get
the new user token. Here we need this
Hurley user ID. So per Mongoi we compass, go to users and copy
this new user ID. Back to Postman for follow, we create a new request. Follow API, SDB method, to post, URL to SDDP, Column double forward slash
Local host, Column 3,000, slash API slash user slash here we page the new
user ID, slash follow. We need the current user token, so we go to the login API, change the password
because we change it in our previous lesson
and send the request. Copy this token, and in our
follow API, we go to headers, adhere authorization,
and in the value bearer, space pace the token. And send the request. See, here we get user followed
successfully. We directly followed the user because that new user
account is public. Now, let's also taste by
making that account private. So we go to the Mongo Di
becompasRfresh the data. First, we remove the ID
from the following list. And also we remove ID from
the follower list and also make this new
user account is private to true and update it. Now, let's back to follow
API and send the request. See here we get follow
request sent lovely. Now in the next lesson,
we will create API for accepting the follow request and rejecting the
follow request.
169. Accept the follow request: Let's define API for accepting and rejecting
the follow request. First, we will
implement rejecting the request because it
will be easy to test, so router dot post and point
to slash reject Ds request, slash and here we need
the requester user ID. Call requester ID. After that, we also need the
orthomddal ware. We add a sing calve function
with request and response. Now, first of all,
in this function, we will get Cost, requester ID is equal to request dot Perms
dot requester ID. Also we need const, current user ID is equal to request dot user
dot underscore ID, which we will get
from this Omdalware. First of all, we will check if both users are available
in our collection or not. Remember, we did the same
in the previous API. Here it is, copy this code. Also, copy this first
condition for same ID, and at the bottom, paste
it in our reject API. Here we need to do
little changes. So at the place of user ID, we have to add requester ID, and here also requester ID. And also, we need to change this variable name so
select user to follow, press F two and change
this to requester user. Will give us more clarity. Now for rejecting the request, we just need to
remove requester ID from the follow request array. But before that, we can
check whether we have requested ID in the follow
request array or not. So if current user dot
follow request dot includes requester ID, if this condition is not true, we pass here exclamation mark, and then we return
response with status code 400 and dot JSON Object
with message property two, no follow request found. And I follow request is found, then here we can use filter method for removing
requester ID from the array. So Const, updated
request is equal to current user dot follow
request dot Filter. Here we add ID, which is the individual item of this array arrow function, and here we return condition dt two string not equals
to requester ID. This will remove
the requester ID from the follow request array. We can simply set current user dot follow request is equal to updated request. And then we await current
user dot c. And at last, we simply return response with JSON object with message
property to follow, request, rejected,
and that's it. Now let's test this API. So open up Postman, and here we add a new request called rejecting
the follow request. Now in this, we set API method
to post and point to SDDP, Column double for
slash, local host, Column 3,000 API user slash reject request slash here we need to pass
the requester ID, which is the ID of the
user who sent the request. Here we open Mongo Db Compass, and can you tell me which
user send a request? Right, it is our first user, which ID is added in the follow request
array of another user. So copy this ID and
paste it in the URL, and we send the request. Oh, we need to pass JWT Token. Now here we need token of the user who
getting the request, which is our new user. So in the log in API, I change the user name to Hurley 001 and also change the password
and send the request. Copy this token. And
in the reject API, we go to headers, adhere authorization, and value to bearer
space, pace the token. Now we send the request. See, here we get follow
request rejected. As if we check our database,
refresh the collection. See, for the second user, we don't get any ID in
the follow request. Here, our rejected
API is working well. Now we can define API for
accepting the user request. This API is very similar to
the rejected request API, so we copy whole API
and paste it below. Good. Now here, first we change the endpoint to accept as
requests requester ID. After that, if we
accept the request, we also need to remove requester ID from the
follow request array. So we keep our code as it is. But after removing
the requester ID, we need to add the requester
ID to the follower list. That means we are accepting
the follow request. So before the save method, add current user dot
followers dot push. Here we push requester ID, and also we need to add the current user ID in the requester users
following list. So requester user, dot
following dot push. Here we push current user ID. Now let's also await
requester user dot save. At last, we change the
message to follow request, accepted, and that's it. Now let's also taste this API. So back to Postman and here we need the same
request like reject. So we can duplicate the
taste API from here, change the name to accept
the follow request. Also endpoint to accept
as request requester ID. And if we check
the headers, see, here we also get that token, and that's why we
duplicate this test. Now, let's send
this API request. See, here we get
no follow request found because we just
rejected the follow request. Here we need to send the
follow request again. For that, we go to follow API and send the follow request. Request sent, good. Now back to accept request
API and send the request. See, we get follow
request accepted, and if we check our database,
refresh the collection. See, here we also get user ID in the follower list and request our ID removed from
the follow request. And here we get the ID
in the following list.
170. Exercise - Getting the list of Followers and Following: Now it's time for
little exercise. In this exercise, I want
you to create two APIs. One API for getting the
user's list of another user, and second API for getting the following list
of another user. If you are user one, then you can send request
for getting the list of user two, followers
and following. Here our API look like this. Slash API user ID,
slash followers. Slash following
for following API. Think about logic first
and then implement it. It is really simple. I know you can complete this exercise. Try to solve this and
then what's the solution. I hope you complete this exercise or try to
solve this exercise. Appreciate yourself for that. Now let's see the solution. So here we add
router dot Get and point to slash Colin user
ID, slash followers. This is the ID of the user whose followers list
login user wants to see. So we need middleware and then asing callback function
with request and response. Now inside this function, first, we get cost user ID is equal to request dot
Perms dot user ID. Also, we need cost,
current user, is equal to request dot
user dot underscore ID. Now let's understand
the logic of this API. Should we show followers
list of any user no. You can see only those
users followers list, whom you follow or whose
account is public. Here, we need current
user follower list and another users is
private property. Again, we copy both
users code from the previous API and
paste it in this API. Now here we change
the variable name to user and change this
requester ID to user ID. Good. Now, after this, we can pass condition if user
dot followers dot includes, here we pass current user ID. Or user dot is private is false, which means user
account is public. If any of this
condition is true, then we will show
users followers list. Return response dot Json, and here we pass
user dot followers. We add, and in that
we will return response dot Status 400 dot Jon, pass your Object with
message property, Gant, Get followers list,
account is private. Now let's do this
implementation, save the changes
and open postman. Here, we create a new request called Getting Users
followers list. Here we add URL, SDP, Column double four slash, local host, Column 3,000, slash API, slash user ID, and at last, we add
slash followers. Now back to Mongo DB database, copy this second user ID, which has follower and
paste this ID in the URL. Now in this API, we need to pass token.
So go to headers. Here add authorization and
value to bearer space. And here, we have to
pass another user Token who is following
this user ID. So back to login API, and here we simply
login with old account, change the password,
and send this request. See, here we get token, copy this and paste
it in our API header. Good. Now send the request. See, here we get the array
with the follower's ID. But as we know, in
the real world, we need to show user name, so we need to populate it. For that, in our fine query, we had populatet
method at first, we pass fill name which
we want to populate, which is followers, and
then we pass fills name, which we want to get
from the reference, which is underscore
ID and user name. Now here is one thing. Here, we populate the followers list. So now it is not same as before. It will be array of object with underscore ID and username
fields in each object. So we can't pass here,
this includes method. We can use Fine index method which we use in the
previous projects, and also write here different
condition, current user, dot following dot includes, and here we pass user ID. So if current user is
following the other user, then we can also get
those followers list. Save the changes
and take a look. Send the request. See here we get the follows list
with ID and user name. Now, let's create another API and you guessed it correctly, we will duplicate this
API and at the bottom, we add it here. Now here we need to only
change little things. So first of all, we
change the endpoint to slash user ID,
slash following. Then here in the user's query, we need to populate
following field, then this condition will be the same and we change the
message to following list, account is private,
and it's done. Now in the next lesson, we will define API for
unfollow the user.
171. Exercise - Unfollow the User: Let me give you
another exercise. In this exercise,
you have to create API for unfollow the user. So our API endpoint
will look like this. Slash API user, slash
user ID, slash unfollow. It is really simple, spend some time and
solve this exercise. And after that, come back
and watch the solution. Now let's quickly
see the solution. So here we write router dot
Post and 0.2 slash Column, user ID, slash unfollow. Also, we need Omidleware
ACN callback function with request and response. Now here we need to
find both users. So copy this starting code with both these conditions and
in our API pass this code. Now here we don't need this populate method,
so we remove that, and also we change this user
name to user to unfollow. Good. Now after
getting the both user, we pass your condition I user
to unfollow dot followers, dot includes current user
ID if it is not true, and here add exclamation marks, and then here we return
response with status code, 400 and in Jason, we pass Object message to user is not available
in the followers. And after that, we can remove current user ID from the user to unfollows
followers list. So user to unfollow Dart
followers is equal to user to unfollow Dart
followers dot filter method. Here we get each ID, and here we return condition ID, dot two string is not
equal to current user ID. Also, current user, dot following is equal
to current user, dot following, dot filter. Here we get ID arrow function, and ID two string is
not equal to user ID. Now we can save both documents, await user to unfollow dot c and await
current user dot C. And here we return response dot Json object with message property, user
unfollowed successfully. And done. Now we can taste
this implementation. So one Postman and here we
duplicate this follow API. Change the name to unfollow API. Now let's change the
endpoint to slash user, slash ID, slash unfollow, and send the request. See, here we get
success message user, unfollowed successfully. So that's how we
implemented followers, following request
and reject API. You can see it is really simple. We need to understand logic before we implement
any new features.
172. Section 14 - Introduction: Welcome to the 14th section
of the ultimate no JS course. In this section,
we will implement post related features like
posting a new image or video, displaying post, delete
the entire post. Also, we will add
like and unlike feature writing a
new comment on post, replying the comments,
and much more things. This is one important section, so let's start this section.
173. Create Post Model: Let's first create post schema before creating APIs for post. In the models folder, we create a new file
called post dot js. Now go to the
user's Module file, copy all code from here and paste it in
the post model file. Now, first of all, let's
remove this schema object and As we need to
change some name here. Se like this user schema
and renam this by F to post schema and hit Enter. Now select user, and for
selecting multiple times, press Control plus D or Command plus D and change this to post. Now we have to only
add Schema object. In the post document, we
need couple of things. First of all, we need user, which is the user ID who
posted image or video. So type to Mongos dot schema
dot Types dot Object ID, ref to user, and
required to true. Next, we need media. It can be image or
it can be video. Also, it can be one
or more than one. So we add here array, and in that, we add object, first property name to object, type to string, and
required tou true. And another property, media, type to object, type to string. Num to array, here we add image, or it can be video. As we make it required to draw. By this schema, we get proper
understanding about media. Next, users can add
caption with the post, type to string and dream to true for
removing white space, or we can also remove
this stream property. Users can add any
types of captions. Also, we are not
making it required because user might don't
like to add any caption. After that, we add s, which is array, and also here we stood the
reference of the user. So we copy this type
and ref property from the user field
and paste it here. Good. Now let's add some
additional fields like texts, which is also array,
type to string. Next, we also add location, type to string, and that's it. Currently, we need these fills. If in future, we need
some other fills, then we will add it in
the upcoming lessons. Also, we will add comments
fill later in this section. So don't worry about that. Now let me show you one
thing in the schema object. Previously, remember we had created at fill in our project. But in the Mongo schema, we can automatically add that. Simply after this schema object, we had second argument in
schema method, object, and here we add timestamp
property and we set it to drew. By this property, we will get two time related properties, created Ed and updated This is the current time
when the document was created and in updated ED, we will get date and
time of last update. Don't worry, we will
see that when we create new post and we will do
that in the next lesson.
174. Create a new Post: Let's create an API
for creating new post. In the routes folder, we create a new file called post dot js. Here, we need Router, Cost Express is equal
to require Express. And after that, cost Router is equal to express dot Router. At the end, we simply module dot exports is equal to Router. Now let's set this
router in our main file. So in index dot js file, we add cost post route
is equal to require. Here we go to routes folder, and in that post. Now at the bottom, app dot g, prefix to slash API slash post, and at the second
argument, add post routes. Save this file, and now
we can add post routes. So in the post file,
we add Router, which method we will use, right. We will use router dot post. Here we add forward less
for creating new post. Also, here we need
orthomidal ware because we want only log in users
should create a new post. And after that, we add AC callback function with
request and response. Now what we want to do in this API want to simply
create a new post document, and store that images and
videos in our local server. So for that, we need to set up Multer same as we've
done previously. So pen up terminal and
write and pm install Multer at the red
1.4 0.5 dash LTsO. Eight, Enter. Good, minimize this terminal and let's
implement Multer. So in the previous project, we added Multer config
in the same route file, but that looks little
unprofessional. So in this project, we
can configure Multer at different place and then
import it in this file. Okay? So in the config folder, we create a new file
called MulterUload dot js. We know implementing
Multer is really simple. Just we need to set the
file name and we can set the file filter for preventing
unnecessary files upload. Cost, post upload is equal
to Multer and inside this, we need to pass
configuration object. First, we pass storage, which will take care of the
file path and file name. So at the top, we create separate variable const storage is equal to Mltert disk storage. And in this, we also pass
object with properties. Also, we need to input
Multer at the top. So Const Multer is equal
to require from Multer. Good. Now in this object, we add first property,
destination. And here we get request, file, and CB has parameters, error function, and inside this, we simply call CB function. And at the first position, we have to pass
error, which is null. And at the second argument, we pass the path, destination
of our post files. Let's say uploads post. Next we have file name, again, arrow function
with request, file and CB and inside this, we will define specific
and unique file name, same as we did previously. You can also see code
from that project. It's totally fine.
So Const timestamp is equal to date dot now. By this, we get current time. After that, we on to remove some character and spaces
from the file name. So cost original name is equal
to file dot original name. By this, we will get the original name of
the uploaded file. Now in the file name,
there may be some space, so it's better we replace
that space with the dash. It will make the file
name URL friendly. We add dot replace method. Here, we first add regular expression is a
syntax of regular expression, which is double forward
slash between these, we write backward slash a for space and plus for one
or more white space. At the end of the
regular expression, a G for global flag, which makes sure all matches in the string are replaced,
not just the first one. Now, here we pass
dash in single codes. This means all space
will replace by dash. Now, what if in the filename, we also get the
special characters? We need to remove them as well. We add another replace method. First, what we will pass? Write regular expression by double for our slash
between these, we write regular expression for getting the
spatial characters. Here we add A to Z. Also A to Z zero to nine dash. Now we want to get
other characters which are not part
of these characters. It will be our
special characters. So to inverse this, we wrap
it in the square brackets, and after the first
square bracket, we will add Garret for inverse, and at the end, we also
add G for global flag. Now we want to replace these special
characters by nothing. We just want to remove them. So we adhere only codes
without anything. Now at the end, we will mix this time stamp and
original file name. CB first, we pass
null for error, and second, we write taxes. Here, we add Cali brackets, time stamp, dollar
Cali brackets, original name and done. We define destination and also file name for
our uploaded file. Now we can simply pass this storage variable here
in the multi function. Now after storage,
we pass file filter in which we will filter
files by their extensions. So cost file filter is equal to error function
with three parameters, request, file, and CB. Here we will check
while is valid or not. So Const allowed types
is equal to array, and here we add file types. Now, in our social
media application, user can upload images
and videos also. We add here image slash
JPEG, another image, PNG. Next, image GIF and for video, we add videos MP four. And also video slash MOV. You can also add another
types if you wanted. Now we can simply
put here condition I file Mme type is
from these types, only then we will allow it, else we will reject it. If allowed types dot
includes file dot Mme type, it is true, then we call
this CB function and first, we pass null for error, and then pass through
for accept the file. Now, if MM type is
something else, then these types, then we adhere condition and call
here CB function. At the error, we pass new error, and here we pass the error
message, invalid file type, only JPEG, PNG, GIF, MP four, and MOV are allowed to reject this
file, we adhere pause. Now we can simply pass this file filter in the
Multi config object. Also, in the multer, we can specify the file
size using Limits property. For social media, we
can set limit to 15 MB. I user wants to
upload some videos, then they can also do that. Object file size to 15
into 1024 into 1024. By this, we get the bytes value. This is the limit for each file. If we use it for
uploading multiple post, then each file has
maximum limit to 15 MB, not for combined
limit, and done. Here, our post upload
method is ready, so we can export it from here. Module dot exports is
equal to post upload. Save this file, and
in our routes file, here after this orth
middleware, we add post, upload, see auto input works dot because we want to upload multiple
images in the post. Now with the first position, we write the input fill name. Let's pass here media. From the front end,
we need to take file in input with fill
name media like this. After that, we will pass the
maximum number of files, let's say ten, and that's it. Now we can write API logic. First of all, here we check user uploaded file or not
because if there is no media, then we don't want to save
the post in our database. I request or files
are not available, or request dot files dot
length is equal to zero. If any of these
conditions are true, then we return error, response, dot status, 400 for
better request. And in the JSON, we add
object with messed property. At least one media
file is required. Now, if media is available, then we store that post
in our post collection. So cost, new post is equal to new post Great auto input works, and here we need to pass fills. First one is user to request
dot user dot underscore ID. Next, we have caption, which we will get from
the request body. Above this, we con
destructure the request body, and here we get caption. Also, user can add
text and location. Now, in our post, we
add caption to caption, or we can also remove this text to text and
location to location. We need only one
field which is media, and that is array of object, and that object has
two properties, name, which is the name of the
file and media type, which is image or video. By that, when we display
that media on front end, then we can easily decide
which tag we should use. So here we need to create
that array of object. It is really simple
Cast media is equal to request
dot files dot map. Here, we get the single
file in the parameter, error function, and here we want to simply reten an object. Here in the Gully brackets, we are written object, name to file, dot file name, and for media type, here we need to pass condition. File dot mime type, dot starts with
double codes, image. If it is true, then
we set type to image, column, else, we
set it to video. This method, we will
get media array of object here in our new post, we set media to media. After that, we can simply
await new post dot C. Then we return
response dot status to 01 for creating new data. Dot Json object with message property, post
uploaded successfully. As we return a post to
new post, and that's it. Let's test this implementation. Save this file and open postman. Here, we create a new folder called post, and inside this, we add new request
upload a new post, change the SDDP method
to post and URL to SDDP. Column double for
slash Local host, Column 3,000 slash API slash
Post and send the request. See, here we get token required, go to login API and simply
generate a new token. Copy this in our new API, we go to header and
add here new header, authorization to beer space, and here we paste our token. Now send the request. See, here we get at least one
media file is required. Now let's also add media
file with this request. Remember from which
section we can send file, we can send file from
the body and form data. Here in the key, we pass media
because in our API, we define media here
in the multer method. Now here we select file. Add a new file from
the local machine. Let's send any two
or three images from here and send the request. See, here we get error, no such file or
directory aploadsPost. Basically, they are telling us we need to
create folder path. In our server, we create a
new folder called uploads. Make sure you use only small letters because
it is case sensitive. Now in this uploads folder, we create another
folder called Post. Good. Now send the request. C, post uploaded successfully. Here we get new post
object with user, Media also looks great
with name and media type, likes and tags to
empty array and we also get timestamps created
at and uploaded at. We get this both
because in our schema, we addre timestamps to true. That's how we create a new post. Now in the next lesson, we will get post from the
post collection.
175. Getting current user posts: Now it's time for
little exercise. Here, you have to
create a new API for getting all post of
current logged in user. Also in that, you have to
implement pagination feature, same as we did in the previous
project, products API. Make sure you
display the letters post first and then older. I know you can do this, give it a try and after that,
what's the solution. Now let's quickly
see the solution. So here in our post route, we add another router
dot GdndpoEndpoint, let's say, slash my post. Here, we also add Osmidalware and ASN
callback function with request and response. Here we need to get
for squary parameters, which will give us
page and limit. Second object is equal
to request dot query. And here we get page and limit. Also, we can set the default value of
this page and limit. So page to one and
limit to let's say ten. If in the query parameter, we pass something else, then it will replace
these values. Also, as we know, when we get values from the
query parameter, those values are string, so we need to convert
them into integer. So here at the place of cost, we write let, and after that, page is equal to parse integer, page, and after that, limit is equal to
parse integer, limit. Now we can add query for post. So const, post is equal
to await, post dot find. Here we pass Object user to request dot user
dot underscore ID. And here we add Skip method. That page minus one into limit, and also we are limit method, and we pass here this limit, and also we'll make this line. And then at the end, we simply return
response dot Json and pass here this post. Now let's tase this
implementation. Save this file and open postman. Here, we duplicate this request, change its name to getting
logged in user post. Also change the method
to get and URL to API post slash MI Post
and send the request. See, here we get the post. Currently, we have
only one post, and that's why we get here
single post, so it is working. Also, to improve this
query, we can do one thing. Pass variable, which tells our front end there next
page available or not. It is really simple. So here, after we get our post, we create a new variable called a next page is equal to here we pass condition if
this post dot length is equals to current limit, then it possible there might
be a next page for post, so we pass here true if current post size
is less than limit, then in else, we pass false. It is really simple logic. Now in the response Json, we have to pass
object, post to post. Page to page, limit, to limit, and as next
page to hass next page. Save the changes
and take a look. Send the request again. See, here we get post, and if we scroll down, we get a next page to falls because current
page limit is not full. Now in the next lesson,
we'll see how to get post for our
social media homepage.
176. Getting Home Feed: Let's create API
for getting post, which we can display on home screen when user
open our application. Here we add another
router dot GAD and point to slash following
because on homepage, user will see post who
followed by her or him. Here we also need Osmidleware because we need
current users following list and also acing callback function with
request and response. First of all, in this API, we need to find the following
list of current user. Seconds user is
equal to await user, dot fine by ID, and here we pass request
dot user dot underscore ID. Here we only want
following fill, so dot select, and we pass
here following in codes. Here we get three lines, which means auto
input did not work. Let me input this user
model at the top. Cost user is equal to require. Here we go one folder b
models and go to users. Good. Now for post query, we can define separate variable. Let query is equal to object. Inside this, we pass user, and here we again
add curly brackets. Now we can use Mongo Deb
operator called $1 in, which is used to check
item value from the array. Here we pass user dot
following, which is array. Basically, this means
so post of users who are available in the
user dot following array. Now from this, we can
simply get post, so Const, post is equal to await, post, dot find, and
here we add our query. Also, with this, we need to populate the user who
uploaded those posts. So dot populate and here we pass user filled at
the second parameter, we pass which fields we want. So score ID, user name
and profile name. If we have profile picture, then we can also
populate that here. Next, we will add short. Here we pass object with
created at property, and we shot them in
descending order, so pass minus one. By these, we get new post first. Currently, we didn't implement pagination feature in this
query, but in real world, we need pagination feature
because in real world, there may be hundreds
or thousands of posts. At that time, we can't send
all post in single request. It will make our server slow. So here we need infinite
query feature at the top, add let object is equal
to request dot query, and here we destructure
page is equal to one, and limit is equal to ten. These are default values. Also, we need to convert
them in integer. Page is equal to
parse integer to page and limit is equal
to parse integer, limit. Now in our query, we add
Skip method and pass here page minus bone into limit. And we also add limit method, and we pass here limit, and also at the end, we add len method. Now, everything is great, but here is little
issue in this API. Let me explain you
that with the example. Suppose here we have 20 post and our user send API
request for post. Our API send this
first ten post. Now when user is
watching this post, it might possible
any following user can add new post
in the database. Suppose any five following
users upload five new post. Now when current
user want to see more post request with
page two query parameter. At that time, our query will
skip first ten data from the database because page
two and display this post. Here we can see duplication of post because this five
post added recently, and this is the issue. Now, what is the solution here? We can use here cursor
based pagination. Let me explain you
in simple words. Cursor based pagination means we will fetch post
based on the time. For example, here
is user h he is on the homepage and send
requests for first ten post. Now, from the back end,
we will send ten posts and also we will send last
post created at time. Now when Harley wants
another ten post, then he will send that
last post created at time, which we called as cursor. Now you might ask why
we need this cursor. We will use this
cursor in our query. Find another ten post whose created time is less
than this cursor time. By this, we don't
get duplicate post, and also our pagination
will work smooth. This technique is called as
cursor based pagination. This technique is used
very often when the data we want is adding very
quickly like social media. Now let's implement
this in our query. First of all, we will
return the cursor in the response so you will
understand it properly. Here after our query, we add cost next
cursor is equal to. Here we pass condition, post dot length is
greater than zero. If it is true, then we return
last post created at Tate. So post, square bracket, post dot length minus one. By this, we get the last post, dot created at, we return null. Now at the end, we add response dot Json
here we add object, post to post, and next
cursor to next cursor. Good. Now, let's
add this cursor in our query because that is the reason we are
sending this cursor. So when we send this next
cursor to front end, our front end will
send this next cursor in the next post request. So in the body or
query parameter, we get the cursor
which sends front end. And now we simply add
this cursor in our query. Here query created at is
equal to Coli brackets. Here we pass Operator dollar LT, which is the less than,
and we pass here new date. And here we pass cursor. Now here we need to
handle burn situation. First post request,
there will be no cursor. So we pass here condition
if cursor is available, only then we add this
created in the query. So without cursor, our
query will look like this. And with cursor, our query will look like this,
simple as that. Also we can pass here
has next page property. So copy this line from the previous API and
paste it in our API. And at the end in
response object, we add hasNext page
to hass next page. Now, let's t this
implementation. Open Postman, duplicate
this last query. Change its name to
getting following, post. Also, we change the URL to API post slash following
and send the request. See, here we don't get post because our current user
has uploaded the post, not any of his following. So here we need to add
another user token. Go to Login API, log in with another
user account. What is the user name and
password of another user? Let me remember yeah, it is h001 and password
is set to 128. Yes, get this token, copy this, and in our current
API in the header, we replace this token
and send the request. Still, we don't get post. Let me check who follows whom. Oh, here, no one follow anyone. So to make this dic, I copy this current user ID, which we logged in and dits ID in the followers
of previous user. And also copy this user ID and d it in the current user
following list and update it. Now, let's send
this request again. See here we get post, and also we get next cursor, which is the created at date of the last post and has
next page to falls, this means it is
working properly.
177. Deleting the post: Let's create an API
for deleting the post, so Router, dot delete, and here we add forward slash, column, post ID, which
is the query parameter. Also, we need of middleware because only authorized
users can delete their post. And last, we add ASN callback function with
request and response. Good. Now, first of
all, in this function, we get const post ID is equal to request dot
PRMs, dot post ID. Also, we get cost
userid is equal to request dot user
dot underscore ID. Now here in this deleted API, we warned only user who created
post can delete the post. So for that, we need to
find the user of this post. Cost post is equal to
await post dot Fine By ID. And here we pass post ID. We pass condition I
post is not available, then we return response
with status code 404, and in Jason, Object, message property
two, post not found. Now, what if we found post? Then we have to check
current login user is the author of
this post or not? So if post dot user, which is the object ID to sring
is not equals to user ID, then we again return
response with status code 403 for unauthorized
error in the Jason, we add object with message property unauthorized
to delete this post. Now, what if user and
author is also same? In that case, we
first need to remove the images or videos
of that post, and then we can delete
the post document, right? So here we need to run for each loop because
our media is array, supposed dot media dot for each. Here we get single file object, arrow function, and here we
run our logic for each file. First of all, we need
the file path of the image or video
because by that only, we can remove that file.
It is really simple. For path, we need path module. So Const path is equal
to require path, which is the inbuilt
module of node. Now at the bottom, cost file path is equal to path dot join. First, we add underscore
underscore Dname and also comma we
add file dot name, which is a name
property in media. I'm not sure about this path. So let's consult dot
log this file path. And at the bottom, we simply
return response dot json, Object with measured
property, post, deleted successfully just
for checking this path. Save this file and open postman. Here we duplicate this
getting logged in users post because in that API, we have token of the user
who created that post. Here, we change the
API name to delete, the post, change the method, to delete, and in API endpoint, we change that to
API post post ID. Let's also copy the post ID from the previous API,
send the request, and here at the
bottom, we get ID, copy that and paste it
in our API endpoint. And send the request. See, here we get post
deleted successfully. And if we check our terminal, see, here we get the file path. Ignore the first path because it is underscore
underscore, dear. Just to see here, we get
slash routes slash filename, which is our current
folder path, but we don't want to add routes. So here between Date
Name and file name, we add double codes,
period, period. Save this file, and
let's send the request. In VS code terminal, C, route is removed. Now we just need to add slash uploads post because
that is a path of our post. Before this file name, we add slash uploads post, save the changes, and
send the request again. If we check our terminal, see here we get our file path. So this is how you can taste
the code using Console. I always do that
when I'm confused. I just comment out
the database logic and then taste the API. By that way, we don't need to create and remove data
from the database. So we get here file path. Now we just need to remove
that file using file module. We already did that in
our previous project. So here, remove the
Console and add here, try and catch blog. And in cache, we add
console dot error in Batis we pass error
in deleting file, dollar Coli brackets, file path, and also log this error object. Now in the drilog
we simply await fs dot unlink and here
we pass file path. For using await, we need to make this callback
function async, and also we need to import
Fs from the inbuilt module. So Const Fs is equal to require Fs promises because deleting
the file is async operation. And after removing the
file from the server, we can remove the post document. So await post dot Delete one because we already fetch
post at the top. And done. Save the genes and send
the delete request again. See here we can post,
delete it successfully. And if we check our Bend
files in the post folder, images are also deleted.
178. Like and Unlike the Post: Let's create API for
and unlike the post. It is really simple. If in the array, user ID is not available, then in that we add user ID, which means we like the post, and if user ID is
already in the L array, then for unlike the post, we just need to remove
user ID from that array. Let's add router dot page. Here, we add endpoint
to slash column, post ID, slash L. This is the same API we will use
for and unlike the post. Comma ORT es callback function
with request and response. Now, first, we get
post ID is equal to request dot
PRMs, dot post ID, and also we get cost
user ID is equal to request dot user
dot underscore ID. Now, first, we find
the post, so Const, post is equal to await
post dot Fine By ID, and pass post ID. After that, we check condition. If post is not available, then we return response
with status code 404, and in the Jason, we pass object with message property
to post not found. Now, if post is available, then we check user already
like the post or not. So Const already liked is
equal to post dot likes, dot includes, and
here we pass user ID. Now based on this, we can pass
condition, cost, updated, post is equal to of eight, post dot fine By ID, and update. First, we pass the post ID, and can you tell me what we will pass in the
second argument? Right, we pass the updates. So here we pass condition. I already liked is true, then we have to remove the like. So for that, in object, we use Mongo Di Be operator, which is dollar pull to object. And here we add
likes to user ID. And if already liked is false, we pass here a and
here in the object, we add another Mongoib method, dollar at to set to object, is to user ID. The reason we add here at to set because it will only add user
ID if it is not available, it will not duplicate
the user ID in s. Now at the third argument, we pass object with new to true. This will give us updated data. At the end, we simply return response dot Json Object
with message property. Here, we check condition. I already liked is true,
then we pass here, post unlike else we
pass post like and also we can return likes to updated post dot
likes dot Length. You can return whatever value you want to return from API. Now let's weekly tis this API. One postman, first, we need to create a new post because
in the previous lesson, we deleted our one
and only post. Go to create post API
send the request. See here we get post error, so we have to select
images again. And send the request. See,
here we get new post, copy its ID. We need that. Now in our post folder, we create a new request
called L and unlike the post. Change the method to page, and endpoint to SDDP, column double for our
slash local host, Column 3,000 slash
API slash post, and here we paste our post ID. Also in the header add
authorization key and value to bearer space copy token from the login API and simply paste it in our API
and send the request. See, we get post like
and likes count to one. Now send the request again. See, we get post like and likes count to zero, so it is working.
179. Implementing Comments Feature: Nowadays, in almost
all social media apps, there are features for
adding comments on the post. It is the way to engage
people for that post. Let's also implement command
features for our post. First of all, the question is, where we at the command? We need to add command
in the schema. So on post model file, and here at the bottom, add commands, which is
the array of object. Now, instead of writing
whole schema here, we can create separate
schema for commands, and then here we add reference. By that, we don't get confused. So before this schema, we add const comment schema is equal to new
Mongoose dot schema, and here in the object, we pass the single
comment schema. First of all, we need
here user to object, type to Mongos dot schema
dot type dot Object ID, and we make reference to user
and also required to True. Next, we need text to object, type to string, and we also
make it required True. After that, we need created at, which is the time when that particular
command was created. So type to date and
default to date dot now. Now as we know, in social media, user can reply to
other users command. Should we add that reply
features in our Linky fi or not? Let's do that also. For replies, we add another filled replies, which is array, and
inside this array, we need to define
schema for each reply. Don't worry, it is the
same as this command. Simply copy these three
properties and inside this array, add object and paste them here. Make sure you add
it in the object, otherwise, it will
give us error. Now we have command schema. We can simply add it at the
bottom in the schema see, now we can easily
see the post schema without getting confused. Now let's create API for adding the new
command to the post. It is really simple. So here we add router dot post
and point to slash column, post ID, slash commands. Here, we also need
orthomiddal ware and ASN callback function
with request and response. First of all, we
get cost post ID is equal to request dot
PRMs dot post ID. Also, we get Cost
userID is equal to request dot user
dot underscore ID. Then we also get Const text is equal to request
dot body dot text. Before doing anything, first we check if text is not available, then we simply return response with status 400 and I Jason, we pass object with message property to
comment, text is required. Now, if we have command text, then we need to simply
create new command. So cost, new comment
is equal to object, and inside, we have to pass user to user ID and text to text. So we don't need to pass
created date because we already pass current date as
default value in our schema. Now here we need to pose this new comment object in
the post commments array. Can you tell me which
method we will use? Yes, we can use here, fine by ID and update. Cost post is equal to await, post, dot, fine by
ID, and update. At the first position, we add post ID. At the second position, we need to pass what
we want to update. Now for pushing the new command, we use dollar push method. Here we add Cully
brackets and in that commands to what we want to push Wt this new command. And at the third argument, we pass object with new to true. Now, can you tell me why we don't use here at to set method? Right, because at to set
don't allow duplicate values. But here, command
can be duplicate. One user can
attributed commands. We can't deny that,
and that's it. At the end, we return response
with status code 201. New data, and we return JSN object with message property to comment added successfully. And as we can send comment to post dot commands
in square packet, post dot commands dot
Length minus one, which is the latest
command we add and done. Now, let's do this
implementation. Open Postman and duplicate
this API, change the name. To creating a new command. Change the method to post, and also point to post ID
commands and send the request. See here we get error
because we don't pass text. In the body, we go to raw and here we add object
with text property. What should I comment? Yes,
this is a great product. Here you can add any command.
Now send the request. See here we get command, edit successfully, and here
we also get letters command. Now in the next
lesson, we will create API for adding reply
to this command.
180. Adding Reply to Comments: Now let's create API for adding reply to
specific command. It is very similar
API to previous one, so router dot post,
forward slash, Column post ID, slash commands, slash here we also
need comment ID because in which command user wants to add reply,
we need to find that. And after that, slash replies. Scroll up and copy this entire API with OT
and paste it in our API. Good. Now we need to
change little things here. First of all, in the params, we are getting comment ID also. Let's destructure
request dot params, and here we get command ID. Now at the place of new comment, we name this to new
reply and at the bottom, we will change the method
to fine one and update. As we use fine one and update, we need to pass her
condition object at the first argument. Inside this, we pass
underscore ID to post ID. And also we pass her
condition, double codes, comments dot underscore
ID to comment ID. Make sure we write the right property name
because it is case sensitive. Also, you might ask
our comments is array, and here we are writing comments dot underscore
ID, will it work? The answer is yes, it will work because
in Mongo Di B, we use this dot notation, which allows us to search through the comments
array and find the first object where underscore
ID matches command ID. Now in the push method
at the place of commans we add
comments dot replies. Here we can use comments dot dollar dot
rep. Now you might ask, what is the difference between comments dot replies and
comments dot dollar dot RPS. So commens dollar operator is optimized for targeting a specific element
inside an array. On the other side,
comments dot replies is for general field. Let me explain you with example. Suppose this one post
has ten comments, and we want to add reply
to this second comment. Now if we use here
commens dot replies, then it will add reply
to all commands. We don't want that,
on the other side, if you use here comments
dot dollar dot replies, then it will add reply to only the second command,
and that's what we want. Always remember if you want to update nested arrays
specific item, then you need to add dollar
operator between them. Like commens dot
dollar dot replies. This dollar is called
as positional operator. Short in Mongo DB, dot notation works for searching
and deleting elements, and positional operator is needed for modifying
array elements. Now here in our update method, we change this new
comment to new reply. And also at the end, we change the message to reply, edit successfully, and we simply send that new
reply in our response. So above these we
add cost comment is equal to post dot commands,
which is the array. Now for getting the
specific command, we need to find its index. So instead of that, in Mongoose, we have another
method called dot ID. This will help us to find a subdcument by its underscore
ID field within the array. Just we need to pass
here comment ID, but make sure you add comment
dot ID is method of mangos. Otherwise, in the future, you will get confused
by looking this code. In the response at the place
of command, we add reply, and here we pass
command, dot replies, square bracket,
command, dot replies, dot length minus one. By this, we get the latest
reply which we just add. Save the changes,
and let's taste this API back to Postman, and here we duplicate
this comment API change its name to
adding a new reply. Now in endpoint, after commans
we need to add comment ID. Let's copy the comment ID from the previous API and
paste it in our API. After comment ID, we
add slash replies. Now let's also change the
reply text to thank you so much and send the request. See here we get the
success message and also we get the last reply. So you can see how simple it is.
181. Exercise - Removing Specific Comment: Now it's time for
little exercise. In this exercise,
you have to create a new API for deleting the specific command
from the post. So our APIURL will look like this slash API
slash post post ID, slash commands,
slash command ID, and you have to remove this
command with this command ID. Also, make sure only
the user who uploaded that post or the user who add that command can
remove that command. Not all users can remove
commands. It is really simple. I know you can do that, give it a try and then
watch the solution. I hope you solve this exercise, and even if you stuck somewhere, don't worry, at least you try. That's matter the most. Now let's quickly see the
solution for this exercise. Here, we simply duplicate this last API because
it is very similar. First of all, we change
the method to delete. Also, from the endpoint, we remove slash replies
because we don't need it. Now in the function, first, we don't need this text. Also, we don't need
this condition, and also we don't need
this new reply variable. In the fine one
and update method, we need to add our condition. So first one is the
underscore ID to post ID, and now for finding the command, and we need to make sure current login user should
be the author of the post, or he should be the
author of the command. So here we use
dollar or operator, and it is an array of condition. Object, object. Here, our first condition should write or second
condition should write. First of all, we check current user is the author
of the post or not. Here we add user
column, user ID, and for second condition, in another object,
we pass codes, comms dot underscore
ID to comment ID, codes, commenst
user two user ID. What we want to do if any of
these two condition is true. We want to pull that command
from the commands array. Here at the place of push, we use pull operator and from which field
we want to pull. Yes, we want to
pull from commands, and here we pass the condition, which item we want to pull. So in object, we pass as
good ID to comment ID. Now we don't need this comment variable, and in the response, we simply return message,
comment, deleted successfully. And also, we return commands to post dot Commans
and that's it. Here what I think we should
check post is found or not. Otherwise, we directly get this message without
doing any deletion. So before this response, we add, I post is not available, then we return response
with status code 403, and in the Jason,
we pass message to unauthorized or post
comment not found. Will useful. Save the changes, and let's test this
implementation. Back to Postman. And here, let's duplicate
this reply API test, change the name to deleting a specific command,
method, to delete. And in the endpoint,
we just need to remove these replies
and send the request. C commentate it successfully, and if we again send
the same request, see, here we get not found
error, so it is working. As you can see, creating
API is not hard. If the logic is clear to you, then you can create
any kind of API. Now there are many more
APIs for post and comments. We are not creating all of
them because as you can see, it is repetitive and if I
show you all APIs one by one, then it will also bore you. You can define APIs according
to your project needs. It's totally up to you.
182. Error Handling: Now currently in our project, we didn't add error handling. So I open here our
previous project, Card Wish, and from
the index dot JS file, we simply copy this
logger variable and also the both error handlers and in our inkifiPject
in Config folder, we create a new file called logger dot js and
pise that code here. Now, in this file, we
need this Winston object. So in terminal, we write
NPM, install Winston, at the rate 3.17 0.0, and also we need
Winston MongoDB, at the rate 6.0 0.0,
and eight Enter. Good. Now at the top, cost, Winston is equal to
require Winston. And also, we need to add require Winston Mongo DB for
logging in the database. Now in our logger,
we need to change this database string to
process dot nw dot dB. And at the very end, we simply module dot exports
is equal to logger. We can use this logger
in any of our files. So in the index dogs
file at the top, we add Const logger
is equal to require. We go to Config folder
and in that logger. Now, from the previous project, we also copy this cache
method of Mongoose connect and simply replace
it with our cache method. So from the Cardwih project, we copy this custom
error middleware for handling errors in API request and paste it in our Linky fi project
after all our routes. Also, here we can console dot
log error for simplicity. Now you might ask, we don't need to add those functions for handling ungod exceptions and
unhandlePmise rejections. Yes, here, we don't
need to add them in our index dogs file because when we import
logger from loggers file, these two methods
will automatically execute because they are set
up globally in this file. And that's why we
get here clean code. So you can see how simple it is. Also, I always add this logger file in
my Github repository. So whenever I need this
error handling code, I can get it directly. But for to understand
what is inside the code. If you know that, then you can use any code and make it yours. That's it for this section. In the next section,
we will learn very interesting
real time features. See you in the next section.
183. Section 15 Introduction: Welcome to the 15th section
of the ultimate no JS course. In this section, we are going to learn about real
time communication from our back end to front end and also front end to back end. This is going to be fun
because we will also taste our implementation with
the front end like this. Imagine these are two users
from different computers and they are chatting with each other on our inkifi application. See how smoothly it works. Our both users are getting
real time messages, and also when one user
is typing message, another user getting
the typing indicator, which makes user
experience one to chat. Also, we will
implement group chat, and by that, more than one
user can chat in groups. So we will implement
these features in our back end and taste
it with the front end. Also, one thing I
want to clear is here we are not going to
create front end from scratch. Is just for tasting, so you can understand more about
these real time features. I'm very excited for this
section and hope you are too. So let's get started.
184. Creating Chat - Message Model: For creating the Jet API, first, we need to create Jet
collection or model, whatever you want to call it. So here in our models folder, we create a new file
called chats dot js. Now, from the
previous user model, we copy whole code
and paste it here. Good. Now, first, we change the variable
name to chat schema. Also, let's remove everything
from the schema object and also change this user
to chat and done. Now, let's define
the chat schema. But before that, let me
clear what is chat here. Hat is like a room in which two or more users can
communicate with each other. In simple words on Instagram, did you see on the left side, we get all chats shorted
by the latest message. Those fills are chat. It can be groups or it
can be individual chats. What we need for these chats. Currently, we are just
implementing one to one chat, so don't worry about
group chat features. Just think about one to one. So for individual chat, we can create center field, and also we can create
receiver field. And for both, we add type to
Mongos dot schema dot types, dot Object ID, and ref to user. So suppose user A sends
the message to user B. We add user A as sender
and user B to receiver. Now here is one big problem. Suppose now user B is
sending message to user A. Then sender will be user B
and receiver will be user A. This will create a
new chat document, and these both users will not get the same chat for
talking to each other. Now what is the solution here? It is really simple. So at the place of defining sender and receiver
both separately, we can add single
field for them. Like here, we had participants, which is the array and in that we will store the user
ID of participants. Type to Mongos dot
schema dot types, dot object ID, and ref to user. If user A sends
message to user B, then we have user A and user B, both IDs in the
participants array, and if user B sends
message to user A, then also both IDs are
available in the participants. By this way, we will
get single chat for user A sends
message to user B, or user B sends
message to user A. This participants fill is used to fetch jets for specific user. Now, after participants,
we want date and time. For that, we can simply enable timestamps to true,
and that's it. Now you might ask
what about messages? For messages, we will
create another model. So copy this schema, and in the models folder, we create a new file
called messages dot js and paste
here that's schema. Now, first, we change the
chat schema to message schema and chat
model to message. Good. Now let's define
schema for single message. So for message, first, we need its chat ID. So type to Mongos dot schema
dot types dot Object ID, and reference to
chat collection, which we just define. Now after that, we
add sender who sent this message and it's type to Object ID and reference to user. So we can simply cut it from the participants and
paste it for sender. Now we don't need
this participants. After that, we have
content type to string, and required to true. This is the message content or message text which
users wants to send. Now after content, what we add, yes, we need status
of that message. Type to string um to array, and here we define the
set of values like send, delivered, and read. And by default, we
set status to send. In the future, we will
update status when our message delivered or read by other users,
and that's it. Also, for messages, we add timestamps to true forgetting
the date and time. Now you might ask why we don't add these messages
in the chat model. We can do messages to array and define all messages
with its related chat. Yes, we can do that, but
one reason why we don't add messages in the chat model
because in the messages model, we are going to perform add, update, and delete many times. We add messages in
the chat model, then we need to deal with
really deep nested data, which can confuse us. And also, if we add messages
array in the chat model, then when we open chat, we have to fetch all
messages from the back end, and that is a lot more
data in single request. So by separating the messages, we can only send last
ten or 15 messages, and then if user wants to
see older chat messages, only then we will fetch other messages using
Pagination technique. So that's why we separate
messages from the chat model. Here in the chat model, we can also add one more fill. Last message, which is type to Mongos dot schema dot types dot ObjectID and ref
to our message model. By this, we can display
the last message with chat and we don't need to find it from the
messages collection. Now in the next lesson,
we will create API for getting the list
of jet for login user.
185. Getting Chats For User: Let's create API for getting the list of jet for
logged in user. Here in the routes folder, we create a new file
called jets dot js. Now, first, we create Router, so Cs Router is equal
to require Express. This expression will
return Express Object, and here we simply
get dot Router. This is the single line code for these two lines which
we are using till now. At the end, we simply
export this with module exports is
equal to Router. Now what we will do, we also add these routes
in the index dogs file, press Control plus P and
go to index dogs file. Here we add cost jet routes
is equal to require, we go to routes
folder and inside it jets at the very bottom, app dot U here we add prefix API slash jets at
the second parameter, we add jet routes and save this. Now we can define our APIs. First, we want to
find out the list of jets for current log in user, router dot get endpoint
to forward slash, and here we need Os Middleware because only log in users
can see chats or do chat. At the end, we add Asyn callback function
with request and response. Now inside this callback
function, first, we get cost user ID is equal to request dot userid
dot underscore ID. Next, we can find chat. Cost chats is equal
to await chat. See, auto input works
dot Find and here we pass our condition in Object
participants to user ID. Here we can also use
Mongo Di Be operators, but this will work as well. If it doesn't work,
then we will change it. What is the matter?
Now on the front end, we want to show
something like this. First of all, user who
receive or send messages. Also the last message, who send the last message, and on which time they send. Here we need to populate the user data of
participants. Dot populate. In the first argument,
we pass the fill name, which is the participants, and second argument
is fills name, let's say, underscore
ID and user name. Now also we need the information
about the last messages. Again, populate what
we write first. Write, last message, fill name, and then second, which fills we need from
the last message. First, we need sender
who sent this message. Next content, the
text of the message, and next, it's created at. Here, if you are confused
about Phil's name, then you can also watch Schema. Now here, little bit
challenge for us. As we can see, we are getting sender of
this last message, but it is also ID,
not the user name. We need username of that sender. So here we can do like this. After this populate method, we add another populate method. First, we add last message dot sender
and second argument, we add fill name,
which is username. Now I think this looks
a little bit confusing. So there are another way to
write nested populate method. Let me show you. Till
now in populate method, we pass two arguments. On first argument, we pass the field name which we want to populate and at the second, we pass the properties which we want to get
from the relation. This is the first way. For
nested populatet method, we can pass here
populate method. Now instead of pass
here arguments, we can pass object. First, in this object, we pass path property. This path is a field name
which we want to populate, which is last message. And another property is select. Here, we select which data we want to get
from the relation. Let's say center
content and created at. Now here we want to again
populate the sender data. After select, we pass another property
which is populate and here we pass object with same two properties
path and select. Path to what we add
here, write sender. We don't need to write here
last message dot Sender because we are already in the last message
populate method, and here we pass
select to user name. Here, if you want
to go more deep, then we can also pass
another populate property, for now we don't need it,
so let's remove this. Now here, these two
populate methods and this single nested populate
method both works the same. I like this nested approach because it is clear
and not confusing. If you like first
one, then go for it. Don't worry, it's
completely up to you. Now after populate,
we want to show jets in reverse order
of the last message. But here, how can we sort jets? It is really simple. See
when someone sends message, as we know, we will
update that jet, last message property, and
when we update that property, our jets updated at
property gets updated. So we can short by that
updated at property. So dot short, and in object, we pass updated at
value to minus one, and at the end, we return
response dot json, Object with chats to these
chats, and that's it. Currently, we get the
chat list for login user. Now, next, when user
open one specific chat, then we need to show them
messages of that chat. In the next lesson, we
will create API for that.
186. Getting Messages of Specific Chat: Let's get messages of
one specific chat. So outer dot GT. First, we need the
ID of the chat, so slash column, chat
ID, slash messages. Also, we need here or
middleware and at last is in callback function
with request and response. Now inside this callback, we get cost chat ID is equal to, and we destructure
request dot PAMs. After that, cost
messages is equal to await message dot find. Simply here we pass Object
chat ID to chat ID. Let me check it is
chat or chat ID. Yes, it is chat ID. When I was learning not
Jazz, this happens to me. I write wrong fill
name in the query and spend almost a
day finding error, and at last, I check
the fill name. So make sure it not
happens to you. Now here we want to populate
this data. So dart populate. And here we want to
populate sender, and what do you want to get
underscore ID, and user name. Now, after that, we
want to get messages in reverse order because that's how we display messages
on the front end. So sort here in object, we pass created at
to yes, minus one. Now, this query will return
all messages of this chat ID. But in real world, we only need last ten to 20 messages because nobody will see
messages from the start. If someone wants to
see previous messages, then we can fetch it
later as pagination. So for pagination, we write
here, let Ci brackets, page comma limit, is equal to destructure here
request dot query. Also, we pass here that default value if front
end will not pass this data. So page to one and
limit to let's say ten. After that, we need to
convert them in integer. So page is equal to pass
integer and pass here page. Duplicate this line with sift plus Alt plus down arrow or Sift plus Option
plus down arrow. And here we change
this page to limit, and also here limit. In our query, we simply add
Skip method, and inside it, page minus one into limit, and we also add limit
method and pass here limit. And also, at the end, we add len method. We create a lot of API
with pagination, right? Now we can also add here
C has previous messages, is equal to here
we pass condition, messages dot length
is equal to limit. If it is true, then return true, else, we return false. Now at the end, we simply return response dot JsunObject
messages to messages, has previous messages,
to a previous messages, page to page, and
limit to limit. And that's it. For now, we don't have chats
and messages, so we will taste these APIs after we create
chat and messages. In the next lesson, we are going to create sending message EPI.
187. API for Sending Messages: Now let's create API
for sending messages. So here we add router
dot post for endpoint, we add for our slash
column jet ID, in which jet we want
to send message, slash message or messages, whatever you want to call it. Also, we add Osmitalware and acing callback function
with request and response. First of all, we will
get user ID from the request dot user
dot underscore ID. As we get cost Calibakets chat ID is equal to request dot
PRMs, what else we need? We need content of the message which we
get from the body. Cost Calibacket content is
equal to request dot body. Now, before doing anything, let's check here condition. If content is not available, then we return response with Stata code 400 and
in the JSON method, we return object with
message to content, or we can say message, text is st required. Now what we want to
do inside this API, we want to simply store
message in the database. That's it. Here we can do cost, new message is equal
to new message. Here in the object, we pass jet ID to chat ID, sender to what write, user ID, and then we
add content to content. After that, we await new
message dot c. And at last, we simply return response
with Status Code 201, and in Jason, we pass Object, new message to new message. Now before we taste this API, you might ask, we didn't
create API for creating chat. Then how can we pass jet ID? Your question is right. We need to create Jet API
before we save message. Here we create a new API, router dot post, point
to slash Create chat. Here, we need middleware and also in callback function
with request and response. First of all, we
get Cost user ID is equal to request dot
user dot underscore ID. Also we need receiver ID, who is the receiver or
another user of this jet. Cost receiver ID is equal to request dot body
dot receiver ID. As we are getting this receiver
ID from request dot body, it's good practice to validate. So I receiver ID
is not available, then we return response with Status Code 400 and in Jason, we return object with message
to receiver required. Now before we create a new chat, it's better to find these two users have already
existing chat or not. So let chat is equal to
await chat dot Fine one. Here in object, we pass
participants to object, and in that we add
dollar or to array, user ID, comma receiver ID. Now, this query means a jet in whose participants
there are these both user ID and receiver ID. Now, what if it is a group jet? Then it might possible there are multiple jets in which these
both IDs are available. Here we need to make sure only user ID and receiver
ID should be available. For that, we can use
here dollar size to two. This is another useful
operator of Mongo Dib. I recently learned about it. Now, what if there are really no chat for user and receiver? Simply then to
create a new chat. So if chat is not available, then we do chat is
equal to New Jet. Here in the object, we add participants
to array user ID, comma, receiver ID, and then
we can await chat dot c. At the end, we simply return response which status code 201. In JSON, we simply send
this chat and that's it. Now we have create Jet API, before creating a new message, we should first check user
pass a correct chat ID or not and also is the
participant of the chat or not. If we don't check
this condition, then anyone can send message
to any person or group. Cost chat is equal to await
chat dot fine buy ID, and here we pass JatiD. And after that, we check
if Jet is not available or jet dot Participants
dot includes user ID. If sender is not in
the participants, we pass your exclamation marks, and then we return response
which status code 403, and in JSON message
to access denied. Now we verify jet. Now in the new message, we replace this chat ID to
chat dot underscore ID. Now, what else we have to do
when we send a new message? Can you guess? Right, we have to update the last message
property of the chat. Remember, after we
save the message, we write chat dot
last message is equal to new message
dot underscore ID. And after that, we also
await chat dot save. And that's it. Now
finally, T is this API. Open Postman and here we create a new folder called
chats and inside it, we add new request called
create a new chat. Good method to post. I endpoint, we add SGDP, Column double forward
slash Local host, Column 3,000 slash API
slasheTS slash CretHaT. First of all, we need to
pass token in the header. Back to our login API and
here for Hari account, good copy this token and in our create chat
API in headers, we had authorization to error, pace that token and
send the request. See, here we get
internal server error is because receiver
ID is required. So here in the
body, we go to aw. Here, we pass JSNObject and pass here receiver ID to codes, back to Mango Di
Becompass and here I copy this other user ID and paste
it in the receiver ID. Now send the request.
See here we get new jet, copy this jet ID. As Test, send message API. Make sure you save this API with Control plus
or Command plus a, and then duplicate
this post request. Change the request name
to send a new message. Also, I notice in our API, we have to pass chat ID in the request parameter and
content in the request body. So we can do
something like this. We can change our API and
point to only send messages, and here we get chat ID
in the request body. Good. Save these and
back to Postman. Here, we change our endpoint to slash chats, slash
send messages. In header, we
already have token, so we just need to
pass request body. Here in the object, we pass content to let's say
this is the first message. Also we add jet ID and
paste that jet ID here. Now send the
request. See here we get the new message
content to our message, and in the sender, it is our ID. Now it's better that at
the place of sender, we can see the ID and user name. By that, it's easy to display
new messages on front end. At the very end, we define cost, populate message is equal
to await message dot find BydNew message dot
underscore ID, dot populate. First, we add sender, and at the second argument, we add underscore
ID and use a name. And in the response, we return new message
to populate message. Save the changes, and let's
send another message. This is the second message,
and send the request. See, in the sender, we get ID and user name. So here, our send message
API is working well. Lovely.
188. What are Web Sockets: So in the previous lessons, we created APIs for
sending the message, and also we define API for getting messages
of specific chat. Now imagine this
is our front end, and it has open chat page for
two users Hurley and Mike. So this is the screen of Hul and this is the
screen of Mike. Both are on the chat page in which they both
are participants, and they both gets two messages
which are history chat. Now imagine here Harley type a new message
and click on Send. So on the Send button, we will call API for sending
a new message, right? And as we know, this API will save this message
in our database. Now the problem is how
Mike will get this message because Mike already fetched all messages of this
jet and after that, Hurley sends the new message. One was, Mike will send get messages request in every 5
seconds to get new messages. It's like Mike asks the server, is there any new message for me? Server says no. Again,
after 5 seconds, Mike asks, is there any
new message for me? Again, server says no. Then if server gets new message, then in another 5 seconds when Mike again ask
for the new message, at that time, server
will send him message. Now imagine we have 10,000 or 100,000 users on our website, and all users are sending message request in
every 5 seconds, which is really meaningless
because we will only get new data when our
server will get new data. Otherwise, our requests are just going to server
and coming back. This will definitely
makes our server slow or even crash sometimes. So here we can't rely
on this solution. We need something which automatically tells
Mike if he has new message and automatically
send on the mice screen. And to solve this issue,
we have web socket. So what is web sockets? Web socket is a way for
our website front end and our server backend to communicate with each
other in real time. It is like two way conversation. In simple words, web sockets will help us to build
real time conversation. Of a web socket
connection is like a phone call between your
browser and the server. Once the call is made, browser can talk
to the server and server can also
talk back anytime. Now you might ask, what is the difference between
APIs and web sockets. Imagine this is our front
end and this is our server. Now suppose on the front end
we want to get some data, so we send an API request from the front end
to our server. Server will process that
request and send response to our front end here our
API connection is closed. For example, from front end, Harley sends a message to
our server using post API. Now our server will
store that data in the database and then return the new message
to the front end. Here, our connection is closed. This is the way of
connection using SGDP APIs. Now let's see how
web socket works. Here we have front end, and this is our server. Now, first of all, here we create connection
using web socket. By this connection,
our client can send the new messet data to the
server without any API call. Same as server can also
returns new messet data. After server did a
new message data, still this connection
stays there. It will not close until we close our website or we manually
close it with the code. Let me explain you with
the real world example. Here is Harley, here is Mike, and here is our server. Suppose Harley and
Mike both builds connection with our
server using web sockets. Now if Halley sends
new message to server, server will store that
message in the database, and then it will
send that message to the mic without Mike
send API request. Now, if Mike send
message to the server, server will also store that
message in the database, and then it will send that
new message to Harley. Now if our server will get another message for Mike
from different user, then also Mike will
get that message, but this will only
happen if Mike build connection with server
using web socket. If Harley is connected
with server, but Mike is not connected
to server using web socket, then the new message will
store in the database, but it will not
reach to the mic. That's how web socket works. Web sockets are
used for building real time communication between
front end and back end. Don't worry, it
is really simple. Understand all this when
we apply web sockets in our back end and taste
it with the front end.
189. Connection of Socket: Now in the previous lesson, we see that if we want to send and receive real time data, then we have to connect with web socket for dealing
with web socket, we will use the most popular
library socket dot IO. This library is used by
many popular platforms, so we can definitely use it. Open up terminal,
and here we write NPM install socket dot
IO at the rate 4.8 0.1. It Enter. Good,
minimize this terminal, and let's add socket in
our backend application. In the index dot JS file, we input cost Coli Brackets, server is equal to
require socket dot IO. And at the bottom, we can add cost AO is equal to new server. Now in this server, we have
to pass our SGDP server. Without SGDP server, the socket will not get
connection request. For that, we need to
create SDDP server. Cost SDDP is equal
to require SDP. This is the built in SGDP
module at the bottom, we add Const server is equal
to SDP dot Create Server. Now we can pass this
server in our socket. Good. Now here is one question on which board the socket
server will listen. Because here for Express app, we define port 3,000
and at the end, we listen with that port. In our current implementation, if we add socket dot
Listen to 5,000, then our APIs will run on port 3,000 and our socket
will run on port 5,000. This is not we want. We want our APIs and socket both
works on the same board. So for that, here in
the create server, we have to pass
this Express app. By this, this SGTP module will create server
with this express app, and that same server we are using for initializing socket. Also at the bottom, we need to change
this app dot Listen with server dot Lisen. Otherwise, this will
not work properly. Save these changes,
and let's restart our application to make sure it is running
properly or not. See here we get server
is running on B 3,000, and we also get
Mongo Db connected. That means it is
working properly. So to quick recap for initializing socket
in our back end, we need to first create
server and pass Express app insideE then we can use this server to
create socket server, and at the end, we have to also server dot
LISN to this port. So here we successfully initialize socket
in our application. Now from the front end, anyone can connect
to this socket. But what do we want to do when someone connect to the socket? We need to add that logic.
It is really simple. Here, after these app routes, we add IO which is the
socket instance dot on, and at the first argument, we pass connection, and
at the second argument, here we pass callback
function and what we do when someone
connects to our socket. For now, we simply
console dot log. User connected. Whenever from the front end, someone connects to our socket, then this callback
function will run. Now, how can we taste
this implementation? For tasting that, I
created one dummy, simple SGML, and
JavaScript application. Don't worry. If you
don't know anything about front end, I
will explain you that. Also if you don't
want to taste it, then you can see this tasting. Will also help you to understand the workflow
of the socket. In the resources folder, we have Project three
folder, and in it, we have LinkFi testing app, copy that folder and move
to our projects folder. Here we paste that folder. Now open this
folder in Vas code. Good. Here, we first use
this simple chat folder. The reason why I use here front end app because
with real front end, learning socket will be fun, and also it will clear
full workflow of socket. Don't worry, you don't need
to write any code here. Just do as I do. So to run this front end, track this simple
chat dot GML file in the browser and open
it in the new tab. See, here we get this
type of interface. Now back to the front
end vis code and open this simple chat dots file. At the top, you can see write socket is equal
to AO and inside, we pass our backend Link, which is Local
host column 3,000. This expression means we
want to connect with socket, which is initialized
on this pot. Also, you might ask how we
are getting this Ao method. We are getting this Ao method because I added
socket with Dan Link. See, here it is, and
because of that, we are getting here Aomthod. Save this page and
back to our front end. Let's check our Console, so right click on the
page and go to Inspec. See here we get this
course error for local host column
3,000 slash socket. Now you might ask, we already enable course in
this express app, why we are still getting
this course error? The reason is we only configure course for
our express app, but we have to also configure course for our socket server. It is really simple. Here in the server method, at the second
argument, we can pass the options for
the socket server. I object, we pass course
here also object, first property origin to star, which means any port is allowed. Also, if we want to pass
only our front end, then here we can pass that URL. But for now, we stay with star. You can copy or port from
the front end browser. Now after origin, we
can pass methods, which is array and we
pass here, Get and post. This method of sun specify which SGDP methods are allowed
during the web socket ndsaC. Now you might ask what
is websocket Hensak. When a client connects to
a socket or di server, then it start with SDP
request. The handshake. If it is successful,
then it will upgrade to a web
socket connection. During this handshake,
the server checks if the incoming SDDP
method is allowed or not. It's important to add these
methods because with that, our connection will establish. If we don't specify
the correct methods, then browser might block the connection due
to course error. Save this and let's
refresh our front end. See, here we don't
get any error, and if we check our back
end in the terminal, we get Console message
a user connected. And also we refresh our
application one more time. Then in our back end, see, here we again get a
user is connected, which means our front
end user is connected successfully with our
back end using socket. So to quick recap, after initializing socket,
in our back end, we add ao dot on connection, and at the second argument, we pass callback function. So whenever a new user
connect with our socket, then this callback
function will run. And also inside this
callback function, we will write all our
logic for socket. That's how we initialize and handle the
connection of socket.
190. Socket Emit and on methods: You might think writing a socket related
code is difficult. It has many methods
and functions. How can we learn them? If you have these types of
equations, then don't worry. For the first time, I also think socket is very difficult. But when I actually
apply it in my project, it is really simple. Socket has mainly two methods, socket dot d and socket dot on. 90% time, we will use
these two methods, and trust me, they
are really simple. So as we know,
socket is used for real time conversation between
front end and back end. Basically, it means anytime
our server or back end can send and receive data from the front end without
any API calls, and it works in reverse as well. Front end can also send and receive data
from the back end. Suppose from our front end, Harley wants to send one message to back end like this
is the new message. Now the thing is, how can we send data from the front end? So for that, we use socket
dot E send message. This is the event name, and at the second argument, we can add our data. In plain English,
emit means send. So we are telling our socket, emit the data, or in simple
words, send the data. By this, we send message
from the front end, but in the back end,
we need to also write the logic for
handling that event. And for that, we use socket
dot on send message. This is the same event name which we send from
the front end. And at the second parameter, we will pass callback function, which will run when we
get send message event. So we will write all our logic
in this callback function. Whether we want to
store message in the database or we want to directly send them
to other users. In simple words, just
remember when we want to send data from front end to back
end or back end to front end, we will use socket dot
method with event name, and when we want to receive data from the back end or
from the front end, then we use socket dot on, and here we write the same event name
and callback function. Let's implement this
in our application. So on front end dab first, here I added submit event for our form in which we have message input
and submit button. And here we get input message and use a name filled
in these two variables. So everything is set. We have to send
message using socket. So here, which socket method we use for
sending the message. Right, we will use socket dot
m. At the first argument, we write the event name, let's say, send message. And at the second argument, we can pass data we want
to send with this event. Let's say we pass here object
with sender to object under square ID to let's say 123 and use a name to
username dot value. And another property
content to input dot value, which is our message text. Also, we send created
at to new date, and also we pass status
to send. And that's it. Here, we added fills as we
define in the database. Here, by this code, we send the message data
when we submit a form. Now, let's see if we are getting this data in our backend or not. So tell me which socket method we will use for getting
the event data. Right, we use socket dot on. You learn pretty fast, lovely. In our back end
in the ao dot on, we at socket dot on. At the first argument,
what do we pass? Right, we pass event name
which we want to handle, which is send message. Make sure you write the same name as we pass
from the front end. Otherwise, it will not work, and also it is case sensitive. Now at the second argument, we pass callback function, which will run when someone from the front end send this
send message event. In the parameter
of this function, we get the data, and for now, we simply consoled log new
message from front end, and we adhere this data. This data is the message object which we pass from
the front end. Now you might ask,
how can we get socket in this o
dot on callback? Simply we get here
from the parameter. Using this socket, we can
add emit and on methods. Save these changes, and let's send message
from the front end. Refresh the page right here, user name and right here, message, first message,
and click Onsent. Here, nothing happens. And if we check our back
end, check our terminal. See here we get user connected, and then we get that
message object, which means we successfully get the message
from the front end. That's so simple to send
and get data using socket. Just we have to remember socket dot and socket
dot on method. Now here we get the message
from the front end. Next, what we want to
do with that message. Obviously, we want to store that message in the
database and then return that message to front end our front end can display
that message on the screen. For now, don't worry
about database logic, we will implement it later. For now, we just want to send
this message to front end. Tell me which method
we will use for sending the data from
back end to front end. Yes, we use socket dot, and at the first
argument, we pass event. Get message. You can
give it any name. It doesn't matter. Now what do we want to send
with this event? Want to send the same data. In real world, we will send new message object which
we store in the database. Now save this file,
and in the front end, we need to handle
this G message event, and you already know
which method we have to use for handling the event
or getting the event. Right, we will write
here socket dot on. First, we pass the event name, Get message, and at
the second argument, we pass callback function, and in that callback, at the
parameter, we get that data. What do we want to do
with this message? Simply, we want to show that
message on the browser. So for display the message, I created this display
message function. Simply call this and
pass the data in it. This function will create message and display
it on our browser. Also, here you can see
that logic if you want to. Save this, and let's taste
this implementation. Make sure you refresh the page after doing any changes
in the front end code. Otherwise, you will
not get these updates. Now we use a name to Hurley and message to first message
and send the message. See, here we get the message on the front end within a
second or lays in second. So to quick recap, socket
has mainly two methods, socket dot m and socket dot on. Socket dot is used to
send or emit the data. And with socket dot on, we can handle that sent event
or we can see the event. If you understand these two
methods, then congratulation. You 90% socket is done.
It is that simple.
191. Getting messages for both users: In the current implementation, we taste it with one user. Now let's taste it
with two users at the same time like Haley and Mike are talking
with each other. So open another private
tab of the browser, or you can also use another browser and open
the same S DML file. Now suppose first screen is Halley spec and second
screen is Mike spec. They are chatting
with each other. So here Halley sends message, and here he get this message, but Mike is not
getting the message. How here from the back end, we are sending
message with socket, how it is working for Halley
and not working for Mike. The reason is here
from the back end, we use socket dot m.
This method will send event to only that user who
emit the send message event, and that's why Halley is
getting this message, but Mike is not getting. Now, what is the solution here? Because we want our message, we'll send to other
users as well. Simply at the place
of socket dot, we will use this ao dot method. Using ao dot, we can send message to all users who
are connected with socket. Save this file and let's
taste this implementation. Refresh both pages. Good. Also, here we
write username for both users and from the Hal
we write message and send it. See, now Mike also
get this message. And if we send
message from Mike, see, He also gets it. And it is how real
time chatting works. By using socket dot
from the back end, we can only send event to
users who emit that event. And if we use ao dot, then our socket will
send that event to all users who are
connected using socket.
192. Logic of Joining Chat Room: In the previous
lesson, we are getting messages instantly
in both users. What if we add another window
and see what will happen? So let's add another
private window and open same HTML file. Now write here, user name and from the first window,
we send message. See here for both user,
we get the message. And if any other
user send message, then also other users are
getting that message, which means our
socket is sending the message to all users who
are connected to the socket. It's like you host
one big party. Here, people are chatting
everywhere without rooms, and because of that, all
conversation would mix up. It's like user A
talking to user B, but user C hears everything, so there would be no
private conversation. How funny it is everyone
is suting across the whole house instead of
talking gently in a room. Here we also have
the same situation. Our current implementation is sending messages to all users. So what is the solution here? We need to implement rooms. So rooms in socket can solve this issue by allowing us
to group users together. Each group or room is
private space where only members of that room can
send and receive message. In simple words,
room in socket are virtual spaces where
users can join and live. In that you can
broadcast message to everyone in a room without
disturbing others. Suppose Halley and Mike's
wants to chat with each other. When Halley opens a chat, Halley join with
one room which has a unique name like chat
HM Halley, and Mike. Now Mike open jet page
to talk with Halley. Mike also joined the
same room called JathM. Now both users are
connected in the same room. So if Halley send
message in that room, then only Mike will get this message, not
the third person. I Hi close the tab, then Halley lost
the connection with the socket and when
socket gets disconnect, then socket dot IO automatically removes Halley from Room jet hm. By this implementation,
our jets will not get public and also this works
in group chat as well. Suppose user A, user B, and user C are group members, and they all are connected
to specific room chat ABC. Now anyone of them
is sending message. Other two will get this message because they are
connected in the same room. You will understand
this properly when we implement and
taste this feature, and we will do that
in the next lesson.
193. Implementing Joining a Chat Room: Now let's implement room
feature in our inkifi app. But before that, let me
quickly recap the workflow. So when user opens a chat, they join the room
with its chat ID. Only users in the same room can send and receive
the messages. And at last, when the user
leaves or close that chat, then we remove them
from the room. So first of all, when
user opens a chat, then from the front end, one event should trigger
call join room with chat ID. After that, from the back end, we add that user in that room, and when someone sends message, we will send that message to only users who are
joined that room. Here in our back end, we handle another event, socket dot on, and which event we want to handle
yes, it is join room. Now here we get data, or we can say from
the front end, we will send the chat ID, arrow function, and
now to join Room, we simply write socket dot join, and here we pass
name of the room, which is our chat ID, and to make sure we also
console dot log in backticks, user dollar Cali Brackets, socket dot ID, which is the current user unique
ID provided by socket. Join Room dollar CL
brackets chat ID. Now in the send message, we want to send message to only those users who
currently in that room. So we comment out this
previous EIT method, so you can look at it later. And here we write
ao dot MIG message, and pass here data. Now to send this event
to only separate room, here we add ao dot two, and inside it, we need to pass that room name which
we set to chat ID. Now the question is, how
can we get the chat ID? Right, we can pass the chat
ID from the front end. So data chat ID, and that's it. We join user in the room, and then send get message
event only to that room. Now to test this implementation, we need to use
another SDML file. So in the testing folder, you will get another folder
called Romjat and inside it, you will get the file
called Rojatt SGML. Also, in its JS file, first, we connect user with socket, and then we are asking
for room name in prompt. After we get the room name, we send this join room event. This will help us
to join a room, and then rest of
the front end code is the same as before. Just in the send message event, we also send jet ID because we need it
in the send message. Now let's run this file. Dig this file and open
it in your browser. First of all, it will
ask for room name. Here, for tasting, we are
passing any random jet ID. Let's say chat underscore
one, two, three. Okay. As we write use a name. Without it, we might get error. Write use a name, and
if in our back end, we open Console see here
we can see user connected, and then user socket
ID, join room, and here we get our room name, chat, underscore,
one, two, three. Great. Now let's open
another tab and do the same. Write here room name,
chat, underscore one, two, three, because we want
to join the same room. Now here we write user name. And then write
message and send it. See, for both users, we are getting this message, so it is working pretty fine. Now, let's open another window
and join a different room. Let's chat, underscore 45, six. Good. Now, if we send
message from this user, these two users will
not get this message. And if any of these two users are
sending the same message, then this third user is
not getting this message. So this is working as well. So that's how simple
it is to join room and send message
to those rooms. As I previously told you, socket is very simple. Just you need to
understand the logic.
194. Exercise - Typing Indicator: Now it is time for
little socket exercise. In this exercise, you
have to implement show typing indicator when
someone is typing the message. The logic is when users
start typing in the input, we trigger one
socket event typing. And from the back end,
we will send data who is typing and in which
room he or she typing. Now after implementing
that as a bonus, when our user don't
type for 2 seconds, then we will trigger another
event called Stop typing, which will simply remove typing indicator
from the front end. Don't worry, if you don't
know about front end code, just write code for backend, handle the event and send that user is typing
or any message. In the front end
also, I added code, which will work when we write
something in the input box. And also I added commands where you have
to emit the events and also the code
which you have to write when you
handle those events. The goal of this exercise is
you think in event terms. So try to implement it and
then what's the solution. Now, let's see the
solution of this exercise. So first of all,
in our back end, we have to handle event,
let's say typing. So socket dot on typing. And from the front end, we will send object with
jet ID and user name. So we can destructure
it here and get jet ID, and also we get user
name, arrow function. And here we send this message to other users who are
in the same room. So o dot two here we
pass the chat ID, and after that,
dot, show typing. And here we pass message in BTI dollar user name is typing. And that's it. After that, we also need to handle event, stop typing, so socket
dot on, stop typing. And here we also get object
with chat ID and user name. And in the error function, we simply Iot to chatd
dot m, Hide typing. Here we pass the user name who stop typing, and that's it. We don't need any other
thing in the back end. In this exercise, if you
confuse about front end, then don't worry about that. I just added front
end for tasting. You can also skip that
tasting part as soon as you know how to write
socket logic and event. Now, let's quickly see
this tasting part. As I told you in the previous
lesson, here at the bottom, I added this code, which will run when we write
something in this input box. Here, if input
value is not empty, only then we do socket dot
m and which event we emit, we will send typing event, and at the second position, we add object with
chat ID to chat ID, and then we also send
username to user value. In this typing time out, which will run after every 2 seconds when
we write anything in the input box when for two second user didn't
write anything, then we send another
event called stop typing. Here we duplicate this
emit method and pass it here and change this
event to stop typing. Outside of this event listener, we need to handle two events which we emit from the back end. Socket dot on, show typing. Here, we pass Callback function, and here we get message, user is typing in
this arrow function, our front end will
write their logic. For now, we simply add here this logic which I added at
the bottom with command. Now after that, we add
another socket dot on. Stop typing here also
callback function. Here we get to use a name
if you want to use it. And here we move this second
logic for stop typing. Save the changes
and take a look, refresh the page, Enter chat, ID, chat, underscore one, two, three, and write
use a name, Ali. Now in another window, also we refresh the page. Enter chat, D, chat,
underscore one, two, three, and we write
use a name to mic. Now let's write
something from Mike. See here for Hi, we are getting the
typing message. Mike is typing. But here we can see
Mike is getting, Mike is typing. This
is what we want. We want our back end, only send show typing event to users who are
in the chat room, but not for the sender. For that, here at the
place of ao dot two, we need to do socket dot two and here socket dt two,
firsts typing event. I'll explain you this
in just a second, see the changes, and let's
test it one more time. Refresh the page,
write jet ID, to chat, underscore one, two, three, and write a user name, Harley. And also for Mike,
we refresh the page. Enter jet ID, to
chat, underscore one, two, three, and write
user name, Mike. Now type something see now only Harley is
getting Mike is typing, and after 2 seconds, this message is still here. Let me check the event. We handle here stop typing from the front end and which event we are emitting
from the back end. Oh, it is hight typing. So on our front end, we change
this event to hide typing. Save the changes, and let's
test it one more time. Refresh the page. Write chat
ID to chat underscore one, two, three, and right
here, use a name. So for Mike, we
repress the page, Enter chat ID to
chat underscore one, two, three, and right
here, use a name. Now write message from here and if you
stop for 2 seconds, then that message is gone. So this is working fine. Now here is one little thing. See when we send the message, then for little
bit, we are getting Mike is typing. We
don't want that. So we will also emit stop typing event when we
send the message. So copy this socket
dot m for Stop typing and paste it at the
end of our form submit. Save the inges and let me
taste it one more time. Refresh the page, write
chat ID and name. Also, we do that same repress, write chat ID and name. Now we write her
message and send it. See, now we are not
getting that typing. There are four variation of
methods in socket ao dot, socket dot I ao
dot two room dot M and socket dot two room dot m. Let me explain to you
each in simple words. So ao dot show typing. This will send show typing
event to all connected users, whether they join a room or not. It will send show
typing event to all users who are
connected with socket. Next, we have socket
dot m, Show typing. This will send Show typing
event to only sender. Next, we have ao dot two,
room dot show typing. This will send show
typing event to all the users who join this
room, including the sender. Our case, they are
Halley and mic both. That's why previously, we get show typing event for both user. At last, we have socket dot
two room dot show typing. This will send show
typing event to all the users who join this
room except the sender. I Hal is typing, then Haley will not get
this show typing event. This is the basic difference
between these methods. We can use them
according to our needs. There are no rules about
using only this or that. We will use whatever suits
the most for our logic.
195. Applying real send message code: Now currently in
our application, when user sends message, we simply send that message to all users who are joined
in that chat room. But in real world,
we also need to save those messages
in our database. So even if user left the room, he can see those new messages and also the history
of the messages. Let's save our message
in the database. First of all, I want
to clear one thing. When from the front
end, we send message, there must be a chat already
created between users. For example, if
Hurley wants to send message to mice and if they never did
chat with each other, which means chat is not
created between them. In that case, when Harley
sends the first message, we call separate API which
is our create chat API, and when Harley has et ID, then we will emit send message
event with the chat ID. We already know this, right? Remember, in the starting
of this section, we created send message API. Yes, we need that same logic here in the send message event. So let's copy that code. And in our send message event, we simply paste it here. Now, first of all,
we need to make this callback function async. Good. Now here we are getting chat ID and content
from the request body. But in socket, we don't
have request body, so we have to get chat ID and content from the
function parameter. So here we destructure this data and get here
chat ID and content. Now, what else we need? Right, we need user ID also, so we also get it in this data. And now we don't need this line. Also, at the bottom, we are sending data
in the response. But as we know, in socket, we don't get response, so we can simply remove that and from the socket
at the place of data, we send this populate message. A in the o dot two, we only at chat ID at the
place of data dot Jet ID. Now let's check also where
we use our response. Yes, in the returning error, and also here we use response. At the place of sending
error through response, here we can send error
message through socket event. But the question is to whom we want to send
this error message? Should we send it to all
users active in that room? No, we want to send this error
message to only sender and tell me which socket method we use to only send event
to current user. Right, we use
socket dot m. Let's send event called
error in send message, and we simply send error
message access denied. Now here, our bottom
code will still run. So to stop running it, we can simply add here, return. Here, we also emit the same error event engine
this message to this message. Content message text is master
required, and after that, we simply add return, which will exit our code from running this
rest of the code. Also, let's remove this code. Now our front end can handle this error event
however they want to, whether they show Alert, or they show toast
notifications. So here we need to import
this chat and message models. At the top, before these
routes, we add cost, chat is equal to
require period models, slash chats and
another cost message is equal to require
period models, slash messages, and done. Save the changes, and let's
taste this implementation. In the front end,
roomchat dots file, we can check in send message we are sending the data or not. Yes, first of all, here
at the place of sender, we need to send user ID. For now, we add here
username dot value. So in our username input, we have to pass user ID. After that, content
to input value. Here, we don't need this
created at and status because it will generate automatically by
mangoes, and that's it. Save this, and let's
rephrase the page. Here, we need jet ID, so we copy the jet ID from the Mongo D we come
pass and paste it here. Now we have to write
here user ID at the place of user name
because in event, we set user ID as this
username dot Value. We copy one user ID from the Mongo Divi Compass
users collection and paste it here as sender.
Now write a message. Let's say socket is easy. See, we get the message and
if we check our database, and at the bottom, this
new message is also stood, our send message event
is working properly.
196. Authenticate user in Socket: Now currently in
our implementation, we are getting current user
ID from the front end. But in real world, we can't get user ID directly
from the front end. Obviously, for security
reasons because anyone can take any user
ID and send the message. So we need to make it
a little more secure. And for that, we will
use socket middleware. It is the same concept as we apply middleware in our APIs. As our orth middleware run before every request
callback same as the socket middleware will run before our client
connect to the socket. Let me show you practically. Here before this
ao dot on method, we define our socket
middleware using ao dot U. Now in this method, we
pass callback function, which will run before our
user connects to the socket. For now, we simply add here console dot log, running
socket middleware. Save the chanes and let's
refresh our front end. Here chat, ID, chat
underscore one, two, three. Now, if we check our
back end terminal, see, here we first get running socket middleware.
But what is this? We don't get a user connected. So here in our
socket middleware, our code is stopping here. We need to move it in
the next function, same as we did in our
Express or middleware. In this callback function, at the first position, we get socket object, which is used to get
information about that socket. And at the second
parameter, we get next, which works the same as next function in
express middleware. Now, after this console, we simply call this
next function. Save the changes and
repress the page. Enter here jet ID, and now in our terminal, C, first we get running
socket middleware, and then we get user connected. By this, it is clear the socket middleware is running before our client
connect to the socket. Now in this socket middleware, we can do authentication of
user, and how can we do that? We did it before in
our auth middleware. Right. For authentication,
we use JWT. So in this middleware, we check, user pass
the JWT token. It is valid or not. If it is valid, only then user
can connect to the socket. And if token is not valid, then we will not allow
user because for jet, we need logged in user. Now you might ask,
how can we get token in this
middleware function? So here we will use
the socket object. So Const token is equal
to socket dot Hensig. Here we get data which we pass with our
socket connection. Our planning is to get data in object OT and inside
it, we pass token. Here we write socket
dot hensgtth dot token. After this, we simply
pass here condition. If token is not available,
then what do we do? We simply reject the connection. For rejecting the connection, we return here next function, and inside it, we have
to pass new error. Here, we pass error message to authentication error,
token required. Now, what if user pass token? Simply, we verify that token, and for that, we need JWT. So at the top, cost, JWT is equal to required
JSON web token. Good. Now in the database, we add JWT dot verify. At the first argument,
we pass token, and at the second argument, we pass our secret
key process dot Env dot JWT underscore key. Let me double check
this variable name. Yes, it is JWT key. Now, if this token
verifies successfully, then here we get to user data, which we pass with token. We store it in
variable called user, and then simply we can do socket dot user is equal to this user. Also, I want to let you know the socket object is
different for all users. So when our user
connect to the socket, our socket will assign
them this object, and because of that, it is
individual for all users. This socket user has our
current logged in users data. So to see that, we
simply console dot log, socket user, and adhere
socket dot user, and then we have next function. Now, what if this
token didn't verify? We need to handle
that case as well. So here we add try and cache blog and simply move these four
lines in the Try blog. So anything goes wrong
in these four lines, our cache method will run. So in cache, what we do right, we simply reject the connection. So let's copy this
next function with the error and paste it here. Also, in our current
tasting front end, we need to send the socket dot user data from the back end. So here I simply add socket
dot user data event, and simply we pass
socket dot user. By this event, our front end will get logged in users data. Otherwise, we need to manually enter the
user ID for tasting. Now let's this implementation. For that, we need to run
our final testing file. Honestly, this is the
last testing file. Don't worry. In our
front end folder, we get folder called
final testing. And here, as we know, we have to run this SDML file. But before that, let's see what I did in the
JavaScript file. First of all, I
ask for a token in which we have to enter
Jason Web token. If we enter token, then only we will
connect user to socket. But wait in this
connection, what I did, I add here object which we send to the socket
and in that object, we pass OT and also
token to our token. This object we are getting
in our back end in the socket dot hang
dot th dot Tgon. Now, if this token is verified, then our connection
will successful. And if this token is not
verified or expired, then in the socket
dot on Connect error, we get the socket
middleware error. And also, when we
get user data event, I simply set that data
in this user variable. Also all code related to socket, we added here in
this if condition. Let's see it is working or not. So open this SDML file in
the browser close tabs. Now, first of all, it
will ask for token. Let's pass here one, two, three, and as we know, this token will not verified
by our back end. Let's see what we get. Hit Okay. See here we get authentication
error, token required. Open postman and simply open login API and send the
request and get the token. Copy this and simply paste
it in our SDML file. See, we don't get the error, and if we check
our Bend terminal, we get here socket user
to our users detail with ID and user name and we
also get a user connected, which means our middleware
is working pretty fine. Now, we use this socket dot
user in our socket event. In the send message event, remove this user ID from the
parameter, and at the top, we add cost user ID is equal to socket dot user,
and a Score ID. Make sure you check the ID
property name and also in the typing event at the place of getting the username
from the front end, we can pass here socket
dot user, dot user name, and we do the same in
the stop typing event, socket dot user, dot user name. Now also, we remove user name
parameter for both events. Now, let's again taste
this send message. So repress the page
and base the token. Now here we need the chat ID, so copy that also from
the MongoiVcmpass. And paste it in the chat
ID and click on Join Room. Without joining the room, we can't send message. Now, if we send the
message from here, let's say this is final
tasting first message. See, here we get
the new message, and if we check our back end, then it is also
getting stored in the database with the
proper user data. So to quick recap in our socket, we should not get user data directly from the front
end. Will be no secure. So we create this socket
middleware and ask JWT token before connecting
user to our socket. After that, we verify
that token and simply set the token data
in the socket dot user. So here we can use this socket dot user in
our whole socket logic. Also, as we know,
the socket dot user is individual for
all socket users. So that's how we apply and secure our socket
using JSON Web Token. Here, as our previous
concepts are clear, it is really easy to understand advanced topics like
the socket middleware. And that's why I am explaining socket step by step so you
can understand it properly.
197. Marking users as Online and offline: How do you know, in
many chat applications, we get user active
or online mark. Do you think how they
apply that feature? Don't worry in this lesson, we will mark users as
online and offline. First of all, what do you
think when a user is online? Think about it in
the human language. Our user is online when
they connect to socket, and when user is offline, write, when user left
your application, or we can say they get disconnected to socket
simple as that. Here before these IO methods, we declare a new variable
cost online users is equal to new map. Now, some of you might
ask, what is this map? Map is an advanced
verson of our object. It is also collection
of the key value pair. Map has more features
than object. So as we know, in
object, key is spring. In value, we can
set it to anything. But in map, we can set
anything string, number, bullion, object,
array, function, any type can be key. In short, for frequent
addition removers, map is generally more
efficient than using objects. It is designed to handle key value pairs
more efficiently. In this map, whenever user join, we can add that user ID as key and its socket
dot ID as value. So here in the io
dot on connection, we simply write
online users dot set. At the first argument, we pass socket dot
user dot score ID, which is the key at
the second argument, we pass socket dot ID,
which is our value. Now we just need to remove user when their
connection will lost, which simply means
this user is offline. So at the very bottom, we add socket dot on. Here, we pass disconnect, and then we pass
Callback function. Disconnect method will run when users socket
connection will last. At that time, this
disconnect method will automatically run. Also, when user close
the browser tab, then also this disconnect
method will run automatically. So we don't need to worry about a a disconnect method
from the front end. Here we simply add
online users dot delete, socket dot user
dot underscore ID, which is the current
user user ID. See, in the map, we
can directly use set and delete method to add
and remove key value pairs. Now let's see it
is working or not. So we simply adhere
console dot log, online users, and
adhere online users. So let's copy this
console and paste it when we add user in this
map. Now, let's taste this. So p the SDML file
in the new tab, paste here your token. And if we check
our Bend terminal, see here we can see
the online users key to user ID and
socket ID as value. And if we close our tab, then here we can see our
online user is empty. So it is that's easy. You can send these online
users to your front end and show indicators as green
dot or something else.
198. Multiple sockets for Single user: Here is one thing in our
current implementation. As we know, our application
will be used on browser or our backend
can be used in apps. If our user connected
in one tab, then user ID and socket ID will added in
the online users. Now, if he opens a new tab
and connect to socket, then in our online users, we get another key as user ID. But here, previous
key and current key is same because of that, this socket ID will be
replaced by new socket dot ID. Now imagine he close
only the second tab. It will also remove that user key value and
mark our user as offline. But here he is still online. He is still online
in the first tab. So here we need to solve
this duplication issue. So what is the solution here? We can add user ID as
key, same as before. But at the place of setting
socket ID directly, we can add array or
set of socket IDs, which means two tabs
has two socket IDs. We have both in this array, and if user close only one tab, then we can remove only that particular socket
ID from the array. You can see how
advanced we are going. Just kidding. Let's implement
this in our application. First of all, before this, we add cost user ID is equal to socket dot user
dot underscore ID. Now here in online
users dot set, first we have user ID, and at the second
place as value, we pass New Set. You might ask what is set. Set is the advanced
version of arrays. Don't worry. They
are really simple. The reason we use here set
at the place of array is because we can easily add and remove socket IDs
from the socket. This line means we create a new key value pair
in online users. Key is user ID, and value is set which is empty. Here we want to create
a new key value pair if our user ID is not already
available in online users. So here we pass condition. If online users has user ID, if this is false, we pass exclamation Mg. Only then we
create a new key value pair. So move this line here. Now after we add new pair, or even if user ID is already available, then
what do we want to do? Right, we simply wants to add
socket dot ID in the value. So we write online users dot
G, what do we want to get? Write userID as key, and for adding a value in set, we use dot add method and
pass here socket dot ID. And by this line, we add socket dot ID value in set where the key is user
ID, simple as that. So here at the place of socket dot user
dot underscore ID, we pass user ID. Now in the disconnect, we want to remove
current socket ID. So online users dot gt userid
dot Dili, socket dot ID. This will remove socket dot ID from the set where
key is user ID. Now, what if our user has
no socket ID in the value? In that case, we also wants
to remove user ID as pair. By that, we can mark
our user offline. So we pass your condition
if online users dot GET userid dot size
is equal to zero. Then we simply online
users dot Dilate userID. So as you can see, we can easily add and remove
items from the set. Instead of using array
where we have to find index of that item and
then remove that item, we can easily add and
remove items from the set. Let's see it is working or not. So repress the page, page here, our token. Now in the terminal, we get map, which has one key value, user ID and socket. Now in the browser,
we open another tab. Here we open our DML file, and again, let me
paste the same token. Good, and let's
check the terminal. See here we have one user ID, and it has two values, which is our set of socket IDs. Now let's try to close
this second connection. Again, check the terminal. See that last socket ID
is removed from here, but our user is still online. Now if we also closed the
first tab, check the terminal. See, our user is
removed from the map, which means now our
user is offline. So that's how simple it is to mark users online and offline. I know this section
is little bit long, but you can see how this real time
application is working. And if you learn
this, then you can create many types of
real time applications. Now here, you can
take little break, drink some water,
stretch your body, or listen some music. Here I am also taking little break and we will
meet in the next lesson.
199. Update messages deliverd: Let's see how we can update the message status to delivered. First of all, let's figure
out the logic of it so you get the
clear understanding when you write the code. So status to delivered means message reached to
the recipient device. Now there are two situations. Imagine sender
sends the message. First, we store that message in the database with
status to send. Now, at that time, if
recipient is online, which we will know by our
online users variable. So if recipient is online, then we immediately mark
that message as delivered. This is the first situation. Now another situation is
our recipient is offline, which we will also know
by online users variable. So if recipient ID is not
available in the online users, it means he or she is offline. So if recipient is offline, initially our message
status is on send. Now whenever recipient connect to the socket, at the time, we fetch all the
undelivered messages and mark them as delivered. This is the second situation. Let's apply them one by one. Our first situation, our
recipient is online. If he or she is online, then we simply mark message as delivered when sender
sends the message. In the send message event, here in the new message, we need to check recipient of
this chat is online or not. Consors to online users dot, and here we have to pass the recipient ID. But
how can we get that? Right, we will get them
from the participants, but it is an array
with sender ID also. So from that array, we
have to filter sender ID. So here, const
recipients is equal to at dot participants
dot filter. Here, we get each
ID arrow function and we simply pass here condition ID dot two string
should not equals to user ID. Here we get array with single ID because we have only two
users in our participants. That's why here
in the He method, we pass recipients
square packet, index zero, which is the first element and
it is Mongo DibiD, we need to convert
it into string. Therefore, we add
dot two string. Now what do we want to do
if our recipient is online? Simply, we return
status to delivered. If this is not true, then in else we return sent. That's it. This is
our first situation. Now our second situation is
our recipient is offline. So when the recipient opens
app and connect with socket, then we fetch all
undelivered messages and simply mark
them as delivered. Here in socket, we listen
another event socket dot on, Mark messages as delivered. We will emit this event
from the front end when our user logged in and
connected with the socket. Now inside this, we
need to do some steps. Make sure you write these
steps in the command. Otherwise, you
will get confused. First one, we have to find all the chat messages in
which our user is available. So cost undelivered messages is equal to await message dot find. Also, we make this call
web function async. Good. As we know at
the first position, here we pass object
with condition. So first, status to send and also the sender should
not be the current user. Sender in object, we use dollar N for not equals and
pass here user ID. So we don't need all
fields from the messages. So we add here select
method, and inside it, we pass a score ID, chat ID, and sender. Now, this query will
find all the messages whose status is sent and its sender is not
our logged in user, but it will not
check our logged in user is participant
in the chat or not. Here we also need
to pass chat ID. So before this query, we need to find all the jets in which our logged in
user is available. So Const chat IDs is equal
to await chat dot find. Here, we pass Object with
participants to user ID. This will give us
chat like this. But here we only want chat IDs because that ID we will
pass in our update query. So for distracting the only IDs from the data in Mongo Di B, we have another method called
distins and in the codes, we pass fill name which
we want to distract, which is underscore ID. So previously, without
distinct method, we get data like this. Now with disting we get
our data like this. So we can simply pass these
chat IDs in our fine query. So chat ID two calibracet
dollar in to chat IDs. So now, it will find only those messages in which our locked in
user is available. Great. Now we need to
update only these messages. So here we pass
condition I undelivered messages dot length,
greater than zero. Only then we want to
update messages status. In this calibracket await
message dot update many. At first, we pass object for finding those
undelivered message. So we pass underscore
ID to object. Here, we use dollar in, and as we know here, we have to pass array of message IDs. How
can we get that? We get them from
undelivered messages, which is the array of IDs. We write undelivered
messages dot map. Here, we get each
message arrow function, and we simply return
message dot underscore ID. By this expression,
we get the array of undelivered messages IDs because map method returns an array. Also, we can directly pass
undelivered messages here. But for now, I stick with this. If you want to do
that separately, then you can also do that. Now what do you want to update? For that, we pass another Cali brackets
and pass here dollar set to object,
status to delivered. Make sure you write the correct spelling which you write in the message schema. So we successfully
update the messages. Now here comes a fun part. What do you think? What
should we do after this? So to explain this more clearly, imagine Hale and
Mike has one chat. Currently, Mike is offline
and Hali sent three messages. As we know, by default, its status will be sent. Here, our recipient
is not online. Logic in send message event
will not do anything. Status will stay as sent. Now, same as Halley, John also send two
messages to Mike. Their status will
also set as sent. Now, after some time, Mike comes online and he connect
with the socket. At the time we update
those three messages from Halley and two
messages from the John, which means for
total five messages, we will update
status as delivered. Now, what should Halley and John will get from the back end? Simply we can send Halley's three messages
ID to Halley and John's two messages ID to John and tell them messages
whose IDs are these, these messages are
updated as delivered. So our front end will update
the UI part using those IDs, which means front end can
show double check icons. Simple as that. Now
here is one thing. Imagine Halley send 100 messages and Mike is offline
for one year. And when he comes online, after update should we send all hundred messages
IDs to Halley, it may increase the data load. So what I think is instead
of passing all messages ID, we can send jet IDs which titers are updated to delivered, and that single chat ID will cover all those messages
inside that chat. So we don't need to
pass all messages ID. So here, our
conclusion is we will create an object where
property name is user ID, and as a value, we store array of chat IDs
which are updated. The reason we use here array of chat IDs because it will also
work for the group chat. For a single user, there can be multiple chat IDs whose messages
are update as delivered. Now, after getting this object, we will pick this user ID, find the socket ID from
our online users map, and simply emit event message delivered and send
those chat IDs. Currently, don't think about how front end will update
chats. It can be managed. Here our main focus is to
create this object and send event message
delivered to each sender. Rest of the work
done by front end. Our front end will update
all those messages in the chat list and messages list as well or simply
reroute the data. Let's implement this part. We will go step by step. First one is we need to
create that object who has user ID as property and
array of chat IDs as value. Const grouped chat
IDs is equal to undelivered messages dot reduce here we have two parameters, AEC, which is the accumulator, accumulator is our final result. As we get message which is
the single message object, arrow function, and at
the second argument, we can pass the default
value of this accumulator. So we pass here empty object. First of all, we will check. Message sender is already available in our
SC object or not. I AC square bracket
for accessing the key of object and pass
here message dot sender. If in accumulator, message
dot sender is not available, so we add here exclamation mark. Then inside this, we create a property by its
name accumulator, square bracket,
message dot sender is equal to empty array. After that, we can simply add
that jet ID in this array. Accumulator, square packet,
message dot sender, dot push message, dot chat ID. This message JetD is Object ID, so we have to convert
it into string. At the end, we simply
return accumulator. This ensures accumulator keeps
growing with new messages. Now in this code, there
is one little problem. If one sender has multiple
messages in the jet, then we will duplicate
these jet IDs in the array, but we don't want to send
duplicate jet IDs in the array. So at the place of this
array, we can use set. Set will not add
duplicate values. So at the place of
this empty array, we add new set, and at the place of
using dot push method, we use dot Ed method for set. The group chat IDs will
return an object with sender as property and set
the chat IDs as value. Now also at the end, we need values in array, so we have to convert
set to array. For that, we use
here for loop const, sender in group chat IDs. In that loop, we simply group chat IDs, square
bracket sender is equal to empty array and inside it
spread operator and we simply add group chat IDs
square bracket sender. Now we have our
object with user ID as property and array
of chat IDs as value. Now we have to just emit to the sender and pass
jet IDs array as data. Here we again use for loop, const sender ID, grouped jets. The sender ID is the key of
our group chat IDs object. Now in this loop,
we have to find the sender ID is online or not. So Const sockets is equal to online users dot Get sender ID. If we have sockets, which we already
know that can be array and we use
sockets dot for each. Here we get
individual socket ID, arrow function, and inside it, we simply Ioda two socketd.it
message status updated. At the second parameter, we pass data object
IDs to group chat IDs, square packet sender ID. This will send all CAT
IDs to this sender. So if you want, we can
declare it separately. So cost chat IDs is
equal to group chat IDs, square bracket, sender
ID, and at the bottom, we pass chat IDs to
chat IDs and done. Now let's is this
implementation. So the changes, open browser. In first window, I log
in with Hal's token. And at the second window, I logged in with
another account. Generate a new token
for another account. And paste it on our website. Now, let's join Room. So copy chat ID from the Mongo Dew compass and paste it and join the room
from Hi account. Now I send message. This is testing for first
delivery situation and send it. See, here we get delivered. This is our first implementation when our recipient is online. Now, let's test when our
recipient is offline. So let's close this
second window and again, send the message from Hi this is testing for second
delivery situation. See here we don't get
message delivered. If we check our database,
message collection, refresh the collection,
and at last, we get our message
status is sent, which means not delivered. Now, when our second
user come online, this message status
should update and Hardy also gets the chat IDs
whose messages are updated. Let's open SDML page
in second Window. Log in with second
account token, where is this, copy this
and add it in token. Now, the moment we hit Okay, S on the first window, we get the notification
icon on this tab, and when we move to that window, we get your send
messages are delivered. This means our implementation
is working well, and also in the console, you will get jet IDs
which are updated. Is very interesting part
of chat application. Even changing status
to scene is not that difficult as this updating
status to delivered. If you are confused, then you
can see this lesson again, understand the logic
and with that, compare logic with your code. By this way, your all
doubts will clear. Now in the next lesson, we will implement update
messages status as seen.
200. Updating message status to seen: Let's see how can we update
the message status to scene. Status seen means our
recipient seen that message. So here we also have
two situations. Let's understand
them with example. Imagine Harley has four
unseen messages from Mike. All these four messages
status is delivered. Now, the moment Halley
opens the chat of Mike at the time we update these four delivered
messages status to scene. So we have to fetch delivered
messages and update them as seen when our
user opens the chat. This is our first situation. Now imagine Harley is already available in
the chat or room, and Mike sends a message. At that time, also, we need to mark the new message Setters to scene because Hurley is already available
in the chat room. This is our second situation. Let's implement them one by one. So in the first situation, we have to update stats when our user join the
room we chat ID. So here we listen
one more event, call socket dot on, Mark messages as seen and we
pass here callback function. First of all, we will again find all messages which are
unseen by our current user. So const unseen
messages is equal to await message dot find, and here we pass comparison object first
chat ID to chat ID, and we will get chat ID from the front end
in this parameter. A let's make this
function async. Back to our comparison object, chat ID to chat ID, sender to Cully brackets, dollar NE not equals to user ID, which is the current user ID
which we are getting from the socket middleware and As
we pass status to delivered. Now here also, we want to
get only selected fills, so dot select and
ascore ID and sender. Now after this fine query, we adhere condition if unseen messages dot length
is greater than zero, only then we run update query. So in this condition, we write await message, dot update, many. At first, we need the
same condition object. So copy this object from the find method
and paste it here. Good. Now at the
second argument, we need to pass object, wet dollar set, Object,
status, to scene. Let me check we pass that
value in our schema or not. Oh, here we pass read. So stain this to scene because this is
more user friendly. Save this and back to our event. Now, again, question is same. What do we want to do after
we update messages as seen? Right, we will inform every sender whose messages
are updated to SN. So here we need only senders ID because our jet ID is fixed, which we get from the front end. Because of this, it is a lot more easier than
delivered event. So cost sender IDs
is equal to tell me which method we use for extracting the new array
from the existing array. Right, we use unseen
messages dot Map. Here we get single
message arrow function and we simply return
message dot sender. This sender is object ID, so we need to convert
it into string. Now, as we know, this
map method collects all the chat IDs from
the unseen messages, so we might get duplicate
sender IDs in this array. We can solve that using set. We wrap this map method with parentheses and
right here, new set. Will create set, and now to
convert this set into array, we simply wrap it with square brackets and
spread the set here. This map method will give us array with
duplicate sender IDs. That's why we
convert it into set. I set, we don't get
to duplicate values, and after that, we again
convert it in simple array. Now we have sender IDs, so we can simply do
for loop const sender, which is the individual
sender ID of sender IDs. Make sure here you use of not in because we use in for
getting object keys, and we use of forgetting
value from array. Previously, our group
chat IDs is object. That's why we use in. But here, the sender
IDs are array. That's why we use here off. I make this mistake before and it is really
frustrating because I don't get the error and still code is not working
the way I want it. So make sure you remember that. In this loop, we
again do cost sockets is equal to online
users dot get Sender. Here, we do same as before. If sockets are available, then sockets dot fe. Here we get single socket ID, arrow function, and inside it, we do Iot two, socket ID, dot m message scene. As the data, we send
the chat ID to chat ID, and also we send the user
ID who send those messages. Seen by two user
ID, and that's it. You can see how simple it is. This is our first situation. Now let's implement
second situation. In that, our user is already
available in that room. We need to update status when other user send new
message in that same room. So instead of writing logic in send message even we can emit the same mark messages
as seen event from the front end when user get the new message
in the joined room. So our end implementation related to status scene
is complete here. Now let's simply taste
this implementation. So in the front end, as we
join a room, after that, emit here this event, mark messages as seen and
pass here chat ID value. Also we have to emit the same event when we
get the new message. In the GET message event, we add if condition
data dot sender dot underscore ID is not equals
to user dot underscore ID. This is the user ID of
our logged in user. If this is true, then
we do socket dot, Mark messages as seen. At the second argument, we pass at d dot value. See the changes and take a look. Let's refresh this window and
pass here Harley's token. And also join the jet. And from the bottom,
we send the message. This is for tasting scene
status and send it. Now, if in our database, we check our latest message, see its status to send. And now if we open a new window, and login with my
first account token. Now, again, if we check
our message status, see it is updated to delivered. Now, I copy this chat ID, and from another user,
I join this room. Now let's again check our
database and refresh the data. See here we get message to
scene and also that user will get data with chat updated and who seen
that message is. So that's how we update
status as seen using socket.
201. Adding Group Fields in Schema: Now let's add some ones
features in our application. Currently, in our chat, we are implemented
only one to one chat. Now let's add group chat also and we don't need
to do much in it. First of all, we will add some group related fields
in our chat schema. Here in the participants, we can add multiple users
who are part of this group. After that, we also need last
message, and after that, we pass this group
and we set it to type to Bullion and
default to false. Here we can add
commands for groups. So when we implement
group features, we can remember which
fields we need to handle. Now, after that,
group has admin, so admins, which can be
one or more than one. We simply copy participants array with object
ID and ref to user. Add it here. Next, what we need? Yes, we need group name, which is type to string, and also user can add
group icon or group image, which is also type to string. I think this is enough
for group chat. Also, here, make sure
to not add required to true for group fills because
if chat is one to one, then required to true for group fills can give
us schema error. Now let's see what we need
to do in the message schema. Here for group, we don't
need to add much stuff. Just we need status
for all group members. In group message, the
status will not enough. So here we simply add
filed delivery status, which is array, and inside, we will store objects for
each participant's status. So first, user in object type mongs dot schema
dot Types dot Object ID. Ref to user. This is each
participant user ID. Also, for that user,
we add status, so copy this same object from here and paste it inside this. Now, after that, we also need time when our message
is delivered, delivered at type to date, and also we need seen at and
also type to date and done, we don't need more fields. If in future we need, then we can definitely
add them here.
202. Create a new Group API: Now let's create API for
creating a new group. Here, we don't need to
use socket for that. In the chat route, we can
simply copy this create chat API and paste
it at the bottom. Good. Now change the
endpoint to create group. And in this function, we
have to do little changes. First, at the place of receiver, we get participants array, which are the user
IDs of participants. Also, we get her group name. For group image, we can
separately add another API. And here we need to
destructure the request body. Next, also, we change
the condition. If participants
is not available, then we return error message,
participants are required. Now here we don't
need to find any chat because same participants
can have multiple groups. We simply remove this chat variable and as
this if condition. And here we add const chat. Inside the object, we add
participants to participants. But here we also need to add current user ID in the
participants array. Here we add array
and adhere user ID. Next, group name to group name. A is group to true, and we pass admins to array, and inside it, we
pass current user ID. If in future, we need to add more participants or we want
to remove participants, or we want to add new admins
or update the group name, update the group image. For all of that, we can
create separate APIs. For now, we don't
need them because our main focus is to learn
real time chat features. So let's test this API. For that, we open Postman. We need another user for
adding them in group. So open register a new user API. Here, we pass another user name. John underscore 24, email
to john at red gml.com, and password, I keep
the same one to eight. You know why Because
I forgot passwords. Good new user created. Now let's duplicate the
create chat request and rename it to create a new group and point to slash API HATS
slash Create Group. Also in the header, we already have token and
in the body in the object, first we had participants to
array here we add all users. From the database, we simply
copy other two people's ID. Make sure you don't copy the object ID of your
current account, which token you
pass in the header. Good. Now, after participants, we pass group name, and what should we
name the group? Let's say node ninjas. This is school name, right?
Yeah, and send the request. See, here I get
internal server error. Let me check what is wrong. In a terminal, we get
error in participants. Oh, here I pass directly
array inside array. So we spread the
participants array, save the changes, and let's
send the request again. See, here we create
a new group jet.
203. Adding Group Chat Logic in Socket Events: Let's apply group chat
logic in our socket events. First of all, we will start
with send message event. Imagine here, user send a
new message in a group. Now, in both cases,
if user send one to one message or user send group message
from the front end, we just need to pass content of the message and
also the chat ID. So in our group chat, also, we check content
is passed or not. Also, we find the jet by its ID. Next, we also create a new
message with same object, but in group message, we have to adhere
delivery status, select delivery status, and after that, we pass condition
if jette group is true, then we need to create array of object which looks like this. So for that, we use
delivery status is equal to jet dot participants dot Map. Here, we get user
arrow function, and inside it, we
simply return Object. Now in the object, user to user, and status to here,
we pass condition. Online users doth user. If this is true, then we
add status has delivered. As Sent. Also, we add delivered
at two here, we also need the same condition, so we can add variable
before this return. Cost online is equal to
online users dot as user. Now at the place
of this condition, we pass online and in
the delivered at also, if user is true, then we pass new date, else, we pass null. Now here is one thing.
In these participants, we also get the ID
of sender and we don't want to add sender
ID in the delivery status. Here we have recipients, which is the filter
version of participants. So at the place of
jet dot participants, we add recipients dot Map. By this, we don't need to update status explicitly at the bottom. After that, we also update
the last status in the chat. Now here we populate
Sendar data and emit get message event
for the chat ID. Now also we populate the data, but here we also
need to populate delivery status user
because in one to one, we only use our global status, but in group chat, we have
to display each user time. Here we add another
populate method. At first, delivery
status dot user, and what do you
want to populate? Write underscore
ID and user name. Now let's see what
we need to do in Mark messages as
delivered event. So as we know, making
delivery status of log in user in group message
is little confusing. So instead of using
the same event, it's better we create a separate
event for group message. So these mark messages, as delivered event is for
only one to one chat. Also, in that, we need
to do little changes. As we know, these
chat IDs are all the chat IDs in which
our user is available, but this will also
include group chats. So here in the chat find method, we pass one more condition. Group to false,
which means it will return only one to one
jet, and that's it. We don't need to change
any other thing. Now, let's create a new event. So socket dot on
event, let's say, Mark group messages
as delivered, and we pass here a
synclwcFunction. Will call this event when our user logged in at front end, same as we emit mark
messages as delivered event. Now, first of all, we need all the group jet IDs in
which our user is available. Jet IDs is equal to
await chat dot find. In comparison Object, we pass
participants to user ID. Is group to true. And here we only want IDs. So which method we
will use, right, we use dot distinct and
pass here underscore ID. Now, after that, we need to find undelivered messages
in this group IDs whose status is sent. So same as before, const
undelivered messages is equal to await
message dot find. Object, we pass
chat ID to object, dollar in to chat IDs. Also sender to object, dollar N for naught
equals to user ID, which is the logged in user ID. Now we need to also
find those messages which are not delivered to
current logged in user. So here we pass delivery status. To object, and as we know, delivery status is array of object with user and its status. We need to check user as our user ID and status
is set to send. For that, we need to
specify condition that must match within individual
elements of an array. In our case, it is
user and status. Don't get confused, see this. We can use here another
Mongoiboperator, dollar Aleem, match to object, and here
we pass user to user ID, and user status is must be sent. So by this image, we can specify condition even
in the array of objects. Also, we don't need
messages or information, so we simply addere dot select, and pass here underscore ID, jet ID, sender, and
deliver datas. Great. Now, after this, we
pass if condition undelivered messages that
length is greater than zero. Only then we want to
run update query. In this I log, we use four loop, const message of
undelivered messages. And inside this four loop, we simply weight
message dot update one. First, we pass
comparison Object, underscore ID to message
dot underscore ID, and in codes, delivery
status dot user to user ID. At the second argument, we pass Object with dollar
set to object first in codes, delivery status dot dollar
dot status, two delivered. Here, this dollar dot
status will help us to update only that fill
whose user is user ID. Now, also, we want to add
delivered at time, in codes, delivery status dollar
dot delivered at, and we pass here new date. And done. This for
loop will update status to delivered for all
these undelivered messages. Now, what do we want to do
after updating the messages? Right, we want to send the jet IDs which
status are updated. So from our previous mark
messages as delivered event, we can simply copy this whole logic after
this update method. And paste it after
the fall loop and also make sure it is
also in the I blog. First of all, in this blog, we are creating object
for senders and as value, we fetch the set of chat
IDs which are updated. The reason we use here set
is because it will remove all duplicate chat IDs and
give us unique chat IDs. After that, we convert
that to array, and at last, we run this fall loop for its
sender from group jet IDs. And in this loop, we
find socket IDs of those senders and simply emit event message
status updated, and we pass that jet IDs
to its related sender. So we don't need to change
anything in this logic. You can see how simple it is. Just we need to clear
what we want to do. Now let's move to last event, which is mark messages as seen. Here also, we create a separate event for Mark
group messages as seen. Socket dot on Mark
group messages as seen. And here we pass ASN
callback function, and in the parameter
of this function, we need Jet ID of that group. Now we copy the undelivered
messages query from the group delivered event
and paste it here. Now here we change undelivered messages
to unseen messages, chat ID, to chat ID, sender to object, dollar NI for not equals to user
ID, delivery status, image, user to user ID, and status is object, dollar, not equals to scene. Now we can pass I condition if unseen messages dot length
is greater than zero, then we want to
run update query. Again, we copy this
four loop from the delivered group event and
paste it in our seen event. Now here we change these undelivered messages
to unseen messages, and in the update dollar set
at the place of delivered, we market SN and also at
the place of delivered at, we update SN at. Make sure you don't get error in any of
these cli brackets. Be careful about that.
Now what do you want to do after we update
messages as seen? Simply, we copy the logic
from the simple scene event. And paste it after
our update query. Basically, here we face the sender sary of
these messages, and using for loop, we simply emit
message in event with chat ID and seen
by, and that's it. Now let's test this
implementation. Here, we open final SDML
file in the browser, pass token for the user. Good. Now we need
to join group chat. From the Mongo Di we compass
in the chat collection, we simply copy our group chat ID and paste it in the chat
ID, and join the room. Now let's send the
message in this group. Hello, this is group
chat and send it. Good. If we check our database,
refresh the collection, and at the bottom, C, we get message, and
in delivery status, we get empty array. Let me see what is wrong. Move to our send message event. Here in the new message, we forgot to add
delivery status. So delivery status
to delivery status, save the changes, and let's again taste
or implementation. Refresh the browser,
pass user token. Copy the chat ID from the chat collection and paste it in the chat
ID and join the room. Now let's again
send the message. Hello, this is group chat
and send this message. Now if we again
check our database, refresh the collection
at the bottom, we get new message and
in the delivery status, now we get status for
each participants. Great. Now let's add
another window open again SDMLFle here I add a token
of different account. And the moment I hit Enter, see in our first window, we get notification your
send message is delivered, which means our mark group event as delivered event is working. I refresh the database, see in the message
delivery status, that user status is delivered, and also we get here
delivered at time. Good. Now let me
join the same chat. So go with this jet ID. And paste it here
and join the room. See, in first window, we get notification your
message is seen by this user. If we again check the database, refresh the collection,
and at the bottom, see for that user, we update our status as seen and we also get
the scene at time. This means our mark
group messages as scene event is also
working properly. It is so simple
and not confusing because we separate the logic
in different group events. So that's how we implement
group chat in the socket.
204. Section 16 - Deployment Options: Welcome to the last section of the ultimate
node hair course. In this section, we will see deployment process of
node applications. So currently, our application is running on our local machine. Now to use our application globally, we need to deploy it. It is real easy thing.
Don't worry about that. So there are two ways of
deploying node applications. We can use pass, which means
platform as a service, or we can use Docker. Now we might ask what
is pass and Docker. So pass or platform
as a service means we can use a platform which help us to deploy
our application. PAS, we have Render, Eoco, Google Cloud Platform, AWS, Microsoft, Azure, et cetera. These platforms provide
variety of functionality, so we don't need to worry much
about deployment process. It's like, imagine you
want to open a food stall. Now instead of
worrying about buying a stove, setting up electricity, and getting water connections, you simply rent a fully
equipped kitchen. Here you just need to
bring your ingredients, cook your food,
and start selling. That's exactly what PAS
does for developers. With PAs, we don't have to worry about setting up servers, databases, networking,
or skiing. These platforms take
care all of that for us. We just write our note JS code, deploy it, and it
runs. Simple as that. On the other side, if you want to control over your deployment or if you want to deploy your node application
on your own web server, then Docker is a great option. With Docker, we can
create an image of our application and deploy that image to any
computer in the world. As you guess, using Docker
is a little complex process. For now, we don't need
to worry about that. So if you don't
want to worry about servers, load balancers,
infrastructure, or restarting your
application on the crash, then platform as a
service is a good option. So in this section, we will
use render for deploying our node application
because it has the easiest way of
deploying node application, and also it provides
a great features. Different people like
different platform, but I think render
is a great way of deploying and it
has also free tire. So we can deploy
our application for free and without
any card details.
205. Simplifying the Code: Before deploying
our application, let's make our index dot js
file more clean and readable. It will create good
impression on our team and also all of us like
to work on clean code. Let's make this happen. Here, we create separate
file for separate things, like for database
connection, separate file. For routes, we
have another file, and for socket, we also have
separate file like that. It is really simple.
Let's do this one by one. First of all, we separate
the database connection. Here in our project, we create a new folder
called startups. Inside this, we create a
new file called DibtJs. Now in this file, we want to add code for
database connection. Here, from the
index dot JS file, cut the Mongoose
dot Connect method. And paste it in our new five. Now here we need a
couple of things. First, we need the
mangos succed mongoose is equal to require mangos. Also, we need this logger, sconct logger is
equal to require, we go one folder up, go to config, here
we import Logger. Now, one question you might ask, how can we add this code
in our index dot JS file? It is really simple. Don't get
confused. Let me show you. Here at the bottom, we simply module dot
exports is equal to. Here we add function
inside this function, we simply move this Mongoose
dot Connect method. Back to index dot JS file. Tell me how can we input something from the
db dot js file? We use required forward
slash startups DV. This expression, return
whatever we export from this DB file and what
we export function. We can store that in
variable called const DB, and then we can
call this function. Instead of doing
this in two lines, we can simply do
that in one line. Aropris of storing that
function in variable DB, we can simply call it here. When we call that function, connection code will run,
and that's what we want. So here our first step is done. Now let's separate all these
routes implementation. In the startup folder,
we create a new file called routes dot js. Also, same as before, we export module dot exports is equal to function
inside this function, we add all our routes. So cut these all apps routes
with app middleware as well course express dot Json and
also Global error middleware. Them and paste them
in our routes file. Now here we need a
couple of things. First of all, we need this app and how can
we get this app? Should we create a new app in
this file and then use it? No, we can't do that because that will create a new
Express application. But here we want to use
our same application. What is the solution here? Solution is really simple. Here, from the index js
file, after this DB, we had required period, slash startup, slash routes,
which is our function. We call this function and
simply pass app as argument. In our routes function, we simply get app as parameter. Next, what we want? We need course and how can we get yes, we can use the same way. But as we know, course
is our package, and we can use package in
any file by simply input. With app, we can't do that, only that's why we
get app as parameter. So at the top, Cost course is
equal to require Cos. Also, we need express, which
we can also import here. Cost Express is equal
to require express. Now after that, we also need
logger, sconct, logger, is equal to require, we go one fuler up,
config, and logger. Now, next, we need these routes, SeconstUser routes,
is equal to require. Here, we also move one
folder up routes users. Now duplicate this line two more times using alter plus sift, plus down arrow or option
dot Sift plus down arrow. Here first, we change
the variable name to post routes and file to post. Next, we change the
variable to chat routes and file to chats and that's it.
We're done with Step two. Now let's separate
our socket logic. From this online users variable, we select our code to till
we get server dot LISN. Cut this the startup folder, we create a new file
called socket dot js. In this file, we again module dot exports is
equal to function. In this function,
we add our code. Now, first of all, here
we need this AO method. Tell me, should we
create a new Ao object? We need to use that
same Ao object which we created using server. In index dot js file, we had required period
startups socket. And here we call this function and simply
pass here Ao Object. In socket dot js file, here we get the Io object a
parameter. Simple as that. Now in this logic, we have
to import a lot of things, so don't try to hurry. Let's go line by line. See, in this ous, we need this JWT. At the top const, JWT is equal to require JSON web token.
Next, what we need. Yes, we need this chat model, and also we need
this message model. So at the top, cost
chat is equal to require here we go one
folder b models chats. Also, if you get confused
in importing file, then let me show you my trig
when I'm importing file. Here we want to
import message model. Cost message is equal to
require, first of all, I collapse all the folders
using this button, and then I simply open the
folder in which I am working. Is startup, and also open folder from which
I want to import. That is models. Currently,
we are in the Stu folder. We need to go outside
of that folder, so we add period,
period forward slash. Then we want to go
in Models folder, and we import from
messages model. See, it is that simple. Now let me see what
else we need to import. I think that's all we need. If we miss something,
then we get error. Don't worry about
that. Now let's see how our index Dsi looks. See, it is clear now, but let's make it more clear. Here we have a lot of unused
inputs. We can remove them. First, we remove
code input line, so press Control plus X or Command plus X to
remove that whole line. So we remove Mongos and remove all carried out imports
which all are not used. See, now our code
looks more clean, easy to scale, and
we all love to work with this type of
clean applications. This is how professional developers code
should look like.
206. Preparing Node app for Production: Now before we start the
deployment process, it's better we prepare our node application
for production. So for that, we need some packages like
first one is helmet. We already use that in
our previous project. Basically, helmet is used for security of our application. It is an excellent
option to enhance the security of our app
with minimal configuration. Also, it protects our app from common web
vulnerabilities setting proper STDP headers
automatically. In our terminal NPM install helmet and if you want to
use the same erson as mine, then you can use at the
rate 8.1 0.0 and hit Enter. Now another package
is compression. This is also very useful
package for node applications. As we know, sometimes we have to send big size data
to many users. For example, list of post
or list of messages. This package will compress that big size data and reduce
load of sending big data. So NPM install Compresson at
the d 1.8 0.0 and hit Enter. Good. Now, let's implement these packages in
our application. Don't worry, it is just
two lines of core. So here in the starter folder, we create a new file
called prod dot js. In this file, we import
these two packages. So const, helmet is equal to
require helmet and const, compression is equal
to require compreson. Same as before, we export
function from this module. So module dot exports is equal to here we
export function. Here we get app as parameter, and inside it, we want to
implement this middle was. App dot U helmet which is
the function, we call it. By this only helmet is
applied in our application. Also we add app.us compression, and we also call it and done. By these two lines of code, we apply these packages
in our application. Also, you can add this
code in route dot js file. There is nothing
wrong about that. Now here we just need to import this module in our main
index dot js file. Here before these routes, we simply require period databsPd which will
return that function. So we have to call
that function and simply pass your
app as argument. Make sure it is added
before this route imports. So our helmet and compression apply for all routes,
and that's it. By using those two lines, we can make our
application little better.
207. Overview of Deployment Process: Let's see the overview of the deployment process so
we don't get confused. Currently, our code is
available in our local machine, or we can say in our computer. Now to make our application
live on the Internet, we use render platform. First of all, we will
upload our code on Github, and then we connect our Github repository
with our service, which is render. Don't
worry about that. It is really simple.
I will explain all steps in simple
and easy way. Also, we will see how to update our code after we
deploy our application. Let's start with uploading
node application on Github.
208. Uploading Node application on Github: Let's see how we can upload
our project on Github. If you don't know
Github, in short, it is a website that allows
developers to store, share and collaborate on
code with other developers. Also, Github allows developers to create repository or we can call repos where they can store their code and track
changes over time. This is the best and easy
way for teams to work together on a same project without overwriting
each other's code. So there are many ways to
upload our code on Github, but we will see the easiest
and the simple way, which is by using Github
desktop application. Head over to Browser and search Github Dektop application
and open this first link. Now click on the
Download button. This will take some time. And after completing
this download, open the setup, and our installation process
is started. Good. Now, if you open this
application first time, then you have to login
with your Github account. So to show you this, I remove my Github account from
Gitub desktop application. Now to login, go
to file and open Options and click on this
sign in forgitub.com. Continue with browser. This will redirect us on
GitubOficial website, fill your user name and password of your Gitub account
and click on sign in. I have to verify my
account and done. And now click on this Open
Github desktop application. It will automatically redirect
us on our application. Don't worry, you need to only
set up for the first time. Now let's verify we
logged in or not. So again, go to
file and Options, and in the accounts, we can
see we have our accounts. Go to the Git option,
and from here, we can set our name and
email for our Github. So when we push
code on the Github, other team members will
see this name and email. Also make sure you select your official email
here and click on Save. Now, before we move
forward, as we know, in our project, we have
node modules folder. We don't want to post this on
Github because at any time, we can generate
node Module folder using NPM install command. So it is useless, we upload
whole node modules folder, which has a lot of files. So we have to ignore that folder from getting upload on Github. For that, in our
project at the root, we create a new file
called dot git ignore. Make sure you don't make
any typo in this file name. It should be dot gitignore. Now in this file, we
can add which files and folders we want to
ignore or in simple words, which files and folder, we don't want to
upload on Github. Suppose we want to ignore
this node modules folder, so we write node
underscore modules, which is the folder
name and forward slash, which indicates this is folder. Similarly, we want to
ignore logs folder. So what we write? Write Logs forward slash.
Now here is one thing. When we upload our code
on Github, as we know, this code will become public and everyone can
see our project. Now suppose we upload
our project and this donVfle also get
uploaded on Github. By that, our all
secret information will become public and
anyone can misuse it. So we have to also add this
dot NV file and gitignore. Let me show you short cud for
getting Gate Ignore file. Simply go to your browser and
search, Get Ignore, Github. Open this first link, and here we get all templates for different types
of application. Have Android Git Ignore. Also, you have C plus plus, C, and many more files. Now search at the top, node gitignore and
open this file. See, here we get the content
of the GetI Nur file. Simply copy this code and add that code in
our GetI Nur file. Save the changes, and we
have our GetI Nur file. Cool. Now let's finally
upload our code on Github. For adding our code
to repository, go to File and select
Add Local repository. And here we select our path
of our inkifi application. Now here it says we have to
create a new repository, so click on that link. And here we have to pass
our repository name. Also, we can write
here description. This is the social
media application and click on Create repository. Now let's verified we
had right path or not. So click on show in Explorer and see here we
get our Linky five folder, so close it, and simply click
on Published repository. Here we can change the
repository name and description, and also we can select
the privacy of code. Also, you can make it private, but I want to give
you this code. That's why I make it public
and click on Publish. It will take some time and done. Let's see it on Github, so click on view on Github and see here we
get our application. Lovely. You can see how simple it is to upload
code on Github. Now in the next lesson, we will set up our render account.
209. Deploying Node App on Render: Let's deploy our backend
application on Render. So head over to render.com, and first of all, we will
register our account here. We can use Google or Github for registration or we can simply
use email and password, and we got this dashboard.
Don't worry about that. Simply click on New button, and here we select web service. Now here we need to connect
our Github account. So Glicon connect Github and login with your
Github account. Make sure you use the same Github account in which we publish
our backend code. From this page, we can select which repository we want to
add in our render account. You can also add all repository, but in my suggestion, select only selected repository option. From here, we can
select repository, so we can select our
inkifi application. Now, this will redirect
us to the dashboard home. See, now we get here
our repository. Simply, click on
this repository, and here we get our form. Now, first of all, here we
add our application name, which is our Linkifi. We will get this name as our
base URL of our application. Next, we have environment which is set to node.
Don't change that. Next, we can select branch of our Github repository
which is main or master. Also, we can select region. Now for root directory, we add period, and
for build command, we write NPM install. For start command, we simply
add node index dot js. Basically, by this command, render will run our application. From bottom, we select our service type which
we select for free. Now click on this Advanced
drop down and select Add Secret File and give it a name dot and V. Now back
to our wagon project, and in that we open DotynVFle in which we have
our secret variables. Simply copy all code
and in Render website, click on Contents and
paste our code here. Now just click on
Create web service and see our deployment
process is started. It will take some time
like five to 10 minutes. C, build successful. Now it is deploying
and after that, I get error in one goody B
connection. Let me check this. It is error related
to DB variable. I think I made mistake
in adding secret file. So here we go to the
environment tab, and here, let me check the
file contents. It is good. Oh, here I enter
wrong file name. It should be dotnV. I intentionally create
this error because I want to show you how to
check environment for a bus. S the changes, and
back to Logstb. Here, it will again start
the deployment process. And see here we again get
error in database connection. Why? So currently, our
database is local database, which means it is working
in our local machine. Now when we deploy
node application, we can't access local database. So at that time, we have
to add Cloud database, and we will do that
in the next lesson.
210. Adding MongoDB Cloud: Currently in our back end, we have local Mongo
Debe database, so we have to create our Mongo Dibe database
in the Cloud. By that, all users will
use the same database. So head over to mongotib.com and simply sign up
with your account. It will take only 1
minute to set up. I already sign up, so here I try to log
in with my account. Thank God, it's open. Now at the top left corner, here we get new Project button. Click on that and here we
write our project name, which is our Linky fi
and click on next. Now from here, we can add
team members to our project. Just click on Create Project. Now click on Create button. Here, we select plan, simply go to free version and
click on Create Deployment. And see here we get our username and password
for our database. So I copy first username and
in note paid, paste it here. After that, also copy this random password
and paste it also. This is the most important step. We can save this user name and password as backup
of our database. Now click on Create database. Next, we click on, choose a connection method. Select here Compass. See, here we get Mangaib
Connection URL for Cloud. Don't worry, just simply
copy this database link and in our ENV file at the place of this
local Mangaibi ink, we paste our MongoibCloud link. Also, here I forgot to add a database name at
the end of this URL. So make sure you
add it like this. Okay? Now let's continue. We have to give access
to network which can read and write
data in our database. From anywhere, user can access our database and
get post from it. In the left side,
go to Network Xs. Here we have our
current address. Click on Edit and simply click on Allow
access from anywhere. You set our address to 0.0.0.0, which is the aces for every
one and click on Confirm. This will take little time
and see it is active. Good. Also, if you want to check the database access related setting like editing
password or something, then you can do that
from this step. Now, let's verify it
is connected or not. So open up terminal
and write node, index dot js, and hit Enter. This will take some
time and we get go to be connected
successfully, so it is working. Now we have to update
our deployed code. How can we do that? Simply, we just need
to push our code to Github and render will automatically detect
those changes, and that's why we add
no project to Github. Now here is one thing.
We know we ignore this Dotty and V file from
uploading on the Github. For updating the environment
variable values, we need to go to
the render website. Here we select
environment variables. Click on edit and
simply from here, we can edit our values. I replace database URL to Cloud. Here also, you have to add your database name at
the end of this URL. Now, click on, save, rebuild, and deploy. Good. If we check our logs, after some time, we will
get again deploying log. And see here we get Mongo to
B connected successfully. Lovely. Now, let's taste our application is
actually working or not. So here we copy Cspace URL
of our deployed application. Now open postman, and in this, we duplicate this create new user API because we just
create the fresh database, so we don't have our
previous data in it. Now let's change the
local host URL with our deployed application
URL and send the request. See, here we get JWT Token, which means our API is
working. Let's verify that. On Mongo Deb website, go to our Linkify app here we get Browse Colection and see, here we get all our collections, and I we check user collection. See, here we get the new user. Lovely. But wait, while I'm getting here
taste database name. I think I forgot to add database name in
the collection URL. Go to environment
variables, click on Edit. And yes, after this URL, I add forward slas and
here I add database name. Let's say, our Linky fi and simply click on Save,
rebuild, and deploy. It will rebuild our application, and at the end, we connect
with our database. Let me again taste the API, send the new user request, and here I get new token. Good. Now go to Mongoib website
and refresh the database. See, here I get our
inkifi database. Great. Now let me also show you how can we update our
code and redeploy it. So in our project,
what we can change? Let's this Console message, server is running on portar PT. This is just for demo, save the changes and to
update the code on render. We have to just push
our code on Github, render, will automatically fetch our updates and redeploy it. So back to our Github
desktop application, and here we pass Commit message. Let's say, update
Console message and click on Commit
one file to main, and at the end,
simply push origin. Now in our render website, we go to the deploy section, and here after one to 2 minutes, we can see it is
deploying and at the end, we get server is running
on port Our port, which means our code
updated successfully. You can see deployment process
is really simple and easy. Just you need to
upload your code in Github repository and
then by using render, we will deploy our
application quickly. And if we want to
update our application, then we just need to
push the changes on the Github and in the
two to 3 minutes, our server will restart
and we get our updates.
211. What is MVC Architecture? [BONUS]: Now if you are
working as developer, then you have
definitely hear about MVC architecture
in big projects. So let's see what is MVC
architecture and why we need it. So MVC stands for
model view controller. Basically, it is a way of organizing our
application core. So in the future, it will
a lot easier to manage, maintain, and scale
the application. In simple words, it is a folder structure which separates different parts
of our application. In MVC architecture, we create
three separate folders. One for model, second for view, and third for controller. Don't get confused.
It is really simple. So model folder is
where our data lives. Here, we have to
define how data should be structure and how to
interact with your database. For example, in
Node application, we already created
models folder, and in that we store
all the models and schemas for each
Mongo be collection, and that's why we name
this folder as models. Next, we have view folder. View means what the user sees. It is the UI part
of our application. For example, in the
Node application, we might have template
engines like EJS or PUG or we have
SDML or CSS files, which we want to display. So if we have front
end related code, then we can store that
code in the view folder. Now at last, we have
controller folder. In Controller folder, we will store the logic of
our application. For example, if we
open our user's route, here we can see this is
ASN callback function, which runs when someone send post request on this endpoint. This is the logic part.
In Controller's folder, we will store the ASN
functions separately, and then we will just import
it here in the routes. Don't worry, we have to simply cut and paste
these functions. We will see that
in just a minute. Here we understand
MVC model for data, view for UI related things, and controller for the
application logic. But you might ask, what is the point of this
MVC architecture? Why big companies like it? So as we know in big companies, there are big teams are
working on one application. If we create separate folder
for each part like model, view, and controller, then it will create separation
of concerns. Here by separation, our
code can easier to manage. Suppose one team works on
the post related features, second team works on
payment related features, and another team works on
the user related features. Each team did not need to touch post model file or payment
model file or user model file. They will work on their
separate features, and that's why when they
push code on Github, it will not come up
with big conflicts. Also, another benefit of MVC
is it is easy to maintain. We want to update model, then we don't need to
update view files. Another benefit is it is
much easier to scale. As our app grows, it is easier to add new features because everything is already well organized, and this is why big
companies and nowadays freelancers also low
this MVC architecture. Now here is the
folder structure for the MVC architecture in Node JS. First one is our
main project folder. After that, we add
index dot JS file, which is our main file. Also, we add other
files or folders like package dot sn file or dot ENV
file or middleware folder, you stay here in
the main folder. After that, we add models
folder in all models we store. Next, folder for all
the UI related files, and then Controllers folder
for all the logic part, and in Node JS, we
have routes folder. So we will add all
routes in this folder. Now, this folder structure is good for small or
medium sized projects, but big companies
like Paypal and Netflix use little
different folder structure. It's definitely on you and your company which folder
structure you select. I will show you both options. This another option is
a little different and it is good for Big
Bend applications. Here we are main project folder. In that we had
index dot JS file, which is the main entry
point of our application. Also, we at ENV files, package dot JCNFle Docker files, and UploadedFles, and
folders we store here. After that, here we add
another folder called source or SRC here we can
store our middleware folder, convey folder, and Utils
folder, et cetera. Also, in the SRC folder, we store modules folder
for each module, and in this folder, we can add folders for each
feature like user folder, post folder, et cetera. In this each folder, we can define three files, user dot model dot gs
user dot routes dot js, and user dot controller dot js. So we can add one more file
user dot service dot js. This file is not compulsory, but some companies
like to use that. In this service file, we can store database query. It is not compulsory to add this user dot
service dot js file, but some companies
like to add it. So like this, we can add four
files for post folder also. When we want to add new
feature like product, we create folder product
here in the modules folder, and then we can add four
files in the product folder. As we can see, this
is much complex, but it is really good for big
and complex applications. You can use whatever
you want to. It's totally up to you. Folder structure can be a little different for
company to company. Don't worry about that.
Now in the next lesson, we will apply MVC architecture
in our application, and then we deploy
those changes.
212. Apply MVC Architecture [BONUS]: Now, let's apply
MVC architecture in our Linky Fi project. So currently in our application, we have very
organized structure, like we already have models
folder and Routes folder. Here, we don't need view folder because there
is no template engine code. So we need to do just little bit for manage it. So let's do this. First of all, here we
create folder called SRC. N S like this config folder, hold Control or Command key, select it till Stu folders. Make sure you don't move
these Node modules folder. Good. Now let's see what we have to change in
the index JS file. Mostly, we have to change
the input file parts. See, here we have
these startu paths. We have to update it. Here we create multiple cursor by holding alter or option. And add here SRC, to make sure we are
getting this file or not, let me check by removing
this file name. See here we are getting
file suggestions, which means it is right. Now we don't need to change anything in the
index to JS file. Now in the SRC folder, we already have models
and Routes folder. But as we know, we apply here. Complex Node
application structure. So here, we create a new
folder called modules. In this modules folder, we have to mainly create four folders because here
we have four models. Let's create first
folder called Jet, then another folder
called message. Then another folder for post
and last folder for user. Now remember in each folder, we have to create three
files. So let's do that. First, we move user JS file from the models folder and
did in the user folder. Here we rename its file name
to user dot model dot js. Here, it may ask
for update inputs. Make sure you click on, yes. And if you are not
getting that menu, then you have to do it manually. Now let's move users
dot js Route file from the routes folder and d it
in the user Module folder. Now it is asking for update. Tell here to Update user
dot js Route filename. Now let's rename this file
to user dot routes dot js. See, now it is not
asking for update. Don't worry, we will
update that manually. Now in the user module folder, we create one more file called user dot controller dot js. Now you might ask what we
will add in the controller. Controller is where we store the logic of
our application. It is a collection of callback functions of our
APIs. Let me show you that. Here in the user's route, we have first register API, and this is its
callback function. This is the controller
for this route. Cut this callback function from here and in the controller file, we have to export this function. So Cast register user is equal to and past this
callback function here. Now to export this function, we add module exports is equal to object
because from here, we want to export
multiple functions. So here we can add registered
user to register user, or we can remove this. Save this file, and now we have to add this function
in the user's route. So at the top, we add cost
user controller is equal to require here we import from dot forward
slash user dot Controller. Now at the place of
API CallwayFunction, we simply add user controller,
dot register user. Make sure you don't call
this function here, we have to add
function reference. Now let's do the same
for other APIs also. So cut this login
API controller. In our file, we define
a new function, cost login user is equal
to paste callback here. Next, we got this API
callback function. And in our controller file, we create a new function. Cost, get user is equal
to paste it here. Same as this, we have to cut each callback function and separate it in the
controller file. I know this is a
little worrying, so put some music and we
can complete this together. Cut the callback function, and in the file, Cost, request, reset password is
equal to paste it here. Cut the next callback function, and in the file,
Cast reset password is equal to paste it here. Cut the next callback
function for follow the user, and in the file, Cast, follow user is equal
to paste it here. Now cut the next callback
function, for reject, request. In the file, Cast, reject, follow, request, is
equal to paste it here. Now got the next
ColbkFunction for accept request, and in the file, cost except, follow request
is equal to past it here. Cut the next callback function, for follower list, and
in the file, const, get other user, follow, list, is equal to paste it here. Don't worry, we
have only few left. Cut the next callback
function, for following list. And in the file, const, get other user, following list
is equal to paste it here. Cut the next callback function. And in the file, we add cost and follow user is
equal to paste it here. Also, back to route file, we need this general Cs function for register and login API. So we also cut this whole function and paste it in the
controller's file. Here you might have updated generate tokens function for excess token and
refers token both. Here I have my old code because those are updates after
I publish the course, but don't worry, it
is the same process, cut that whole function and
dt in the controller's file. Also, if you have refresh
route and logout route, then do the same for
those APIs also. Here, we just need to export these functions and then add
them in the Routes file. So after register user, we add login user, get user request,
password reset, reset password, follow user, reject, follow request,
accept, follow, request, get other user, follow list, get other user, following list, and
last unfollow user. Save this file, and
in our route file, we addeeUuser controller
dot login user. I'm not destructuring
the functions here because if some new
developers view our code, then he or she can get confused. Now, user controller
dot god user, user Controller, dot
request password reset, user controller dot
reset password, user Controller dot
Followser user Controller, dot reject, follow, request, user Controller, dot
accept, follow request. User Controller, dot
get user, follow list. Next, user Controller,
dot get other user, following list, and last, user Controller,
dot unfollow user. Now we have to import necessary things for
these controllers. At the top, we can see all unused imports
which grade out. So we can collect them
together by moving the sauce down and simply cut those imports and paste them at the very top of
the controller file. Good. Now here, we have to
update the user model path, so we change it to period slash user dot model because
it is in the current folder. Also, we can make sure
these paths are okay. Yes, these are okay.
Save this file. And if we check our route file, see this looks very clean. By this way, we can
very clearly see API endpoints with API methods. Now we have to do
the same for jets and post also. Let's
quickly do that. First, we move jet model in
the jet Module folder and rename the file name to
chat dot model dot js. Let's move chat route in the chat modules
folder, update input. Now rename the filename
chat dot Rous dot js. Now we have to
create new file for chat dot controller dot js. Back to Routes file. First, we cut this
first API callback and in the controller file, we create a new function. Cost get chats, is
equal to paste it here. After that, we cut the next callback
function, and in the file, Cast get chat messages is
equal to paste it here. Next, cut the next
callback function, and in the file, cost, create chat is equal
to paste it here. Next here, we don't need the send message API because that we have
handle with Socket. We can remove this good now got the next callback
function and in the file, cost create group is
equal to paste it here. Now we have to export
these functions from here. At the end, module exports
is equal to Object, Gchat to GchatsO we can
remove the getchat messages, create chat, and create group. Save this and in the route
file here, we input cost, chat controller is
equal to require here, parlas chat dot controller. Now in our APIs, we will add those functions. So chat controller, dot
GAD jets, chat controller, dot get messages,
chat controller, dot create chat, and last chat controller
dot Create Group. Now we cut these model imports
from here, save this file, and in our controller file at the very top, we paste. Good. Now here, we have to also
update these two models path. So we change the
chat model path to period forward slash
chat dot model, and for message A, we will add message model in the
message Module folder. So we go one folder up message
slash message dot model. Save this file and
we have to move message model in the
message Module folder. Now change the file name to
message dot model dot js. Now we have to just do this process for our
last post module. First, we move post Model in the post Module
folder and rename the file name to
post model dot js. Now let's move post route
in post Module folder, update input, rename
the filename, post dot routes dot js. Good. Now we have to create new file for post dot
controllers dot js. Back to Routes file. First, we cut the
first API callback and in the controller's file, we create a new function. Cast create post is
equal to paste it here. After that, we cut next to callback function and
in the file Cast, get my post is equal
to paste it here. Next, cut the next
callback function. And in the file, Cs get following post is
equal to posted here. Next, we cut another
API callback function. And in the file, const, delete post is equal to, and paste it here. Don't furry only last
few functions to go. Cut the next Colbeg function for like and dislike the post. And in the file, const, like, unlike post is equal
to, paste it here. Cut the next Colbeg function. And in the file, we add cost, add comment is equal
to paste it here. Cut the next callback function. And again, in the
file, we add cost, add comment, reply is
equal to paste it here. And now for the last
API callback function, cut it and in the
controller file, we add cost, delete comment, is equal to paste it here. Now we have to export
these functions. Module dot exports is
equal to in object, create post to create
post, get my post. A following post, delete post, like like post, add comment, add comment reply,
and delete command. Save this and in the post
routes file at the top, we add cost post
controller is equal to require period forward
slash post dot controller. Now in our API, we can add those controllers, copy post Controller and
write, postcontroller, dot create post,
postcontroller, dot GET MyPost, post Controller, dot
GET following Post, post Controller,
dot delete Post. Post controller, dot, unlike
post, post controller, dot add command,
post controller, dot add comment reply, and post controller
dot delayed comment. Good. Now let's cut the unused
imports from Routes file. Save this file and in the controller's file
at the very top, we paste it here. Now here, we have to update
these two paths, period, forward slash post dot model, and for user path, we go one folder up, user folder, shuser dot model. Save this as we can see, our files look more organized. So here our models and
routes folder are empty now, so we can simply delete them, and I think we have to update
path at one more place, which is routes dot Jsle
in the startup folder. See here path didn't get
updated automatically. So here we simply change
the path to modules, user, slash user dot Routes. Modules post post dot routes and modules JET Jett
routes, and done. Great. Now, let's quickly
push this code to Github. And by that, our code can
automatically get deploy. So open Github
desktop application. And here we get all changes. Write the commit message. And commit it. And at last, we simply put this code
on Github, and that's it. This will automatically
get deploy. So this is how in
professional world, big companies manage
their no JS code. Also, there may be a
little different approach depends on companies
or team you work with, but this is the most
common folder structure which professional
developers use nowadays.