aliases | |
---|---|
|
- An asynchronous event-driven runtime environment.
- Allows [[JavaScript]] to be run outside the browser.
- Single-threaded
- A Node.js app runs without creating a new thread for every request.
- Execution stops as soon as there is no more tasks left in the event loop.
http.createServer()
event never finishes running by default.- The event loop is only used to determine what to do next when the execution of a task finishes.
- e.g. In the snippet below, the
while
loop is not interrupted bysetTimeout
, because the event loop is only checked for new tasks once an ongoing execution is complete.
setTimeout(() => {
console.log('Timeout')
}, 1000)
console.log("Enter Loop")
let i = 0
while(new Date().getTime() < start.getTime() + 4000) {
i++
}
console.log("Exit Loop")
/*
Output:
Enter Loop
Exit Loop
Timeout
*/
- Non-blocking I/O model using the event loop (events and callbacks)
- Code is not necessarily executed in the order that it is written.
- Every I/O operation takes a callback. When performing I/O, control is passed back to the event loop. The callback will be run once the data from the I/O operation is available.
fs.readFile('/path/to/file.txt', (err, data) => {
console.log(data);
});
- Built on the V8 [[JavaScript|JS]] engine
- Ideal use-cases:
- APIs utilizing databases
- streaming data
- real-time data transfer (e.g. chat apps)
- server-side web apps
- Not ideal for server-side processing that's CPU-intensive (e.g. file conversion)
- For a CPU-intensive workloads, the only approach is to either optimize the algorithm to use less CPU or to scale to multiple cores and machines to utilize more CPUs.
- By default, request data is transferred in chunks and needs to be parsed (Streams & Buffers)
- Streams are an event-driven way to handle data in Node.js that arrives in chunks, rather than all at once.
Note
Due to non-blocking design of Node.js, code may not execute in the order it's written.
- A single language is used to on both the frontend and the backend, but the ecosystem is different.
- [[DOM]] objects such as
document
andwindow
don't exist in Node.js. - The browser doesn't have access to APIs like ones that allow filesystem access.
- Node.js allows you to control the environment it runs in like the version of Node.js used. In browsers, the option of choosing the browser environment visitors will used doesn't exist.
- We can therefore use the latest supported [[JavaScript|JS]] syntax without worrying about compatibility.
- Node.js supports both CommonJS (
require()
/module.exports
) and ES module systems (import
/export
), while the browser is still getting implementation of the ES modules standard (import
/export
).
-
Node.js provides a number of built-in modules that can be used without having to install them separately:
-
fs
provides an API for reading and writing files, as well as working with directories.- It can be used to perform various file system operations such as creating, deleting, renaming, and modifying files.
-
path
provides utilities for working with file and directory paths.- It can be used to join multiple path segments together, resolve relative paths, and parse path strings into their components.
-
os
provides information about the operating system on which Node.js is running.- It can be used to retrieve information such as the hostname, CPU architecture, and amount of free memory.
-
events
can be used to create custom events and handle them. -
crypto
provides cryptographic functionality that can be used to generate hash values, encrypt and decrypt data, and generate random numbers. -
http
&https
modules allow us to launch a server among other things.https
launches an SSL server.- Data send thru a
POST
request is sent as a stream which has to be buffered in to the desired data.
const http = require("http");
const createPage = (text) => (`
Β <html>
Β Β <head>
Β Β Β <title>Welcome to Node.js</title>
Β Β </head>
Β Β <body>
Β Β Β <h1>${text}</h1>
Β Β </body>
Β </html>
`);
http.createServer((req, res) => {
res.setHeader("Content-Type", "text/html");
Β Β if (req.url === "/") {
Β Β res.write(createPage("Hello, Node!"));
Β Β return res.end();
Β Β }
res.write(createPage("404 - Not Found!"));
res.end();
}).listen(2345);
[!cite]- JS Module System ![[JS Module System]]
- In the browser, the top-level scope is understood to be the global scope, except within ES modules.
- Variables defined using
var
are created as members of the global object.
- Variables defined using
- In Node.js, the top-level scope is not the global scope.
- A variable declared in a module is local to that module, regardless of whether it is a CommonJS module or an ES module.
var msg = "Hello, World!";
console.log(global.msg); // undefined
- The
process
object provides information about and control over the current Node.js process. - It is a global object that can be accessed from anywhere in a Node.js application without requiring it.
- It offers various data sets about the program's runtime providing properties that allow for managing the Node.js process effectively like
process.versions
,process.release
, and methods likeprocess.exit()
for exiting the event loop. - Furthermore, it facilitates interactions with the environment through properties like
process.env
and enables access to command-line arguments viaprocess.argv
.
const server = http.createServer((req, res) => {
console.log(req);
process.exit();
})
- Call stack & stack trace
- Debugging: Debugger
- Errors
- Types
- Uncaught exceptions
- Async errors
node --inspect
- enables inspector agent
- listens on default address and port (127.0.0.1:9229)
node inspect script.js
- spawn child process to run script under
--inspect
flag - uses main process to run CLI debugger.
- spawn child process to run script under
- Synchronous code is said to be blocking can only execute after the one before has finished executed. e.g.
fs.readFileSync()
andfs.writeFileSync()
const fs = require("fs");
const textIn = fs.readFileSync("input.md", "utf-8");
console.log(textIn);
const textOut = "Overwritten text.";
fs.writeFileSync("input.md", textOut);
- Node.js uses callbacks to make execution asynchronous / non-blocking. e.g.
fs.readFile()
andfs.writeFile()
const fs = require("fs")
fs.readFile("input.md", "utf-8", (err, data) => {
console.log(data)
});
console.log("Reading file...")
Note
Because of its event-driven architecture, some operations rely on callbacks that get executed during a certain event. The return
keyword is often used before a function call to prevent further execution of code.
setTimeout
can be used to delay the execution of a function. With a timeout delay of0
, the callback function of asetTimeout
call will be executed as soon as possible, but after the current function execution.
setTimeout(() => {
console.log('After');
}, 0);
console.log('Before');
/*
Logs:
Before
After
*/
- This can be useful to avoid blocking on CPU-intensive tasks.
- The callback function is added to the queue in the scheduler and other functions can be executed beforehand.
setTimeout
andsetInterval
are global functions available in Node.js thru thetimer
module; They don't require imports.- Timer functions in Node.js implement a similar API as the ones provided by [[Web Browsers]] but use a different internal implementation that is built around the Node.js Event Loop.
-
fs
contains file system access methods such asreadFile()
&readFileSync()
. -
Modules
path
process.cwd()
-
__dirname
-
__filename
[!cite]- Express ![[Express]]
[!example] Example: Testing a Node server with Jest & Supertest
// app.test.js
const request = require('supertest');
describe('Product API', () => {
describe('GET /api/products', () => {
it('should return all products', async () => {
const res = await request(app).get('/api/products');
expect(res.statusCode).toBe(200);
expect(res.body.length).toBeGreaterThan(0);
expect(res.body[0]).toHaveProperty('id');
expect(res.body[0]).toHaveProperty('name');
expect(res.body[0]).toHaveProperty('price');
});
});
describe('GET /api/products/:id', () => {
it('should return a product if valid id is passed', async () => {
const res = await request(app).get('/api/products/1');
expect(res.statusCode).toBe(200);
expect(res.body).toHaveProperty('id', 1);
});
it('should return 404 if invalid id is passed', async () => {
const res = await request(app).get('/api/products/9999');
expect(res.statusCode).toBe(404);
});
});
describe('PUT /api/products/:id', () => {
it('should update an existing product', async () => {
const res = await request(app)
.put('/api/products/1')
.send({
name: 'Updated Name',
price: 69.99
});
expect(res.statusCode).toBe(200);
expect(res.body.name).toBe('Updated Name');
expect(res.body.price).toBe(69.99);
});
});
describe('DELETE /api/products/:id', () => {
it('should delete an existing product', async () => {
const res = await request(app).delete('/api/products/2');
expect(res.statusCode).toBe(204);
});
it('should return 404 if product not found', async () => {
const res = await request(app).delete('/api/products/9999');
expect(res.statusCode).toBe(404);
});
});
});
// app.test.js
const puppeteer = require('puppeteer');
let server;
let browser;
beforeAll((done) => {
server = http.createServer(app);
server.listen(3000, () => {
console.log('Test server running on port 3000');
done();
});
});
afterAll((done) => {
server.close(done);
});
beforeEach(async () => {
browser = await puppeteer.launch();
});
afterEach(async () => {
await browser.close();
});
test("should display 'Hello, Node!'", async () => {
const page = await browser.newPage();
await page.goto("http://localhost:3000");
const content = await page.content();
expect(content).toContain("Hello, world!");
});
- Node.js doesn't have built-in TypeScript support.
.ts
will have to compiled into.js
files before running them with Node.
npx tsc -p .
- During development, tools like
ts-node
can be used along withnodemon
to automatically build and run TypeScript files. - The Node.js ecosystem provides many TypeScript-first libraries and tools:
- Prisma
- NestJS
- TypeORM
- AdonisJS
- The
stream
module provides a set of classes for working with streaming data. It includes implementations of readable, writable, duplex, and transform streams.
- Event Emitters
- File System
- Command Line Apps
- Environment Variables
dotenv
process.env
- Testing Web APIs
- Mock Tests using MSW
- HTTP Servers
http
- Fastify, Nest.js, Adonis.js
axios
- Auth
- Auth.js / Passport.js
- Template Engines
ejs
pug
marko
- [[Databases]]
- Logging
- Log: Winston
- Threads
- Streams
-
AdonisJS
-
Express
-
Koa