Node Architecture
Node depends on a set of libraries which is what makes it node. Some of these are:
- http-parser
- c-ares
- OpenSSL
- zlib
but the 2 main libraries are:
V8(c++ and js)
this is the js interpreter and compiler in chrome but it is also node's by definition the one that is in charge of converting the code made in js into a low level code (from js to bytecode and then to machine code).
libuv
Unicorn velociraptor library. this library provides many of the things that make node work.some of these functions are access to the computer’s file system, operating system, networking, and more.the most important functionality's are the event loop and thread pool
Process

- using sync versions of fs, crypto or zlib module functions within callback functions.
- performing complex calculations (>O(n^2)).
- using complex regular expressions (e.g. nested quantifiers).
when a node.js app initializes, modules are imported, the top-level code is executed, and then callbacks are registered. Then, the event loop start. The event loop performs all the operations of the application but some of them are too heavy. as for example:
file system APIs
encryption
DNS lookups
compresion
In these cases node takes care and offloads the task to a thread pool,
Event loop

Any code inside the callback functions is executed inside the event loop. That is to say, this is where all the asynchronous code is executed. When we start our application, the event loop begins executing immediately. Each phase of the event loop has its own callback queue. Some of these phases are more important than others, the most important being
callbacks of expired timers (e.g., setTimeout()).
handle I/O polling, i.e., it looks for I/O processes that need to be executed and puts them in the callback queue of the phase. Since I/O encompasses network and file access, about 99% of the code we write is handled by this phase.
setImmediate() callbacks, which are special timers that are executed after the I/O polling phase. The final phase (for our purposes) handles shutdown callbacks, for example, when a web server shuts down.
the process.nextTick() queue and the other microtask queue (which includes promises). These two queues have their own pattern.
If they have anything in their callback queues, they will be executed after the completion of any of the four phases described above. When the event loop has gone through all four phases, it needs to check whether to run the loop again or just exit the program. It does this by checking to see if there are any I/O processes or timers running in the background. If there are, the event loop goes back to the beginning and repeats itself. the events are emitted. the idea of the event loop is that:
- Events are emitted, events are emitted from an asynchronous function such as receiving an HTTP request, the fileSystem module or a timer has finished.
- Afterwards, the event loop picks them up. The callback functions are executed and as mentioned above, event-loop offloads the heaviest tasks to the thread-pool.
The event-driven architecture
Node has objects called event emitters. They emit events when a file is read, when a timer ends, or when a network request is made, among other things. These events are picked up by the event listener that we can configure.an example of this is when we want to create a server.
const server = http.createServer();
server.on('request', (req, res) => {
console.log('request receved');
res.end('request receved');
});
This 'server.on' is actually how we create a listener and it listens for a request. So let's say we have our server running and a new request is made. The server acts as a sender and will automatically emit an event called "request" every time a request comes to the server, then the callback function attached to this listener will be called and send some data back to the clients.This is an example of the Observer , where a listener is constantly waiting for an event from a sender.
const EventEmitter = require('events');
const myEmitter = new EventEmitter();
myEmitter.on('myevent',()=>{ //Observer 1
console.log('Oh k0')
});
myEmitter.on('myevent',name=>{ //Observer 1
console.log(`Oh ${name}`)
});
myEmitter.emit('myevent', "Pablo") // object that emits the event
Streams
A Stream is a collection of data that is not available all at once, since it is transmitted piece by piece (called chunks). This allows you to read and write each chunk without waiting for all the data to be available. Streams work by default with buffers, as long as it has not been specifically configured to work with objects.Streams are instances of an EventEmitter class So they can receive and send events.
This is used to avoid the error when the node memory is exceeded, since node has a memory limit of 2gb. This happens because with the streams we are working piece by piece and this is freeing memory as the information is used. Think of youtube or netflix, they are called stream platforms because they use the same principle (they load each video chunk by chunk).
In node there are 4 fundamental types:
Redable streams
These streams are the ones that can read information (consume data). An example of these streams can be when there is an http request (the data that comes from a server is received piece by piece) another example can be with file system because when we read a file we read it piece by piece (fs read).
The most important events
- data:The data event is emitted when there is a new piece of data to consume.
- end:The end event a is emitted when there is no more piece of data to consume.
The most important functions
- pipe(): the method used to take a readable stream and connect it to a writeable stream.
readableSource.pipe(writeableDest)
- read():
Example
for example to upload a file to the page
server.on('request', (req, res) => {
fs.readFile('text-file.txt', (err, data) => {
res.end(data)
});
});
This will load the entire file into memory when there is a request, so if the file is very large it will take a long time to load:
server.on("request", (req, res) => {
const readable = fs.createReadStream("text-file.txt");
readable.on("data", chunk => {
res.write(chunk);
});
readable.on("end", () => {
res.end();
});
});
or we can also used pipe
server.on("request", (req, res) => {
const readable = fs.createReadStream("text-file.txt");
readable.pipe(res)
});
Writable streams
These streams unlike the redable streams are the ones that can write and manipulate data. Some examples of these streams are when there is an http response (when we send back to the client) this is a writable stream another example would be with file system when we write a file (fs write) piece by piece.
The most important events
- drain:The data event is emitted when there is a new piece of data to consume.
- end:end:The end event a is emitted when there is no more piece of data to consume.
The most important functions
- write()
- end()
Duplex streams
We can say that these streams are a combination of the 2 previous ones since they can read and write at the same time. They are less common than the previous ones but it does not make them less important. An example of this would be a web socket of a module.
Transform streams
This stream is a duplex stream, but it can edit, transform the data being read. A good example of this stream is the core module zlib or gzip.