📜 ⬆️ ⬇️

19 unexpected finds in the Node.js documentation

I would like to think that I quite well know Node. For three years now, as none of the sites I have been working on, it cannot do without it. But so far I have not read the documentation properly.

I like to write useful things about interfaces, properties, methods, functions, data types, and everything else related to web development. So I fill in the gaps in knowledge. Now I'm busy with the documentation for Node.js, and before that I worked on HTML, DOM, Web API, CSS, SVG and EcmaScript.

image

Reading the Node.js documentation revealed a lot of great things to me that I didn’t know before. I want to share them in this little material. I'll start with the most interesting. I also usually do when I show my gadgets to my new acquaintance.

1. Module querystring as a universal parser


Let's say you got the data from some eccentric database, which produced an array of key / value pairs in about the form:
')
name:Sophie;shape:fox;condition:new . It is quite natural to assume that this can be easily converted to a JavaScript object. Therefore, you create an empty object, then an array, dividing the string by the symbol “ ; ". Next - loop through each element of this array, break the lines again, now by the “ : ” symbol. As a result, the first element received from each line becomes the name of the property of the new object, the second one - the value.

Is that right?

No, not right. In this situation, it suffices to use the querystring .

 const weirdoString = `name:Sophie;shape:fox;condition:new`; const result = querystring.parse(weirdoString, `;`, `:`); // : // { //   name: `Sophie`, //   shape: `fox`, //   condition: `new`, // }; 

2. Debugging: V8 Inspector


If you run Node with the --inspect key, it will report the URL. Go to this address in Chrome. And now - a pleasant surprise. We are available to debug Node.js using the Chrome developer tools. Happy times are here. Here is a guide on this topic from Paul Irish.

It should be noted that this function still has the status of an experimental one, but I gladly use it, and so far it has not failed me.

3. Difference between nextTick and setImmediate


As is the case with many other software mechanisms, remembering the difference between these two functions is very easy if you give them more meaningful names.

So, the process.nextTick() function should be called process.sendThisToTheStartOfTheQueue() . And setImmediate() - sendThisToTheEndOfTheQueue() .

By the way, here is a useful material about the optimization of nextTick starting from Node v0.10.0. A small retreat. I always thought that in React props should be called stuffThatShouldStayTheSameIfTheUserRefreshes , and state - stuffThatShouldBeForgottenIfTheUserRefreshes . The fact that these names have the same length, consider a good match.

4. Server.listen accepts an object with parameters


I am an adherent of passing parameters as an object, for example, with the name “options”, and not an approach, when a bunch of parameters are expected at the input to a function, which, moreover, have no names, and they should also be arranged in a strictly defined order. As it turned out, when setting up the server to listen to requests, you can use an object with parameters.

 require(`http`) .createServer() .listen({   port: 8080,   host: `localhost`, }) .on(`request`, (req, res) => {   res.end(`Hello World!`); }); 

This useful feature is well hidden. In the documentation for http.Server about her - not a word. However, it can be found in the description of net.Server , which is http.Server .

5. Relative file paths


The path in the file system, which is passed to the fs module, can be relative. The starting point is the current working directory returned by process.cwd() . Probably, this is what everyone knows, but I always thought that I could not do without full ways.

 const fs = require(`fs`); const path = require(`path`); //     ... fs.readFile(path.join(__dirname, `myFile.txt`), (err, data) => { //  -  }); //     ? fs.readFile(`./path/to/myFile.txt`, (err, data) => { //  -  }); 

6. Parsing files


Usually, when I needed to get his name and extension out of the file path, I used regular expressions. Now I understand that there is absolutely no need for this. The same can be done by standard means .

 myFilePath = `/someDir/someFile.json`; path.parse(myFilePath).base === `someFile.json`; // true path.parse(myFilePath).name === `someFile`; // true path.parse(myFilePath).ext === `.json`; // true 

7. Coloring logs in the console


