Key-Value Store Using Node.js and Redis

In this tutorial, we'll create a simple key-value store application using Node.js and Redis. This application will allow you to set and get key-value pairs via HTTP requests. Additionally, we'll place this Node.js application behind an NGINX reverse proxy to demonstrate a common production setup.

Overview: System Architecture

drawing

This system facilitates setting and retrieving key-value pairs through HTTP requests. To mimic a realistic production environment, the Node.js application is configured to operate behind an NGINX reverse proxy. The system architecture comprises the following components:

  • Node.js Application: Serves as the core of our key-value store, providing an API to set and get key-value pairs.

  • Redis Server: Acts as the data storage layer, optimized for high-performance key-value data operations. Redis stores the data set and retrieves operations requested by the Node.js application.

  • NGINX Server: Configured as a reverse proxy, NGINX forwards incoming HTTP requests to the Node.js application. This layer adds an additional level of security and load balancing, making the application more robust and scalable.

The integration of these components creates a seamless workflow where NGINX handles incoming requests and directs them to the Node.js application, which in turn interacts with Redis to perform data operations. This setup is indicative of modern web application architectures, offering scalability, flexibility, and enhanced security.

Data Storage: Setting up Redis

Start a Redis server on UCloud. We assume Redis is served on the default port 6379.

Web Server: Setting up NGINX

Start a new NGINX web server on UCloud with the optional parameters:

  • Connect to other jobs: Select the Job ID of the running Redis instance and specify a hostname.

  • Configure custom links to your application: Add a public URL which will be used to connect to the key-value store.

In the following we will fix the parameters:

  • Redis hostname: redis-server

  • Public link: app-mykvstore.cloud.sdu.dk

Node.js Connectivity: Establishing the Interface

Open a terminal window inside the running NGINX instance.

Initialize the project

Create a new directory for the project and initialize a new Node.js application:

$ mkdir key-value-store && cd key-value-store
$ npm init -y

Install dependencies

Install express for the web server framework and redis for interacting with Redis:

$ npm install express redis

Create the application

In the project directory, open a terminal editor and create a new file named app.js with the following content:

const express = require('express');
const redis = require('redis');
const app = express();
app.use(express.json());

const redisClient = redis.createClient({
    url: 'redis://redis-server:6379'
});
redisClient.on('error', (err) => console.log('Redis Client Error', err));
redisClient.connect();

app.post('/set', async (req, res) => {
    const { key, value } = req.body;
    await redisClient.set(key, value);
    res.send(`Key ${key} set with value ${value}`);
});

app.get('/get/:key', async (req, res) => {
    const { key } = req.params;
    const value = await redisClient.get(key);
    if (value) {
        res.send(`Value for key ${key} is ${value}`);
    } else {
        res.send(`Key ${key} not found`);
    }
});

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

The app is served by default on port 3000.

Note

The connection string to the Redis server contains the hostname (in this case redis-server) defined when we submitted the NGINX instance.

Run the application

Start the Node.js application with:

$ nohup node app &

The application runs in background.

NGINX Configuration: Proxy Setup

Edit the default NGINX configuration file /etc/nginx/nginx.conf to reverse proxy requests to the Node.js application:

# /etc/nginx/nginx.conf
worker_processes auto;
error_log /dev/stdout info;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;
}

http {
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';

    access_log /var/log/nginx/access.log main;

    server {
        listen 8080 so_keepalive=on;

        location / {
            proxy_pass http://localhost:3000;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

After configuring NGINX, remember to reload the NGINX configuration:

$ nginx -t
$ nginx -s reload

Evaluation: Testing the Store

Open a terminal window on your local host and use curl to test the set and get functionalities.

  1. To set a value:

    $ curl -X POST https://app-mykvstore.cloud.sdu.dk/set -H "Content-Type: application/json" -d '{"key":"myKey", "value":"myValue"}'
    
  2. To get a value:

    $ curl https://app-mykvstore.cloud.sdu.dk/get/myKey
    

Conclusion: Reflecting on the Build

We have created a simple key-value store application using Node.js and Redis, accessible through an NGINX reverse proxy. This setup demonstrates how to integrate a Node.js application with a Redis data store running on UCloud and how to use NGINX to manage incoming traffic from a remote host to your application.