Transcripts
1. Module System Essentials: An Overview: Hi, and welcome to the
Express Chairs course. Module two, nod Module system. My name is Shevin agonci. I'm a full sech developer
and instructor with years of experience building scalable
APIs and real time systems. I'm super excited to be your
instructor for this course. If you have ever
wondered how Nojs handles events so efficiently,
this class is for you. So this class is a continuation of the ExpressJcurs series, and you may also view
its previous class titled The ExpressJScurse, Module one, getting
started with NodeJS. In this section, we are going to look at the module
system in Node. You'll learn what modules are, why we need them,
and how they work. Throughout this section, we will explore several modules
built into the core of node, such as operating system, file system, events, and GTP. You will also learn how to create your own
modules and finally, apply these skills to create a live GTP server that handles real
world networking task. Who is this class for?
This class is designed for bigner and intermediate
NerdJs developers. If you already know JavaScript and have Nerd Js installed, you are all set to get started. You're not just
learning concepts, you're building
practical skills. By the end of this class, you will have the tools to
create reusable modules, understand advance
event handling, and confidently handle
NRGS networking. These are game changing skills
for backend developers. And finally, our hands on
project includes creating a fully functional
event emitter with advanced features like priority listeners
and error handling. And then you will
integrate it into a NRGs GDP server handling dynamic
G and post requests. I can't wait to see what you
build with these skills. Let's just start it, and I will see you in
the first lecture.
2. Inside Node.js: The Global Object: In the last section, we
use this console that log function to log
something on the console. Now, this console object is
what we call a global object. That means it's part
of the global scope, which we can access
anywhere in any file. We have a bunch
of other objects, for instance, that are also
globally available in node. For example, we
have set timeout, which we have
probably seen before. We use this to call a function after a delay like 1 second, 2 seconds, and so on. So this is just a part of
the standard JavaScript. We can use this on the client. We can use this inside of
a browser or even in node. We also have clear timeout. Similarly, we have set interval, which we repeatedly call a
function after a given delay. We also have clear interval, which we use to stop a function from being called repeatedly. These are the global
objects in JavaScript. Now in node, we have a couple of other global objects that you're going to learn about
later in this lecture. In browsers, we have this Window object that
represents our global scope. It means all the variables
and functions that are defined globally can be accessed
via this Window object. We can call window
dot console dot log or simply just console dot log. The Java Strip engine will
prefix this statement with Window because that's where
this object is defined. Similarly, all these functions that you see here belongs
to the Window object. So we can call window dot
setTimeout or call it directly. By the same token, if
we declare a variable, let's say message, that variable is also available
via the Window object. All right. However,
in the last section, I told you that in node, we don't have the Window object. Instead, we have another
object called Global. So all these functions and objects that we have here can be accessed
via the global object. It means we can do global
dot console dot log or global Set Time
out and so on. Of course, it's easier to use a shorthand instead of
prefixing them with global. But one thing you need to
know about node is that these variables that we define here are not added to
the global object. In other words, if we do a console dot log of
global dot message, we are going to see undefined on the console. Let me show you. Now open the shell or terminal
and run node app dot js. See, we get undefined. As you can see, the variables and functions that
we define here, they are not added to
the global object. They are only scoped
to this file, app Js, and this is because of
the node module system that you're going to learn
about in the next lecture.
3. Modularization in Node.js: A Modular Approach: The last section, you
have learned that in the lien side JavaScript that
we run inside of a browser, when you declare a
variable or a function, it is added to the global scope. For example, when we define
a function like say hello, that function is added
to the global scope, and it's available via
the window object. There is a problem
with this behavior. In a real world application, we often split our Java strip
code into multiple files. So it is possible that
we have two files, and in both files, we define this function, say hello with the exact name. Because this function is
added to the global scope, when we define this
function in another file, that new definition is going to override the
previous definition, and that's a problem
with global scope. In order to build maintainable
and reliable applications, we should avoid defining variables and functions
in the global scope. Instead, we need modularity. We need to create small
building blocks or modules where we define our
variables and functions. That means if you
define a variable or a function with the exact name in different files or modules, they are not going to override
each other because they are encapsulated inside of
their respective modules. Now, at the core of the node, we have this concept
called module. So every file in a node application is
considered as a module. The variables and functions
that we define in that file or that module are scope to that
particular file. In object oriented
programming terms, we say they are private. They are not available outside the container or
outside that module. If you want to use a variable or function in a module
outside that module, you need to explicitly export
it and make it public. And we are going to look at
that in the next lecture. What I want you to take away
from this lecture is that every node application has at least one file or one module, which we call the main module. So in this case, this app
Js is our main module. Now, let me show
you this module. I'm going to delete
all this code here and do a Console dot log of module. This module object here
may appear to be global and you may think we
can access it via global object like
global dot console. But this is not a global object. It appears to be global, but it's not global and you
will find out why very soon. Let's see the log in Console. Open your terminal again, node app dot JS. As you can see, we
have an object module. It's Adjacent object with
these key value pairs. So we have ID. Every module has an ID or unique identifier. We have exports, path, file name, loaded,
churn, and PATs. For now, don't worry
about these properties. As we go through this section, you will gradually become
familiar with these properties. So in node, every
file is a module and the variables and
functions defined in that file is scoped
to that module. They are not available
outside of that module. In the next lecture,
we're going to learn how to create
and load a module.
4. Crafting a Custom Node.js Module: All right. Now let's add a new module to
this application. So I'm going to create a
new file, logger dot js. Let's assume we are
going to create a new module for
logging messages, and we're going to use this
module in various parts of this application or potentially
in other applications. So logger dot js. Now in this module, let's assume that we are
going to use one of those remote logging services
for logging our messages. So there are websites out there which provides
logging as a service. They give us a URL,
and we can send an TDP request to that URL to
log messages in the cloud. So here, I'm going to
declare a variable like URL and set it to
something like this. HTTP suo oger dot slash Log. And of course, I'm making this up it may not be a true
service out there, but let's imagine in
its implementation, we are going to send an HTTP
request to this endpoint. Now, we also need a function called Milob that takes message. And in this function, we are going to send an HTTP request. However, to keep things simple, we just want to
focus on modularity. We don't want to
get distracted from all the details of
sending HGTV request. So for now, I just want to log this message on the console, so console dot log
message, okay? Now this variable
and this function, Milog are both scoped to this
module. They are private. They are not visible
from the outside. However, in app Js, which is our main module, we want to use this
logger module. So we should be able to
access this Milog function. We should be able to call
it from the app module. So we need to make this public. We need to make it
visible from the outside. Now, in the last lecture, you saw this module object. One of the properties
here is the export. You can see this property
is set to an empty object. Anything that we add to this
object will be exported from this module and will be available
outside of this module. So back in our logger module, I'm going to set module
dot exports dot log. So I'm adding a
method for rock to this exports object and simply setting it to this
MLOpFunction, defined here. Okay? In other words, the object that we are exporting here has a single method, log. Similarly, if you want
to export this URL, we could do something like this module dot exports dot URL. We set this to URL. And of course, we can change the name that is
exported to the outside. For example, internally,
we call this variable URL, but when we export it, we may call it endpoint. Okay? Now, in this case, we don't need to export this URL variable because this is purely
implementation detail. So in real world applications, every module might have several
variables and functions. We only want to export a
subset of these members to the outside because we want to keep this module easy to use. Let me give you a metaphor.
Think of a laptop. A laptop has a screen and
a keypad on the outside, and these are the buttons or objects that we interact with. So these objects represent the public interface
of a laptop, right? But inside the laptop, there are a lot
of other objects. We don't need to know
anything about those objects. They are implementation
detail and they can significantly change from
one model to another. But what we see
on the outside is almost stable or static
across different models. So in our logger module, this URL is
implementation detail. Other modules don't need
to know anything about it. They only need to call
the log function. So we export this and make it public and keep the URL private. So I'm going to delete
this last line, okay? So we are done with
our logger module. Now we need to load this module and use it inside app dot JS.
5. Loading Modules in Node.js: To load a module, we use
the required function. This is one of the
functions in node. We don't have this in browsers. This function takes
one argument, and that's a name or path of the target module
we want to load. So here we want to load
the logger module. Now you can see both the app
module and logger module are in the same folder. So we use period slash to
indicate the current folder, and then we add the name of our module that
is logger dot js. Or we can make it shorter
and just use logger because node assumes
this is a Ja Scrap file, and it automatically
adds the Js extension. Now, if this logger
was in a sub folder, we could add that
subfolder here, or if it was in
the parent folder, we could use dot dot slash. So here we are using the relative path to
the target module. In this case, that module
is in the same folder. Now, this required
function returns the object that is exported
from this target module. So this exports object here, this is what we get when we call the required function.
Let me show you. So I'm going to declare a
variable, call it logger, the name of the module, and set it to the return value
of the required function. Now, let's love this logger
and see what we get. So node app dot Js. Look, we get an object. This object has a single
method called log. You can see that's a function. So we can call this function for this method in app that Js. So back here, we
call logger dot. And look, here we have
intelligence in VS code, so we call Log and
pass a message. Now, back in terminal, and we get message
on the console. So this is how we work
with modules in node. When we define a module, we export one or more members, and then to load the module, we use the required function. Now, in the recent
versions of JavaScript, we have the ability
to define constants. So as a best practice,
when loading a module, it's better to store the result
in a constant like this. The reason for this is
because we don't want to accidentally override the value
of logger like this here. If we set this to one,
then we call log method. We're going to get an
exception. Let me show you. So one more time. Look, we got logger that log
is not a function. Now, in contrast, let's say
we define this as a constant. So back in the terminal, let's run this program
one more time. Look, we got a different
kind of error, assignment to constant variable. Now, there are tools
out there that check our JavaScript code
for errors like that. So by using these
constructs properly, we can prevent these errors
from happening at runtime. And one of these
popular tools is Jent. If you have never used
it before, don't worry. I'm just going to show
you a quick demo. So if you run Jsent app dot JS we get this error attempting to override logger,
which is a constant. So with tools like JSN, we can scan our JavaStro
code for errors like that. And that's the benefit of using a constant as opposed
to a variable here. If we accidentally
reset this object, then we're going
to get an error at compile time instead
of at runtime, okay? And one last thing before
we finish this lecture. Sometimes, instead of exporting
an object from a module, you will want to export
only a single function. For example, here in
our logger module, we don't necessarily
need an object because we have a single method. An object would be useful if we had multiple methods
or properties here. But in this case, instead
of exporting an object, we can export a single funtion. So we can reset this exports
to the log function. So initially, it was
an empty object, but we reset it
to just a fenton. With that, back in app or Js, logger is no longer an object. It's a function that we can
call directly like this. So Logger recall it and
give it an argument. Now, a better name for
this function is log. So I'm going to press F two
to rename this Log like this. Now, back in termino,
let's run node app Js and we get the same result. So in your modules, you can export a single
function or an object.
6. Navigating Paths in Node.js: In the previous lectures, I told you that in node, we have some useful modules
built into the core of node. With these modules, we
can work with the files, with the operating system, with the network, and so on. So let's take a quick
look at this module. Head over to nods.org, then click on DCS. On the left hand side, you can see the latest stable version, which is 18.16 0.1 at the
time of recording this video. By the time you
watch this video, it could be possible that it is upgraded to a newer version, although it doesn't
matter, so click on this. Here you can see
a list of items. Now, this is a nod
documentation. It is not necessary that all of the items
here are modules. Some of these are also objects like this console object
or this buffer object, which is also a global object, and we will learn more about
this in the future videos. However, this is a
fairly short list. Now, let me give you
a quick overview of the modules that we
are going to learn. First, we have the file system, which is very useful
module to work with files. Another is the SGTB module, which is very useful
to build services for networking and
for HTP request. Scroll down and you will
find the OS module, which is very useful to work
with the operating system. Then we have the path module, which has a bunch
of useful methods to work with different tats. Then we have the process module through which we will know
about the current process. We have query strings, which is very useful when
you work with SGTBRquest, and then we have
the stream module, which is useful to
work with the streams of data and a bunch
of other modules. So in this lecture, we are going to look
at this path module. Click on this. As you can
see in the documentation, we have different
functions defined here. In this lecture, I'm going
to use the parse method. If you scroll down, you will see how to use
this path module. Here, we have the required
function we have seen before, and it takes an argument, which is the path module. It says node, and then
it is defined as path. Then we store it in a
constant to the object path. So I will just copy
it and back to VSCO. In the app that JS, I'll just page the line
and it is ready to use. So we can use Path dot ps
and it takes an argument. So I will write, underscore,
underscore, file name. It's an argument that you can see in the module
wrapper function, but we'll talk about it later. So I'll store it in a constant
and call it path object. And then we'll just simply
log it to the console. Path object. Save it. Now back to the terminal. In the terminal, we'll
just run the code, node app dot js. And we can see we
have an object with a bunch of elements like root, DIR, which is the complete path to the folder which
contains the file. Then we have the
base app dot js, the extension, and
the name of the file. In the next lecture, we'll learn about the
operating system module.
7. Interfacing with the OS: Node.js OS Module: In this lecture, I'm
going to show you how to get information about the current operating system. Go back to the nod
documentation, scroll down the page, and you will see the
operating system module. Here, Os click on it, and you will see the methods
defined in this module, like we have free MM to get the free
memory of our system. We also have total MM to get the total
memory of the system. We can also find about
the user information, and we can also know about
the uptime and so on. So scroll down, and you'll see how to use this module
like any other module. So we have the
required function. We'll define the node OS here and store it
in a constant OS. So I'll just copy it
and back to OS code. Here, let me delete all the code and simply paste it here. So now operating system
module is ready to use. I'm going to use s
dot fremm to get the free memory of my system and Total Mm to get the
total memory of my system. And I'm going to
quickly store them in a variable total memory. And cons free memory. Now just simply log
them to the console. So console dot lag, total Memory and simply append total memory
variable here. That's it. But there is a
better way to do it. We can use the template
literal feature of ECMAScript. So for those who don't know, ECMAScript is a specification
used for JavaScript. Every year, there are new features that are
being added to JavaScript, and that is being
maintained by ECMAScript. So this template
literal feature is available in ES six or
Abo specifications. And the latest version of
ECMAScript is ES 14 or ES 2023. Let me show you how to
use template literal. So console dot log, and instead of single quotes, we will use the BTI character. You will find the BTI character just below the escape
button on your keypad. So let's add some static
text here like Total memory. And I want to add
something dynamically. So I'll use the dollar sign
followed by curly braces. And inside this, it
requires an argument. In this case, the
argument is our variable, which is total memory. So in this way,
you don't have to concatenate the variable
with the static text. So I'll just copy this and change the total
memory to re memory. Here, also, I'll change
the variable to re memory. Now let's save it and quickly
check on the terminal. Before that, here we don't
need this first console. So I'll just commend this
out back to the terminal, and let's run the code. Node app dot js. As you can see, the total
memory and free memory of my system is displayed here. What is interesting here
is that before node, we couldn't use JavaScript to find this kind
of information. After node, it is pretty
much simple using the OS module and other
built in modules of node. For example, we can build a web server which will stend to the SDTPRquest on a port, and I'm going to show you all
this later in the section.
8. Manipulating the File System with Node.js: In this lecture,
I'm going to show you how to work
with files in node. Scroll down and you will see
the file system module here. Click on it. So
here on this page, you will see a
comprehensive set of methods to work with files. However, I'm not going to waste your time and show you
each and every method. Instead, I will show
you an example of a particular operation
that is to read the directory in different ways to perform that operation. So let's move on to
the file system here. So we have two different ways to use a file system module, and we'll look it into shortly. Before that, let me show you two different syntaxes that we can use to load
the FS module. You can either use the
import functionality of the ESX modules or you can just use the common
JavaScript syntax, which uses the required function where you have to
provide the namespace node fs Promises
or just node FS. And all you need to do is store it in a
constant called FS. So either way, you
are going to use the or load the FS module, the File system module. Now, let us understand the basic difference between
the promise based APIs, callback APIs, and
synchronous APIs. So the file system
operations can be performed synchronously
and asynchronously. And furthermore, asynchronous
operations can be performed using the callback API or the promise based API. All the operations
are accessible using both the common JavaScript
syntax and the ES six modules. Now, let us understand
the basic difference between all these APIs. Synchronous APIs perform all
operations synchronously, blocking the event loop until the operation
completes or fails. So as I told you before, nor is a single
threaded operation. It does not execute another request until the current operation
completes or fails. Moving on to the callback APIs. These APIs perform
all operations asynchronously without
blocking the event loop. Then invoke a callback function
upon completion or error. That means asynchronous
operations can be performed using
the single thread because it's non blocking. So do you remember the example of the restaurant
that I gave you? A waiter can take the order and give it
back to the kitchen. Then take the order from another table and go back
to the kitchen again. In this way, we can serve as
many clients as possible. And lastly, we
have Promises API. This provides asynchronous
file system methods that return promises. So it is something that is
added to node Version ten. Before that, all the
asynchronous operations were performed using
the callback API. Finally, as I told you before, both the callback and
promise based APIs uses the underlying node
thread pool to perform file system operations of
the event loop thread. Now, let us look at the example
using the promises API, callback API, and
the synchronous API. So first of all, let me
show you an example of the synchronous operation using the common JavaScript syntax. So we load the FS module using the required
function like this. Once the function is loaded, we can access the read
DIR sync from here. Now we will have to
mention the path here, so period slash because we are looking at the
current directory. Now, let me save this in
a constant call files. Now, all we need to do is to log the files to the console. So console dot log files. Here, the synchronous
operation will complete, but let me encapsulate all the code here in
the trikt statement. So try to get the files
here and handle the error, something like this, console
dot log error, C error. However, this is not the
ideal way to handle an error, but this will be it for now. We can also rectify the
code a bit more like this for const file of files. So I'm using the four loop to log in individual file
of the array files. Save it and pack
through the terminal. Run node app dot JS. So here you can
see all the files that we have in the
current folder. Check this from here,
Nord modules, app dot js, logy dot js, package lag
dot JCN, package dot JSN. And here, all the same. Back to ES code. Now let me show you the
asynchronous examples using the ESX modules. So I'm going to delete
all this from here. Before that, let me tell you
that to use the ESX modules, you have to declare its type
in the package dot JSNFle. So you have to go
to Packs dot Jason and declare the type as module. So remember, once you
declare the type as module, you will not be able to
use the required function. Once declared, you'll only be able to use the ES six modes. So I'll close this and remember to load
the ES six modules, we will use Import. Import astric as FS from
node column FS. That's it. To use the callback
API, we will usefsRdDR. We will have to include
the path here period. And now the second argument
will be a callback function. So, now the callback function
will take two parameters. First, the error and
second is the files. So I'm using the
arrow function here, ERR for error then files, and this goes to a code block. We are going to use
IL statement here. So I ER error, then you will have to
log it to the console. So error, Column ERRElse you can simply
log the files like this. So let's see what happens. Save it and back
to the terminal. Run nod at Js. So as you can see,
the files array, which includes the
name of the files. Now, back to VS code, we can also rectify the
code as we did earlier. So we will use the four loop again for const file of files. And instead of files, it should be file. Back to the terminal again, node app dot js. C, the same result. Trigger this and
back to VS code, and let me show you
the last example for the promises API. To use the promises API, we have to use slash
Promises here. Now, promises APIs has the file system methods
that return promises. So as you know, if you are familiar with
Asynchronous JavaScript, if you're returning a promise, then you have to decorate
the function with await. So let me quickly
write await here. Then you don't have to use
the Callbek function here. So I'll just move it
here and that's it. Now, we will have to
store this in a constant. So I'll store this in
files const files. And then we can simply
take this code from here and move it here. Finally, we don't
need this function, so I'm going to delete this. Remember, this is a
synchronous function. It would also return an error. So we will have to make
sure that we handle this error with the
help of tr cache block. So I will encapsulate this
in a tri cachblock tryCatch, and I will move all this code in the tri block and to log error, I will do something like
this error column error. Save this and back to dot
terminal one more time. Node app dot js, and the same result.
9. Event-Driven Programming in Node.js: One of the core concepts in node is the concept of events. In fact, a lot of nodes core functionality is based on this concept of events. An event is basically
a signal that indicates that something has
happened in our application. For example, in node, we have a class called STTB that we can use
to build a web server. So we'll listen on a given pot, and every time we receive
a request on that port, that STDP class raises an event. Now our job is to
respond to that event, which basically involves reading that request and returning
the right response. As you go through
node documentation, you can see that several classes in node raises different
kinds of events. And in your code, you might be interested to respond
to those events. So in this lecture, I'm going to show you how to
work with the events module. Now, back in the note
documentation, once again, in the list of modules, you can see here we have
this events module. In this module, we have one class that is
called event emitter. It's one of the core
building blocks of node, and a lot of classes are
based on this event emitter. Let's see how we can work
with this event emitter. Where in VS code? First, let's load
the events module. So require node column events. Now here, when we call
the required function, we get the event emitter class. So constant event emitter. You may also use ESX
modules to import the Evenimter class from the
events module like this. Import event emitter
from node events. Do make sure to update
the package or JCNFle to indicate the type a module as
it did in the last lecture. So let's come in
this out for now. Note that here in
terms of naming, the first letter of
every word is uppercase. This is a convention
that indicates that this event emitter is a
class. It's not a function. It's not a simple
value, it's a class. A class is a type of container for properties
and functions, which we call methods. So in this event emitter class, we have these methods that you see here in
the documentation. So a class is a container or a bunch of related
methods and properties. Now here, in order to
use this event emitter, first, we need to create
an instance of this class. So constant emitter, we set
this two new event emeter. So here, this emiter
is an object. In case you don't know the difference between
a class and an object, let me give you a metaphor. A class is like human and an object is like an actual
person like you and me. So a class defines
the properties and behaviors of a
concept like a human, but an object is an actual
instance of that class. Okay? So here, this first
event emitter is a class. It's a blueprint. It defines what an
event emitter can do, but the second emitter
is an actual object. This is the one that we are going to use in our application. So this emeter has
a bunch of methods. And these are all the methods that you saw
in the documentation. Now, even though here we
have more than ten methods, most of the time you use
only two of these methods. One is MT that we use
to raise an event. ET basically means making a
noise or produce something. In this case, we
are making a noise. In our application, we are signaling that an
event has happened. Okay? So that's
the meaning of M. Now here, we pass
an argument that is the name of the event
let's say message log. In the future, we are going
to extend our logger module, and every time we log a message, we are going to raise an
event called message log. Now, if you run
this application, nothing is going to
happen. Let me show you. Back in the terminal.
Node app Js. Look, nothing happens. Because we have
raised an event here, but nowhere in our application, we have registered a listener that is interested
in that event. A listener is a
function that will be called when that event
is raised, okay? So let's register a
listener that will be called when the message
logged event is raised. You register a listener
and then write emitter. Look, here we have this
method, add listener. But we have an alias for this method that we use
more often. That is on. If you have worked with JQuery, you have seen this before. So on or add listener, they are exactly the same. But quite often, we
use the on method. Now this method
takes two arguments. First one is the
name of the event. In this case, message log, and the second one is a callback function or
the actual listener. So here we pass a function
and this function will be called when that
event is raised. All right. For now, I just want to log
a message on the console. So console dot log. Let's say listener call. Now let's run this application. So node at the Js and we got
this message, listener call. So this indicates that
when we raise this event, the callback function or
the listener was called. And of course, the order
is important here. If we registered this listener after calling the EMT method, nothing would have happened because when we call
the EMT method, this emitter, it reads for all the registered listeners and calls them synchronously. So this is the basic
of raising events and handling them using
the event emitter class.
10. Event Arguments: Diving Deeper: Quite often when we
want to raise an event, we also want to send some
data about that event. For example, in
our lager module, when we log a message, perhaps our remote login service will generate an ID
for that message. Perhaps you want to return
that ID to the coin, or it may give us a URL to access that log
message directly. So here, when raising an event, we can add additional arguments which we refer to
as event arguments. So we can add an ID like
one and we can add a URL. But as you can see,
these magic values here are a little bit confusing. If you want to send multiple
values about an event, it's a better practice to encapsulate those values
inside an object. So here we add an object. We get a couple of
properties like ID. We set it to ID of this
message that is locked, and another property
URL like this. Okay, so we refer to this
object as event argument. Now, when registering a listener here this callback function, this actual listener can also receive this
event argument. So here we add a parameter called Rc. You can
call it anything. The name doesn't matter,
but by convention, we often use arc or some people use E to refer
to the event or event arc. Whatever you prefer
is perfectly fine. So here we have R. Now let's
log it on the console. Very simple. Let's
run this application. The note abjso, look, listener called, and
here's our event argument. And with this technique, we can pass data about the
event that just happened. Now here's a simple
exercise for you. Let's imagine in
our logger module, just before calling our remote service to log the message, we're going to raise an
event called logging. And while raising this event, we also want to send some data. That is a message that
we are going to log. So what I want you to do is
to use what you have learned in this lecture and raise and
handle this logging event. It's a very simple exercise. I just want you to get
used to this syntax.
11. Expanding EventEmitter: Advanced Event Handling: Now in the real world, it's quite rare
that you would work with this event emitter
object directly. Instead, you want
to create a class that has all the capabilities
of the event emitter, and then you will use
that class in your code. Let me explain what I
mean by that and why. So let's open up
our logger module. And in this module, we're exporting a simple
function Milo right? And here, we log that
message on the console. Now, this is the common
Javascript syntax. Let us change it. As we are now using
the ES six modules, we need to write
export default, Milog. After this, we want
to raise an event, and then later in app module, we will listen for that
event and do something. So let's go back to our app module and copy some
code in the logger module. From the top, I'm going to copy these two lines to bring the event emitter
in this module. Okay? Now, back in app module, I'm also going to move
these two lines for raising an event into the logger module because this code
should not be here. It's a logger module
that emits or signals event saying
the message is log. So cut here, after
we log the message, we raise an event like this. Okay? Now, back in app module, we don't need this command. Here we need to log
the logger module and call the MogFunction. So to load the logger module, we'll use the import
functionality of the Six modules. So import log, I
renamed it to log from slash logger
dot Gs. Very easy. And here we simply call the
log function with a message. Okay? Now when we run
this application, we are only going to see
this message on the console. In other words, this event
listener will not be called. Let's verify that, and then I will explain why that happens. Back in terminal node, app Jas, look, we only got
the message on the console. So our event listener
was not called. The reason for this
is because here we are working with two
different event emiters. In App Js, we have this
event emitter object, and in lower module, we have another event
emitter object. So earlier, I told you
that a class is like a blueprint and an object
is an actual instance. As a metaphor, I said we could have a class called
human or person, but the objects could be Eric, Steve, David, Maria, whatever. But in this case, we have
two different objects. In the logger
module, we're using this emitter object
to emit an event, whereas in app module, we are using another entemeter object to handle that event. These are completely different. When we register
a listener here, that listener is
only registered with this event emeter which is completely different from
the other event emitter. So that's why I told you
in your applications, it's very rare that
you would want to work with this event
emitter directly. Instead, you want
to create a class that has all the capabilities
of this event emitter, but it has additional
capabilities. In this case, you want
to create a class called logger that has this
additional method, log. Okay? So the first thing we want to do here
is define a class. In ES six, we have
this keyword class, which is a syntactical sugar for creating a
constructor function. With this, we can
define a class, logger. Note that the first letter of every word in a class
should be uppercase. This is a pascal case convention that we use for naming classes. Class logger, we
have a code block, we need to move
this log function inside this logger class. So cat past it here. Now we have an error because we define a function
inside a class. We don't need this error
function from now on, we refer to this
function as a method. So when a funtion
is inside a class, we say that's a method
in that class, okay? So here, we have
this lager class. Now at the end, instead
of exporting the Mel of pointie we are going to
export the larger class. Okay. Now we want this lower class to have all the capabilities
of this event emitter. The way we do that is
by using the extents keyword that comes in
ES six, so extends. And here we add the
name of the parent or the base class,
so event emitter. And with this simple change, this lower class will have all the functionality that
is defined in event emeter. So here, when
raising this event, instead of using
this emitter object, we are going to use
this so in this class, we can directly emit
or raise events. Okay? Now we no longer need this
actual emitter object because we have not used it anywhere
in this code. So delete. We're done with
the logger module. Now, back in the app module, so here, when importing the logger module,
we get a class. So I'm going to rename this logger with
L. That's a class. Now we create an object, so new logger, and
then to log a message, we call logger dot log. Now, similar to the change
that we made in Lager module, we no longer need this
event dimeter object. Here, we want to work directly
with this lower object. So we are going to register this listener on
this lober object. Okay? So I'm going
to move this code. After creating the logger, we say, Hey logger, when you raise this
message locked event, I want to execute this code. And finally, you can see we no longer need this
event emitter object. It's not used anywhere, the late now when we
run this application, we are going to see this
message on the console. But also because we are using
the same lo or object for registering an event listener
and also raising an event, we are going to see this
message on the console. So note app Jazz look, this is a message
on the console, and you see our listener
was successfully call. So let's quickly recap. If you want to raise events in your application to signal
that something has happened, you need to create a class
that extends event emeter. With this, that class will have all the functionality
defined in event emitter. But you can also add
additional functionality. In this case, we have the
ability to log a message. And then inside that class, whenever you want
to raise an event, you use this dot met because this references
the logger class itself, which extends event emitter. So all the methods defined in event emitter will also
be part of this class. And finally, in
app module, again, instead of using an
instance of event emitter, you will use an instance of the custom class that you have defined that extends
event emeter.
12. Handling HTTP Requests: Node.js Networking: One of the powerful
building blocks of node is the STDP module that we use for creating
network applications. For example, we can
create a web server that listens for STDPRquest
on a given port. And with this, we
can easily create a backend service for
our client applications, like a web application
that we built with react or angular or a mobile application
running on a mobile device. So once again, back in
the node documentation, the list of modules, you
can find this TDP module. In this module, you can see various classes
like tt dot agent, http dot client
request, and so on. Each of these classes
has a bunch of properties, methods, and events. So back in VS code, let's load the SGTP module. So import ASTC as
HTTP from node STT. Okay. Now here we can call
db dot, create server. This is one of the functions
defined in this module. And with this, we can
create a web server. So let's store the result
in a server object. Now what is interesting is that this server is an event emitter. So it has all the
capabilities of event emitter that is so
earlier in this section. So look, server dot we
have the on method. Or add listener or M and song. Also, if you look at the
documentation of the SGTP modo, on this page, you can see
http dot server class. Here, the documentation
says that this class inherits from net dot SAR. So this is another class
defined in the net module. Let's have a look. Now,
here in the documentation says that Net dot server
is an event emitter. But that's why I said
a bunch of nodes, core functionality is
based on event emitter. So back to our server object. Now we can call server dot
LISN and give it a port. Let's say port 3,000. Now, following that, I'm
going to add console dot log saying listening on port 3,000. Okay? Now when we run
this application, the server will
listen on port 3,000. As I told you before, every time there is a new
connection or new request, the server raises an event. So we can use the on method
to handle that event. So before listening, we want to register a listener
or a handler. So server on the
name of the event is connection that you can
find in the documentation. You don't have to memorize
any of this stuff. And the second argument is a callback function or
the actual listener. As you can see in
the tool tip here, this listener is a function with one argument that is socket
of type socket class, and it turns Y. So here we have the arrow
function syntax in ES six. So let's add an arrow
function that takes a socket and goes
to this code block. Now here we can simply lock something on the console
name connection. Now back in a terminal, let's run this application. You can see we are
listening on port 3,000. Back in the browser, let's head over to local host port 3,000. Now if you look in the terminal, you can see we have a
new connection here. So you can see this
SR object raises different kinds of events
that you can respond to. Now in real world applications, we are not going to respond to the connection event
bill HTTP service. This is very low level. So let's delete this.
What we commonly do is we pass a callback function
which creates method. This function takes two
parameters request and response. Now in this function, instead
of working with a socket, we can work with the actual
request or response objects. So we can check if
request URL equals slash, then we can send
something to the client. For example, response stat, write, hello world, and
then we end the response. Okay. In the terminal, we can exit here by pressing Control C and then run
the application again. We are still listening
on port 3,000. Let's refresh the page. So we got hello on the homepage. Now we want to build
a backend service for a web or mobile application, we need to handle
various routes here. For example, we can
have another I block if request URL equals
API slash courses. Perhaps here we want to return the list of courses
from the database. So we would do something like
this, response dot right. Now here we want to return an
array of objects using JCN. So we use cn dot stringify and give it an array of objects. Now for simplicity,
here we don't have to worry about the database
or complex objects. Let's just return an array of numbers one, two, and three. So we pass this to JCN stringifi which will convert
this array into a string using JSN syntax. And then we'll write
it to the response. Now back in the terminal, we need to stop this process again and run it one more time. Now in the future,
I will show you how we can automate this. So every time we make a simple
change to our application, we don't have to restart. So now back in the browser, if we go slash API
slash courses, we get an array
with three numbers. So as you see, building a web server with
node is very easy. Now in the real world,
we are not going to use this SDDP module to build a backend service
for our application. The reason for this is
because as you can see here, as we add more outs, this code gets more complex
because we add all of them in a linear way inside
this callback function. So instead, we use a
framework called Express, which gives our application a clean structure to
handle various routes. Internally, the
express framework is built on top of the
STDP module in note.