Michael Charles Aubrey
Posted on November 13, 2023
Vercel KV (Key-Value) is an innovative offering from Vercel providing a straightforward serverless key-value store solution. It is on top of Redis using Upstash's Redis capabilities, and Vercel provides their own Node library, @vercel/kv
for interacting with it. The @vercel/kv
library is perfect for implementing key-value storage as a part of your NextJS Serverless edge functions.
Why Not Just Use @vercel/kv
?
Or rather it is perfect, so long as the library provides access to the Redis functions you need. Behind Vercel KV is a full Redis instance, with compatibility for Redis client protocol up to v6.2. However, the provided library doesn't necessarily implement every feature available in the underlying Redis instance.
If you can make do with what the library provides, you're probably better off sticking to it. If not, however, you're in luck! Since Vercel KV is just powered by a standard Redis instance, you can use redis
(or any other Redis library of your choosing) to connect to it.
The process is pretty straightforward, but as I couldn't find any other articles online talking about this, I figured I'd write something out.
Pricing
Before we continue, you should review and be aware of the pricing for Vercel KV. It is an affordable pay-as-you-go model, and depending on your usage you might only pay as little as $1 USD a month. However, as these prices are subject to change, I recommend reviewing the pricing yourself before we continue.
Setting Up Vercel KV
First set up Vercel KV itself. You need to create a new Vercel KV storage solution. After logging into Vercel, click on "Storage" in the top menu. This should show you a few storage options that you can create.
This will present you with a dialog to fill out your desired database name and region. Fill those out and you'll be presented with your newly created Vercel KV Redis instance!
Click on "Show Secret" and it will reveal the following string which can be used for connecting to the Redis instance via the redis-cli
command line tool.
redis-cli --tls -u redis://default:597961c4b7344b5f9ce9691ae5fb8bd4@dear-chigger-38419.upstash.io:38419
We're interested in the part after the -u
, that is your Redis connection string which we will use later for connecting to the instance via the redis
node library, so copy that down to a secure location.
Adding Redis to Your Existing NextJS Project
I'm going off the assumption that you're adding this to an existing project. If that is not the case, then feel free to follow any getting started guide for NextJS. There isn't any special sauce needed in the setup.
After you've got your NextJS project, let's install the redis
library.
npm i redis
Environment Variables
You'll want to give redis
access to the connection string you previously copied to a safe location. For that, add a new environment variable to .env.local
like so,
REDIS_URL=redis://default:597961c4b7344b5f9ce9691ae5fb8bd4@dear-chigger-38419.upstash.io:38419
Remember that you need to replace the connection string above with the one that you copied down from your Vercel dashboard.
Redis Configuration
I'm putting this in its own section for readability and in case someone is just scanning this article, but here's what you need.
const REDIS_CONFIG = {
url: process.env.REDIS_URL,
socket: {
tls: true,
},
};
If you already know what you're doing, you just need to pass that configuration into the createClient
function, and you're golden. It is important that tls
be set to true
, otherwise even if you specify the connection string appropriately it won't work. That's all there is to it, really.
If you're still lost, move on to the next section.
Redis Boilerplate
You don't strictly need to use my boilerplate, but I'm using it in a current project and I think it offers an efficient way to provide the Redis client via a Singleton. Here's the code with the configuration already in place. In my project I'm placing this in ./redis/index.ts
.
import { createClient, RedisClientType } from "redis";
const REDIS_CONFIG = {
url: process.env.REDIS_URL,
socket: {
tls: true,
},
};
class RedisService {
private static instance: RedisService;
private client: RedisClientType;
private constructor() {
this.client = createClient(REDIS_CONFIG);
this.client.on("error", (err) => console.log("Redis Client Error", err));
this.client.connect();
}
public static getInstance(): RedisService {
if (!RedisService.instance) {
RedisService.instance = new RedisService();
}
return RedisService.instance;
}
getClient() {
return this.client;
}
}
export const getRedisClient = () => RedisService.getInstance().getClient();
The above exports a function getRedisClient
, which will return a Redis client hooked up to your Vercel KV Redis instance.
Using Your Redis Client
I assume if you're here, you're here because there's some Redis functionality that you want to implement, and therefore probably don't need this section. However, if you're just here out of curiosity and want something to try, here is a simple example of an increment/decrement function implemented via Redis, using our exported getRedisClient
function.
import { getRedisClient } from "..";
const COUNTER_NAMESPACE = "counter";
export const incrementCount = async (
increment: number = 1
): Promise<number> => {
const client = getRedisClient();
try {
const newCount = await client.incrBy(</span><span class="p">${</span><span class="nx">COUNTER_NAMESPACE</span><span class="p">}</span><span class="s2">
, increment);
return newCount;
} catch (error) {
console.error(error);
return 0;
}
};
export const decrementCount = async (
decrement: number = 1
): Promise<number> => {
const client = getRedisClient();
try {
const newCount = await client.decrBy(</span><span class="p">${</span><span class="nx">COUNTER_NAMESPACE</span><span class="p">}</span><span class="s2">
, decrement);
if (newCount < 0) {
await client.set(</span><span class="p">${</span><span class="nx">COUNTER_NAMESPACE</span><span class="p">}</span><span class="s2">
, "0");
return 0;
}
return newCount;
} catch (error) {
console.error(error);
return 0;
}
};
export const getCount = async (): Promise<number> => {
const client = getRedisClient();
try {
const count = await client.get(</span><span class="p">${</span><span class="nx">COUNTER_NAMESPACE</span><span class="p">}</span><span class="s2">
);
if (count) {
return parseInt(count);
}
return 0;
} catch (error) {
console.error(error);
return 0;
}
};
Working Example
I've uploaded a working example to GitHub. You can access it here, pull the repo, and try it out for yourself. Remember that in order to use the repo, you will need to create a Vercel KV instance, get the connection string, and supply it to an .env.local
file in the cloned repository.
Posted on November 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.