Node JS Basics - aakash14goplani/FullStack GitHub Wiki

Topics Covered


Introduction

What is Node JS

  • Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

Why Node JS

  • Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.
  • Node.js’ package ecosystem, npm, is the largest ecosystem of open source libraries in the world.

Advantages of using Node JS

  • One important side note at this point of time, nodejs is not limited to running code on a server, we can execute any javascript code with nodejs and often that is code that runs on a server and is executed upon incoming requests but you also often use nodejs for other code, for example for local utility scripts or build tools.

  • If you worked with let's say react or angular or vue or anything of that kind, you actually used nodejs indirectly a lot for all the build processes these languages or frameworks needed because nodejs is a great tool for writing utility scripts.

  • You have access to the file system so you can write and read and manipulate files and this allows you to do a lot of utility stuff on your computer that is never exposed to the public.

  • In general and that is the most popular thing you do with nodejs though, you use it in the context of web development and server side code. So you use it to run a server and actually and that is an important difference to PHP for example, with nodejs you don't just write the code that is running on your server, you also write the server yourself, so the code that takes the incoming requests and routes them to your well other code. In PHP, you have extra tools like apache or nginx which run the servers which listen to incoming requests and then execute your php code, here nodejs does both.

  • We also use it or we therefore also use it to run all our business logic, so not just to listen to incoming requests but to then work with the requests data, work with files, work with databases.

Alternatives to nodejs

  • That would be things like Python, also with frameworks like flask or Django or PHP with frameworks like laravel maybe or standalone vanilla PHP of course and more, asp.net, Ruby on Rails, all that stuff, these basically are all replacements for nodejs or nodejs can be a replacement for them.

REPL

There are two different ways of executing your node code:

  1. Executing code written in Files

  2. Executing code by writing scripts in CMD (REPL). The REPL is what you use if you just type node into your terminal. REPL stands for:

    • Read => reading user input
    • Eval => evaluating user input
    • Print => outputting a result
    • Loop => returning and waiting for new input

Global Objects

  • Nodejs provides handful of functions and objects we can use globally without importing. E.g. require(), __dirname etc. complete list

  • If we need any other modules apart from global objects but those modules are part for core Node JS environment, we can always import them. This is how we import packages/modules in Node:

const http = require('http');
  • For importing files: Now a path to one of your files always has to start with ./ for relative path or / if it's an absolute path. Core module imports do not need slash. If you omit ./ or / at the beginning, it will not look for a local file, so even if you had a file named http.js, it would not import this file.
const http = require('http'); // importing core module
const http = require('./http'); // importing a file name `http`

Creating Server

  • We'll use that http module and to use it, we need to import it. We do this by creating a new constant and you could create a var or use let too.
const http = require('http');
const server = http.createServer();
  • http has the createServer() method which creates a server. This method takes a RequestListener as an argument. A RequestListener simply is a function that will execute for every incoming request so let's define such a function.
const server = http.createServer((req, res) => {
    console.log('req :', req);
    console.log('res :', res);
});
  • The RequestListener receives a request which is of type incoming message and a response object, so in short nodejs automatically gives us some object that represents the incoming request and allows us to read data from that request and it gives us an object response which we can use to return a response to whoever sent that request.

  • We pass that function to create server and therefore, node will execute this function whenever a request reaches our server. This is an event driven architecture nodejs uses heavily.

    • You work a lot with such setups or such code snippets where you tell node if X happens, do Y, so in this case if a request comes, please execute this function.
  • To reach that server, we can call and one method is listen() that actually starts a process where nodejs will not immediately exit our script but where nodejs will instead keep this running to listen, that's why the method is named like this for incoming requests.

server.listen(4200, 'localhost');
  • Now listen() as you can see takes a couple of arguments, optional arguments, the first one is the port on which you want to listen. Second one is a hostname, by default, this will be the name of the machine this is running on, so for our local machine, this is localhost by default.

  • By calling createServer() method we have registered an event and if you eventually were to unregister and you can do this with process.exit(), it would end and we can see that too.

const server = http.createServer((req, res) => {
    console.log('req :', req);
    console.log('res :', res);
    process.exit();
});
server.listen(4200, 'localhost');
  • Now typically you don't call that in your code because you don't want to quit your server, if it quits people will not be able to reach your webpage anymore but this is important for understanding how this works.