I’ll pretend that I didn’t know that the console.dir(obj, {colors: true}) construct allows objects to be displayed in the console with properties and values ​​highlighted in color. This makes it easier to read logs.

8. control setInterval ()


For example, you use setInterval() to clean the database once a day. By default, the Node event loop will not stop as long as there is code that is scheduled to be executed using setInterval() . If you want to give Node a rest (I don’t know, in fact, what advantages you can get from this), use the unref() function.

 const dailyCleanup = setInterval(() => { cleanup(); }, 1000 * 60 * 60 * 24); dailyCleanup.unref(); 

However, it is worth being careful here. If the Node is no longer busy (for example, there is no http server waiting for connections), it will exit.

9. Process completion constants


If you like to kill, then you probably already did:

 process.kill(process.pid, `SIGTERM`); 

I can’t say anything bad about this design. But what if there was a mistake in the command caused by a typo? In the history of programming such cases are known. The second parameter here must be a string or a corresponding integer, so it is not surprising to write something wrong. In order to insure against errors, you can do this:

 process.kill(process.pid, os.constants.signals.SIGTERM); 

10. Verify IP Addresses


Node.js has a built-in tool for checking IP addresses . I used to write regular expressions more than once to do this. For more intelligence was not enough. Here's how to do it right:

 require(`net`).isIP(`10.0.0.1`) 

will return 4 .

 require(`net`).isIP(`cats`) 

will return 0 .

That's right, cats are not IP addresses.

You may have noticed that in the examples I use single quotes for strings. I like to do that, but I suspect that it looks strange, so I consider it necessary to mention this, although I myself don’t really know why. In general, this is my style.

11. End of line character, os.EOL


Have you ever asked for a line ending character? Yes? Everything, put out the light. Here, especially for those who did, a wonderful thing: os.EOL . On Windows, this will give \r\n , on all other operating systems - \n . os.EOL will ensure uniform code behavior in different operating systems.

Here I will make an amendment, since at the time of writing this article, it was not enough to go into this topic. Readers of the previous version of this post pointed out to me that using os.EOL can lead to trouble. The fact is that here it is necessary to proceed from the assumption that in a certain file either CRLF ( \r\n ) or LF ( \n ) can be used, but one cannot be completely sure of such an assumption.

If you have an open source project, and you want to forcefully use a specific line feed, here is the eslint rule , which, in part, can help. True, it is useless if Git works with texts.

And yet, os.EOL is not a useless toy. For example, this thing may be useful when generating log files that are not planned to be transferred to other operating systems. In this case, os.EOL ensures that such files are correctly displayed, for example, for viewing which is used Notepad in Windows Server.

 const fs = require(`fs`); //      CRLF fs.readFile(`./myFile.txt`, `utf8`, (err, data) => { data.split(`\r\n`).forEach(line => {   //  -  }); }); //       const os = require(`os`); fs.readFile(`./myFile.txt`, `utf8`, (err, data) => { data.split(os.EOL).forEach(line => {   //  -  }); }); 

12. HTTP status codes


Node has a “directory” with HTTP status codes and their names. I'm talking about the http.STATUS_CODES object. His keys are state codes, and the values ​​are their names.


Http.STATUS_CODES object

Here's how to use it:

 someResponse.code === 301; // true require(`http`).STATUS_CODES[someResponse.code] === `Moved Permanently`; // true 

13. Prevent unnecessary server shutdowns.


It has always seemed a little strange to me that a code like the one below causes the server to stop.

 const jsonData = getDataFromSomeApi(); //   !  ! const data = JSON.parse(jsonData); //    . 

In order to prevent such nonsense, right at the beginning of the application for Node.js you can put such a construction, which displays unhandled exceptions to the console:

 process.on(`uncaughtException`, console.error); 

Of course, I am in my right mind, so I use PM2 and wrap everything I can in try…catch blocks when I program to order, but in home projects ...

