Deno World
Published in

Deno World

Native GET and SET operations on REDIS

Redis (Remote Dictionary Server) is an in-memory data structure store, used as a distributed, in-memory key–value database, cache and message broker, with optional durability. Redis supports different kinds of abstract data structures, such as strings, lists, maps, sets, sorted sets, HyperLogLogs, bitmaps, streams, and spatial indices.

Redis popularized the idea of a system that can be considered at the same time a store and a cache, using a design where data is always modified and read from the main computer memory, but also stored on disk in a format that is unsuitable for random access of data, but only to reconstruct the data back in memory once the system restarts.

According to monthly DB-Engines rankings, Redis is often the most popular key–value database.

Redis server is accessible through a TCP connection. The server takes commands, executes them, and returns the result.

In this article, we’ll perform basic GET <key> and SET <key> <val> operations natively, i.e. without using any frameworks.

To know about writing a TCP client in Deno, please visit an article here.

Interface

A client can connect to a Redis server by establishing a TCP connection to the default Redis server port, 6379.

Redis clients communicate with the Redis server using a protocol called RESP (REdis Serialization Protocol). The RESP protocol is documented here. While RESP is technically non-TCP specific, in the context of Redis the protocol is only used with TCP connections (or equivalent stream oriented connections like Unix sockets). In the scope of this article, we’ll look at two heavily used RESP commands:

GET <key>

The GET <key> command gets the value of the given key. If the key does not exist, the special value nil is returned. The response is textual and would need some level of basic parsing to extract the data.

Request: 
GET key1
Response:
$4
val1
Request:
GET key2
Response:
$-1

SET <key> <value>

The SET <key> <value> command sets the key to a given value. If the key already holds a value, it is overwritten. If the key gets set correctly, a simple OK is returned.

Request:
SET key1 val1
Response:
+OK

Binary data

As we’ll be working with low-level APIs like connect, read, & write, we’ll need to work with binary data (Uint8Array). It’ll be useful to write utility functions that convert string to bytes and vice versa.

const encoder = new TextEncoder(),
decoder = new TextDecoder();
const enc = (d: string) => encoder.encode(d),
dec = (b: Uint8Array) => decoder.decode(b);
const CRLF = "\r\n";

The enc utility function convert string to bytes, while the dec utility function converts bytes to string.

Establishing connection

The Deno.connect API can be used to establish a TCP connection with Redis server. The TCP connection would remain active so that the requests gets served as fast as possible. The Redis server exposes a TCP server on port 6379.

In the following example, Redis server is running on localhost

const conn = await Deno.connect({ port: 6379 });

SET request

The SET request takes two inputs:

  • key: The key to set in the database
  • value: The value for the key to set in the database

A SET request is a simple CRLF terminated string that contains SET <key> <value>. The SET request can be sent using write API.

const key = "key1", val = "val1";
await conn.write(enc(`SET ${key} ${val}${CRLF}`));

Once the SET request is sent out, we need to wait for a response using read API. The response to a successful SET is always ‘+OK’, so there is no need to do any parsing.

const n=await conn.read(buf) || 0;
if (dec(buf.slice(0, n)).replaceAll(CRLF, "") == "+OK") {
console.log(`${key}=${val} has been set`);
}

The following is the complete code of a function to set a key in Redis. The set function returns true if the key got set, false otherwise.

const set = async (
conn: Deno.Conn,
key: string,
val: string,
): Promise<Boolean> => {
const buf = new Uint8Array(50);
await conn.write(enc(`SET ${key} ${val}${CRLF}`));
const n = await conn.read(buf) || 0;
if (dec(buf.slice(0, n)).replaceAll(CRLF, "") == "+OK") {
return true;
}
return false;
};

The following code uses the set function to set a key in Redis:

const key = "key1", val = "val1", keyInexistent = "key2";
if (await set(conn, key, val)) {
console.log(`${key} has been set`);
} else {
console.error(`${key} failed to set`);
}
//key1 has been set

GET request

The GET request takes a single input:

  • key: The key to set in the database

As the case with SET, a GET request is also a simple CRLF terminated string that contains GET <key>. The GET request can be sent using write API.

const CRLF = "\r\n";const key = "key1";
await conn.write(enc(`GET ${key}${CRLF}`));

Once the GET request is sent out, we need to wait for a response using read API. The GET response could be empty ($-1) or a string that is the value of the requested key ($<length>\r\n<value>). The response of the GET request needs a bit of parsing to extract the value.

const n = await conn.read(buf) || 0;
const r = dec(buf.slice(0, n)).split(CRLF).slice(0, -1);
if (r[0] === "$-1") {
//key not found
}
r[1]; //the value of the key

The following is the complete code of a function to get a key from Redis. The get function returns the value if the key was present in Redis, undefined otherwise.

const get = async (
conn: Deno.Conn,
key: string,
): Promise<string | undefined> => {
const buf = new Uint8Array(50);
await conn.write(enc(`GET ${key} ${CRLF}`));
const n = await conn.read(buf) || 0;
const r = dec(buf.slice(0, n)).split(CRLF).slice(0, -1);
if (r[0] === "$-1") {
return;
}
if (!r[1]) {
return;
}
return r[1];
};

The following code uses the get function to get a couple of keys from Redis (one exists, one doesn’t):

await get(conn, "key1"); //val1
await get(conn, "key2"); //undefined

Complete code

Now that we’ve understood how to access Redis natively, let’s take a look at a complete example. The following is the complete code to set and get data from Redis:

const encoder = new TextEncoder(),
decoder = new TextDecoder();
const enc = (d: string) => encoder.encode(d),
dec = (b: Uint8Array) => decoder.decode(b);
const CRLF = "\r\n";
const set = async (
conn: Deno.Conn,
key: string,
val: string,
): Promise<Boolean> => {
const buf = new Uint8Array(50);
await conn.write(enc(`SET ${key} ${val}${CRLF}`));
const n = await conn.read(buf) || 0;
if (dec(buf.slice(0, n)).replaceAll(CRLF, "") == "+OK") {
return true;
}
return false;
};
const get = async (
conn: Deno.Conn,
key: string,
): Promise<string | undefined> => {
const buf = new Uint8Array(50);
await conn.write(enc(`GET ${key} ${CRLF}`));
const n = await conn.read(buf) || 0;
const r = dec(buf.slice(0, n)).split(CRLF).slice(0, -1);
if (r[0] === "$-1") {
return;
}
if (!r[1]) {
return;
}
return r[1];
};
const conn = await Deno.connect({ port: 6379 });const key = "key1", val = "val1", keyInexistent = "key2";
if (await set(conn, key, val)) {
console.log(`${key} has been set`);
} else {
console.error(`${key} failed to set`);
}
console.log(await get(conn, key));
console.log(await get(conn, keyInexistent));

A sample run of the above code:

$ deno run --allow-net=:6379 app.ts 
key1 has been set
val1
undefined

This story is a part of the exclusive medium publication on Deno: Deno World.

--

--

--

The one and only exclusive medium magazine with 200+ articles on Deno

Recommended from Medium

Without a Commute to Work, I Am Using That Time to Improve Myself

Advance Java Interview Questions

Docker Tutorial for Beginner

Nebula Update & Applications

JS Design Patterns

First view on Flutter (an Android Developer View)

Is your database GDPR proof? How can we use python and NLP tools to check it?

How to get all file names that are in a folder to a text file. [WINDOWS]

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Mayank Choubey

Mayank Choubey

Deno, Node.js, etc.

More from Medium

File upload with encryption at rest

Run and debug Deno applications in VSCode

Deno nuggets: TCP client

Deno nuggets: Making GET & POST requests