Event Loop

  • The event loop, this is basically a loop process which is managed by nodejs which keeps on running as long as there is work to do you could say, it keeps on running as long as there are event listeners registered.

  • It uses such an event driven approach for all kinds of stuff because it actually executes single threaded javascript. So the entire node process basically uses one thread on our computer it's running on. Now as you might guess if we create a server with nodejs, it should of course be able to handle multiple, thousands, tens of thousands or hundreds of thousands of incoming requests and if it would always pause and then do something with that request, this would not be that great hence it uses this event loop concept where in the end it always keeps on running and just executes code when a certain event occurs so that in general it's always available.

  • It is super fast in handling these requests and actually behind the scenes, it does some multi-threading by leveraging the operating system.

  • Must read article on Event Loop

  • Youtube video

Event Loop Phases

  1. Timers

    • At the beginning of each new iteration it checks if there are any timer callbacks (i.e. setTimeout() and setInterval()) it should execute. Once that timer completes, nodejs is aware of this and at the beginning of each new loop iteration, it executes any due timer callbacks.
  2. Pending Callbacks

    • Then as a next step, it checks other callbacks, for example if we had write or read file, we might have a callback because that operation finished and it will then also execute these callbacks.
    • Nodejs will leave that phase at a certain point of time and that can also mean that if there are too many outstanding callbacks, it will continue its loop iteration and postpone these callbacks to the next iteration to execute them.
  3. Poll

    • After working on these open callbacks, it will enter a poll phase. The poll phase is basically a phase where nodejs will look for new IO events and basically do its best to execute their callbacks immediately if possible. If that's not possible, it will defer the execution and basically register this as a pending callback.
    • Important, it also will check if there are any timer callbacks due to be executed and if that is the case, it will jump to that timer phase and execute them right away, so it can actually jump back there and not finish the iteration otherwise it will continue to next phase.
  4. Check

    • Here it executes any setImmediate() callbacks. setImmediate() is a bit like setTimeout() and setInterval(), just that it will execute immediately but always after any open callbacks have been executed, so typically faster than setTimeout() and setInterval() with one millisecond of open duration, but after the current cycle has finished or at least finished open callbacks that were due to be handled in that current iteration.
  5. Close Callbacks

    • Now we're nearing the end of each iteration cycle and now nodejs will execute all close event callbacks, so if you registered any close events and in our code, this would be the point of time where nodejs executes their appropriate callbacks.
  6. Exit

    • Well and then we might exit the whole nodejs program but only if there are no remaining event handlers which are registered.
    • Internally nodejs keeps track of its open event listeners and it basically has a counter, references which it increments by 1 for every new callback that is registered, it reduces that counter by 1 for every event listener that it doesn't need anymore.
    • We can call the process.exit() function as well to exit from the loop/code.

Event Loop Phases


Blocking vs Non Blocking Process


How Your code gets executed

  • Node JS uses only one single javascript thread to execute your code.

  • Let's say we have some code which accesses the file system, now working with files often is a task that takes longer because files can be very big and it doesn't necessarily complete instantly, therefore if we're doing this upon an incoming request, a second request might have to wait because we're not able to handle it yet or it even gets declined, so basically our webpage is down for that user.

  • Node JS relies on Event loop here. The event loop is automatically started by nodejs when your program starts, you don't have to do that explicitly, this is responsible for handling event callbacks,

  • That doesn't help us with our long taking file operation though and it's important to understand that this operation is not handled by the event loop, the event loop will only handle callbacks that contain fast finishing code. Instead our file system operation and a couple of other long taking operations are sent to a worker pool which is also spun up and managed by nodejs automatically.

  • Worker pool is responsible for all the heavy lifting, it is kind of totally detached of your javascript code and it runs on different threads, it can spin up multiple threads, it's closely intervened with your operating system you're running the app on.

  • The only connection to the event loop is that once the worker is done executing is operation, it will trigger the callback for that read file operation and since the event loop is responsible for the events and the callbacks, this will in the end, end-up in the event loop, so there nodejs will then basically execute the appropriate callback.

node js internals

  • Security Aspect with Node JS
    • If we have two incoming request simultaneously, data between them would not be shared. It would be completely Atomic operation.
    • By default we have some separation e.g. the callback method that gets executed for every new incoming request runs for that incoming request and anything we do to the request or response object there will not be exposed to our request or response objects because each function is only scoped to itself and not accessible by the other functions.