I want to pay special attention to the fact that this approach in no way belongs to the “ best practical development methods ”, and its use in large and complex applications is probably a bad idea. Decide for yourself whether to trust a post on a blog written by some dude or official documentation.

14. A few words about once ()


In addition to the on() method, EventEmitter objects EventEmitter have a once() method. I am quite sure that I am the last person on Earth who has learned about it. Therefore, I limit myself to a simple example, which everyone will understand.

 server.once(`request`, (req, res) => res.end(`No more from me.`)); 

15. Customizable console


The console can be configured using the following design, passing it its own output streams:

 new console.Console(standardOut, errorOut) 

What for? I do not know for sure. Maybe you want to create a console that outputs data to a file, or to a socket, or somewhere else.

16. DNS queries


One bird whistled to me here that Node did not cache the results of DNS queries. Therefore, if you refer to a certain URL several times, priceless milliseconds are spent on requests, without which it would be possible to do. In this case, you can query the DNS yourself, using dns.lookup() , and cache the results. Or, use the dnscache package, which does the same.

 dns.lookup(`www.myApi.com`, 4, (err, address) => { cacheThisForLater(address); }); 

17. Module fs: minefield


If your programming style is similar to mine, that is, it is something like: “I will read a piece of documentation diagonally and will mess around with the code until it works,” then you are not insured against problems with the fs . The developers have done a lot of work aimed at unifying the interaction of Node with various operating systems, but their capabilities are not unlimited. As a result, features of various operating systems break the ocean surface of the code as sharp reefs, which are also mined. And you in this drama play the role of a boat that can sit on one of the reefs.

Unfortunately, the differences related to fs are not reducible to the usual: “Windows and everyone else”, so we cannot just brush it off, hiding behind the idea: “yes, who uses Windows”. (I first wrote here a whole speech about anti-Windows sentiment in web development, but in the end I decided to remove it, otherwise I got my eyes on my forehead from this my sermon).

Here, in brief, what I found in the documentation for the fs module. I am sure that these revelations can bite somebody as well as a roasted rooster.


(I’m not keeping up with fashion here, I’m calling Apple’s OS “macOS”, although it’s not even two months after the old name, OS X, passed away).

18. The net module is twice as fast as the http module


Reading the documentation for Node.js, I realized that the net module is a thing. It is the basis of the http module. It made me think that if you need to organize the interaction of servers (as it turned out, I needed it), should I use the net module only?

Those who are closely involved in networking systems may not believe that such a question should be asked at all, but I am a web developer who has suddenly fallen into the world of servers and knows only HTTP and nothing else. All these TCP, sockets, all this talk about threads ... For me, it's like Japanese rap . That is, it seems to me incomprehensible, but it sounds intriguing.

In order to sort things out, experiment with net and http , and compare them, I set up a couple of servers (I hope you are listening to Japanese rap) and loaded them with requests. As a result, http.Server was able to process approximately 3400 requests per second, and net.Server - approximately 5500. In addition, net.Server easier to net.Server .

Here, if you are interested, the code of clients and servers with which I experimented. If you're not interested, please apologize for having to scroll the page for so long.

