Node 'redis'ライブラリを使用してVercel KVに接続する方法

Node 'redis'ライブラリを使用してVercel KVに接続する方法

Michael Charles Aubrey // Mon Nov 13 2023

Vercel KV(Key-Value)は、Vercelが提供する革新的なサービスで、シンプルなサーバーレスのキーバリューストアソリューションを提供します。UpstashのRedis機能を利用したRedis上に構築されており、Vercelは独自のNodeライブラリ @vercel/kv を提供して、これと対話するためのインターフェースを提供しています。@vercel/kvライブラリは、NextJSのサーバーレスエッジ関数の一部としてキーバリューストレージを実装するのに最適です。

なぜ @vercel/kv だけを使わないの?

より正確に言えば、ライブラリが必要なRedis機能へのアクセスを提供している限りは完璧なのです。Vercel KVの背後には完全なRedisインスタンスがあり、Redisクライアントプロトコルv6.2までの互換性があります。しかし、提供されているライブラリは、基盤となるRedisインスタンスで利用可能なすべての機能を必ずしも実装しているわけではありません。

ライブラリが提供する機能で十分であれば、それを使い続けるのが良いでしょう。しかし、それでは不十分な場合でも問題ありません!Vercel KVは標準的なRedisインスタンスで動作しているため、redis(または他のお好みのRedisライブラリ)を使用して接続することができます。

このプロセスはとても簡単ですが、オンラインでこれについて説明している記事が見つからなかったので、記事を書くことにしました。

料金体系

先に進む前に、Vercel KVの料金体系を確認し、理解しておく必要があります。これは従量課金制のモデルで、使用状況によっては月額1ドル程度で済む場合もあります。ただし、これらの価格は変更される可能性があるため、先に進む前に自分で料金を確認することをお勧めします。

Vercel KVのセットアップ

まずVercel KV自体をセットアップします。新しいVercel KVストレージソリューションを作成する必要があります。Vercelにログインした後、トップメニューの「Storage」をクリックします。これにより、作成可能ないくつかのストレージオプションが表示されます。

画像の説明

データベース名とリージョンを入力するダイアログが表示されます。これらを入力すると、新しく作成されたVercel KV Redisインスタンスが表示されます!

画像の説明

「Show Secret」をクリックすると、redis-cliコマンドラインツールを使用してRedisインスタンスに接続するための以下の文字列が表示されます。

redis-cli --tls -u redis://default:597961c4b7344b5f9ce9691ae5fb8bd4@dear-chigger-38419.upstash.io:38419

私たちが注目するのは -u の後の部分です。これが後でredisnodeライブラリを使用してインスタンスに接続する際に使用するRedis接続文字列なので、安全な場所にコピーしておいてください。

既存のNextJSプロジェクトにRedisを追加する

ここでは既存のプロジェクトに追加することを前提としています。もしそうでない場合は、NextJSの入門ガイドに従ってください。セットアップに特別な手順は必要ありません。

NextJSプロジェクトができたら、redisライブラリをインストールしましょう。

npm i redis

環境変数

以前安全な場所にコピーした接続文字列にredisがアクセスできるようにする必要があります。そのために、.env.localに新しい環境変数を以下のように追加します。

REDIS_URL=redis://default:597961c4b7344b5f9ce9691ae5fb8bd4@dear-chigger-38419.upstash.io:38419

上記の接続文字列は、Vercelダッシュボードからコピーした自分の接続文字列に置き換える必要があることを忘れないでください。

Redis設定

読みやすさのため、また記事を流し読みする人のために、これを独立したセクションに分けました。以下が必要な設定です:

const REDIS_CONFIG = {
  url: process.env.REDIS_URL,
  socket: {
    tls: true,
  },
};

既に何をすべきか分かっている場合は、この設定をcreateClient関数に渡すだけで完了です。tlstrueに設定することが重要で、これがないと接続文字列を適切に指定しても機能しません。実際、それだけです。

まだ分からない場合は、次のセクションに進んでください。

Redisボイラープレート

私のボイラープレートを使用する必要は必ずしもありませんが、現在のプロジェクトで使用しており、シングルトンを介してRedisクライアントを提供する効率的な方法だと考えています。以下は設定が既に組み込まれているコードです。私のプロジェクトでは、これを./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();

上記のコードはgetRedisClient関数をエクスポートしており、これはVercel KV Redisインスタンスに接続されたRedisクライアントを返します。

Redisクライアントの使用

ここまで来た方は、実装したいRedis機能があってここにいるのだと思うので、このセクションは必要ないかもしれません。ただし、単に興味本位でここに来て、試してみたいものがある場合は、以下にRedisを使用して実装された簡単なインクリメント/デクリメント機能の例を示します。これは、先ほどエクスポートしたgetRedisClient関数を使用しています。

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(`${COUNTER_NAMESPACE}`, 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(`${COUNTER_NAMESPACE}`, decrement);
    if (newCount < 0) {
      await client.set(`${COUNTER_NAMESPACE}`, "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(`${COUNTER_NAMESPACE}`);
    if (count) {
      return parseInt(count);
    }
    return 0;
  } catch (error) {
    console.error(error);
    return 0;
  }
};

動作例

動作例をGitHubにアップロードしました。こちらからアクセスして、リポジトリをプルして自分で試すことができます。リポジトリを使用するには、Vercel KVインスタンスを作成し、接続文字列を取得して、クローンしたリポジトリの.env.localファイルに提供する必要があることを忘れないでください。

この記事は元々 dev.to に掲載されています。