Node.js Tips — Redis, Query Strings, Concurrency of Promises, SQL Injections

John Au-Yeung - Jan 26 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

Like any kind of apps, there are difficult issues to solve when we write Node apps.

In this article, we’ll look at some solutions to common problems when writing Node apps.

Best Way to Limit Concurrency when Using ES6’s Promise.all()

To limit concurrency when using Promise.all , we can use the es6-promise-pool package.

For instance, we can write:

const promiseProducer = function * () {
  for (let count = 1; count <= 5; count++) {
    yield delayValue(count, 1000)
  }
}

const promiseIterator = generatePromises();
const pool = new PromisePool(promiseIterator, 3);

pool.start()
  .then(function () {
    console.log('Complete')
  })
Enter fullscreen mode Exit fullscreen mode

We get the values for the promises in the promiseProducer generator function.

Then we call the generator function so that we get an iterator.

We then pass that into the PromisePool constructor.

3 is the number of concurrent promises allowed.

Finally, we call pool.start to invoke the promises.

Remove All Files from Directory without Removing Directory in Node.js

We can remove all files in a directory without removing the directory by using the readdir method to read the directory’s contents.

Then we can loop through each file and call unlink on them.

For instance, we can write:

const fs = require('fs');
const path = require('path');

const directory = '/foo/bar';

fs.readdir(directory, (err, files) => {
  if (err){
    return console.log(err);
  }

  for (const file of files) {
    fs.unlink(path.join(directory, file), err => {
      if (err) {
        console.log(err);
      }
    });
  }
});
Enter fullscreen mode Exit fullscreen mode

We call readdir to read the directory’s content. Then we use the for-of loop to loop through the items.

Then we call unlink to remove each file.

Redis and Node.js

We can access a Redis database by using the redis library.

To install it, we run:

npm install redis
Enter fullscreen mode Exit fullscreen mode

Then in our Node app, we can use the Redis client by writing:

const redis = require("redis");
const client = redis.createClient();

client.on("error", (err) => {
  console.log(err);
});

client.set("key", "val", redis.print);
client.hset("hash key", "foo", "value 1", redis.print);
client.hset(["hash key", "bar", "value 2"], redis.print);
client.hkeys("hash key", (err, replies) => {
  replies.forEach((reply, i) => {
    console.log(i, reply);
  });
  client.quit();
});
Enter fullscreen mode Exit fullscreen mode

We use the redis module and call createClient to create the client.

Then we attach a listener to listen to the 'error' event.

Then we call set to set the key and value.

We can use hset to set a hash key with their own value.

redis.print prints the values.

hkeys get the hash keys and print the index and results.

Parsing Query String in Node.js

We can parse the query string in Node.jhs with the url module.

For instance, if we’re using the http module to create our server, we can write:

const http = require('http');
const url = require('url');

const server = http.createServer((request, response) => {
  const queryData = url.parse(request.url, true).query;
  response.writeHead(200, { "Content-Type": "text/plain" });

  if (queryData.name) {
    response.end(queryData.name);
  } else {
    response.end("hello worldn");
  }
});

server.listen(8000);
Enter fullscreen mode Exit fullscreen mode

We call url.parse to parse the query string that’s in the URL.

The request URL is stored in the request.url property.

The parsed result is assigned to the queryData constant.

Then we parse the name query parameter if it exists.

If make a request with query string ?name=joe , then queryData.name will be 'joe' .

Preventing SQL Injection in Node.js

To prevent SQL injection in Node apps, we should use a library that lets to make parameterized queries.

For instance, with the node-mysql-native library, we can write:

const userId = 5;
const query = connection.query('SELECT * FROM users WHERE id = ?', [userId], (err, results) => {
  //...
});
Enter fullscreen mode Exit fullscreen mode

The ? indicates the parameter that we can pass in. The 2nd argument has the parameters.

The callback is called when the query is done.

results has the results.

Sanitization is done by the following rules:

  • numbers are untouched
  • booleans are converted to strings
  • date objects are converted to YYYY-mm-dd:ii:ss strings
  • buffers are converted to hex strings
  • strings are escaped
  • arrays are turned into lists
  • nested arrays are turned into grouped lists
  • objects are turned into key = 'val' pairs
  • undefined or null are converted to NULL
  • NaN and Infinity are left as is. They aren’t supported by MySQL and will error out if these are present.

Conclusion

We should use a library to make database queries to prevent SQL injection.

Also, we can use a library to limit the concurrency of parallel promises.

We can use Node’s Redis library to access Redis databases.

The url package can parse query strings.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .