The Crash Course on Node Redis
Today we’ll be learning how to use Redis in Node.js, however, I’m not going to dump the full documentation here and bore you to death.
Is this a beginner’s guide? — you ask
Well, yes and no. I’ll be talking about the key concepts of Redis which you can use to build almost any application. That includes both beginner and advanced concepts.
This guide is for the ones who want to say, “I know how to use Redis!” to the world.
Introduction
If you haven’t heard about Redis, this is the introduction taken from their website:
“Redis is an open source (BSD licensed), in-memory data structure store, used as a database, cache and message broker. (…)”
In short, it’s an extremely fast database. Some usage examples include caching, implementing leaderboards and high scores, real-time analysis (e.g. logging), etc.
At Speakrandom, we use Redis as:
- A back end messaging system (Pub/Sub), for Socket.io
- Managing volatile rooms that get inserted and deleted many times
- Basic autocompletion feature (for searching hashtags)
Connecting Redis to Node
First of all, you need a Redis instance you can connect to. Luckily, the nice fellas at have a free tier where you can get 30mb for free to try things out (as of this writing).
Once you have the database connection details, it’s time to set up Node.js to work with it. The two main packages you can use in Node.js are redis and ioredis. here, we’ll use the redis package as an example, but ioredis is very similar.
const redis = require("redis");
const client = redis.createClient(PORT, HOST, { auth_pass: PASSWORD }); // Replace PORT, HOST, and PASSWORD with the details you got from redislabs
client.set("key", "something", function (error, response) {
if (error) return console.error(error);
console.log(response);
}); // example of the SET command
Great, you’ve just added a key with a value of something into the database, good job.
To see if the key really is there, you can do
client.get("key", function (error, response) {
if (error) return console.error(error);
console.log(response);
}); // logs "something" into the console
This is all you need to get started.
Now, some people prefer using Promises (or async/await) instead of callbacks. For this, you can use the promisify utility that comes with Node.
To set up promisify, let’s take the example at the beginning:
const redis = require("redis");
const client = redis.createClient(PORT, HOST, { auth_pass: PASSWORD }); // Replace PORT, HOST, and PASSWORD with the details you got from redislabs
const { promisify } = require("util");
client.setAsync = promisify(client.set);
try {
const response = await client.setAsync("key", "something");
console.log(response);
} catch(error) {
console.error(error);
} // example of the SET command
Now you can use Promise callbacks or async/await in all the commands!
General guide for doing 80% of the work
You know by now how to connect Node to Redis, running Redis commands with the respective callbacks, and even turning those into Promises.
In this section, I’ll tell you the key concepts you need to learn to use Redis. With this, you’ll be able to do 80% of the work for common use-cases.
You should know at least these 3 concepts to use Redis correctly:
Data types
You can check the full, more in-depth list of these in the Redis docs, but I’ll sum it here:
- Strings: Just like we did at the beginning of this guide
- Lists: Remember the time where you needed to study for a job interview? Then for sure, you’ve come across Linked Lists. Yes, Redis uses Linked Lists as one of the main data types.
- Sets: They let you store unordered strings, and you can’t have duplicates here. You’ll use this for relations if you’re using Redis as a full-fledged DB.
- Hashes: The same as objects in JS
- Sorted sets: This is the same as sets, but this time ordered. Useful for keeping track of, for example, high scores or implement a dictionary (for autocomplete).
- Bitmaps and HyperLogLogs: These are used for logging, so not used for common use-cases.
Expiring keys
This is one of the main reasons Redis is known as the cache DB. In Redis, you can set each and every key to expire within a chose time-frame.
To expire a key, you’ll do:
client.expire("key", [TIME IN SECONDS]);
Multi & exec
This is another concept that is crucial to learn for both beginners and advanced developers.
Redis is considered single-threaded. Even when using a cluster, Redis acts in a single-thread manner. This means that each command sent to Redis follows a queue and never runs 2 commands at the same time. This prevents unexpected situations due to race conditions.
Bear this in mind, because multi and exec commands follow this principle.
Which problem do multi and exec solve?
Let’s suppose you want to add a key with the name “key” and set it to expire in 60 seconds. How would you do it?
Thinking about doing this?
client.set("key", "something", function (error, response) { ... });
client.expire("key", 60, function (error, response) { ... });
Although this works, you are wasting two callbacks, slowing down the program, and if your app is large enough, breaking it.
Let’s rewrite it to the following:
client
.multi()
.set("key", "something")
.expire("key", 60)
.exec(function (error, replies) { ... });
Did you get it? Between multi and exec, you can chain any command you want to execute as a batch.
And here comes the best part: remember when I said Redis is single-threaded?
Because of this, all the commands run from multi and exec are executed without anything else happening at the same time, so you can be sure that Redis stays the same at the moment of execution. No race conditions for Redis all right!
Other concepts worth taking a look in this topic are:
- The WATCH command: Let’s say you need to retrieve a value before executing the next command in multi. Watch lets you do a transaction and if the value you were grabbing changes, every action done is ignored.
- The EVAL command: This command lets you execute a script in Redis, taking advantage of both the customization and fast execution that Redis offers. The only downside is that it requires the scripts to be in Lua language.