Here is the client.js code.

 //    .  –  TCP-,  –  HTTP (    server.js). //         . //       . const net = require(`net`); const http = require(`http`); function parseIncomingMessage(res) { return new Promise((resolve) => {   let data = ``;   res.on(`data`, (chunk) => {     data += chunk;   });   res.on(`end`, () => resolve(data)); }); } const testLimit = 5000; /*  ------------------  */ /*  --  NET client  --  */ /*  ------------------  */ function testNetClient() { const netTest = {   startTime: process.hrtime(),   responseCount: 0,   testCount: 0,   payloadData: {     type: `millipede`,     feet: 100,     test: 0,   }, }; function handleSocketConnect() {   netTest.payloadData.test++;   netTest.payloadData.feet++;   const payload = JSON.stringify(netTest.payloadData);   this.end(payload, `utf8`); } function handleSocketData() {   netTest.responseCount++;   if (netTest.responseCount === testLimit) {     const hrDiff = process.hrtime(netTest.startTime);     const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;     const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();     console.info(`net.Server handled an average of ${requestsPerSecond} requests per second.`);   } } while (netTest.testCount < testLimit) {   netTest.testCount++;   const socket = net.connect(8888, handleSocketConnect);   socket.on(`data`, handleSocketData); } } /*  -------------------  */ /*  --  HTTP client  --  */ /*  -------------------  */ function testHttpClient() { const httpTest = {   startTime: process.hrtime(),   responseCount: 0,   testCount: 0, }; const payloadData = {   type: `centipede`,   feet: 100,   test: 0, }; const options = {   hostname: `localhost`,   port: 8080,   method: `POST`,   headers: {     'Content-Type': `application/x-www-form-urlencoded`,   }, }; function handleResponse(res) {   parseIncomingMessage(res).then(() => {     httpTest.responseCount++;     if (httpTest.responseCount === testLimit) {       const hrDiff = process.hrtime(httpTest.startTime);       const elapsedTime = hrDiff[0] * 1e3 + hrDiff[1] / 1e6;       const requestsPerSecond = (testLimit / (elapsedTime / 1000)).toLocaleString();       console.info(`http.Server handled an average of ${requestsPerSecond} requests per second.`);     }   }); } while (httpTest.testCount < testLimit) {   httpTest.testCount++;   payloadData.test = httpTest.testCount;   payloadData.feet++;   const payload = JSON.stringify(payloadData);   options[`Content-Length`] = Buffer.byteLength(payload);   const req = http.request(options, handleResponse);   req.end(payload); } } /*  --  Start tests  --  */ // flip these occasionally to ensure there's no bias based on order setTimeout(() => { console.info(`Starting testNetClient()`); testNetClient(); }, 50); setTimeout(() => { console.info(`Starting testHttpClient()`); testHttpClient(); }, 2000); 

Here is server.js .

 //    .  – TCP,  – HTTP. //          JSON,      ,       . const net = require(`net`); const http = require(`http`); function renderAnimalString(jsonString) { const data = JSON.parse(jsonString); return `${data.test}: your are a ${data.type} and you have ${data.feet} feet.`; } /*  ------------------  */ /*  --  NET server  --  */ /*  ------------------  */ net .createServer((socket) => {   socket.on(`data`, (jsonString) => {     socket.end(renderAnimalString(jsonString));   }); }) .listen(8888); /*  -------------------  */ /*  --  HTTP server  --  */ /*  -------------------  */ function parseIncomingMessage(res) { return new Promise((resolve) => {   let data = ``;   res.on(`data`, (chunk) => {     data += chunk;   });   res.on(`end`, () => resolve(data)); }); } http .createServer() .listen(8080) .on(`request`, (req, res) => {   parseIncomingMessage(req).then((jsonString) => {     res.end(renderAnimalString(jsonString));   }); }); 

19. Tricks of the REPL mode


  1. If you are working in the REPL mode, that is, you wrote a node in the terminal and pressed Enter , you can enter a command like .load someFile.js and the system will load the requested file (for example, a bunch of constants can be specified in such a file).

  2. In this mode, you can set the environment variable NODE_REPL_HISTORY="" in order to disable the recording of history to a file. In addition, I learned (at least - remembered) that the REPL history file, which allows you to travel to the past, is stored at ~/.node_repl_history .

  3. The underscore character " _» is the name of a variable that stores the result of the last expression executed. I think it can be useful.

  4. When the Node starts up in REPL mode, the modules are loaded automatically (more precisely, on request). For example, you can simply type os.arch() on the command line to find out the architecture of the OS. A construct like require(`os`).arch(); need not.

Results


As you can see, reading the documentation is a good thing. Many new can be found even in the area, which, like, you know along and across. I hope you find my finds useful.

By the way, do you know anything else interesting about Node.js? If so - share :)

Source: https://habr.com/ru/post/318322/


All Articles