Redis
 Computer >> コンピューター >  >> プログラミング >> Redis

Nuxt 3とサーバーレスRedis(Upstash)で始めるページ訪問カウント実装

Nuxt 3とサーバーレスRedis(Upstash)で始めるページ訪問カウント実装

アプリケーションの利用状況を追跡したり、リソース利用を制限したり、キャッシュからデータを取得してパフォーマンスを向上させたりする必要がある場合、Redisがその答えとなります。Redisはインメモリのキー・バリュー型データベースであり、オープンソースで「Remote Dictionary Server」の略称です。

この記事では、サーバーレスRedisサービスであるUpstashと、Vue SSRフレームワークの最新ベータ版であるNuxt 3を組み合わせた基本的なアプリケーション構築を通じて、Redisの基礎を学びます。Nuxtアプリのページ訪問数をカウントするシンプルな仕組みを実装しながら、Redisのデータ構造やコマンド、Nuxt 3の新機能(Nitroサーバーエンジン、サーバーミドルウェア、APIエンドポイント)の使い方を解説します。初心者向けの内容となっています。

参考リソース

Upstashとは?

Upstashは、HTTP/RESTベースのAPIを通じてRedisデータベースへサーバーレスにアクセスできるサービスです。そのため、Redisのユースケースやデータ操作コマンドといった基礎知識を理解することが不可欠になります。

Redisとは?

Redisには主に以下のようなユースケースがあります。

  • データとセッションのキャッシング
  • ランキングボード(ゲームやゲーミフィケーションを取り入れたソフトウェアでのスコアランキング)
  • キュー(バックグラウンドで後から処理するタスクのスケジューリング)
  • 使用量の計測・カウント(リソース利用の制限、配分制御、ECサイトやSNS、モバイルアプリなど大規模な利用状況の監視・分析)
  • コンテンツフィルタリング(禁止ワードリストとの照合など)

基本的にはキー・バリューペアでデータを格納しますが、リスト、セット、ソート済みセットといった高度なデータ構造もサポートしており、それぞれ専用のコマンド群で操作します。今回の例で使用するソート済みセットを含め、主要なデータ構造の概要を確認しておきましょう。

  • リスト(List): 配列に近い構造。両端へのプッシュ/ポップ、個別要素の取得などが可能。コマンドはLプレフィックス(LPOP, LPUSH, LSET等)
  • ハッシュ(Hash): 1つのRedisキー配下に複数のキー・バリューペアをグループ化して格納。コマンドはHプレフィックス(HSET, HGET, HDEL等)
  • セット(Set): リストに似ているが、要素は一意かつ無順序。ソートは不可だが、追加・削除・存在確認が高速。コマンドはSプレフィックス(SADD, SCARD, SISMEMBER等)
  • ソート済みセット(Sorted Set): セットにスコア(数値)を持たせ、スコア順でソート可能。キー・バリューに近い感覚でスコア操作も可能。コマンドはZプレフィックス(ZADD, ZINCRBY, ZSCORE等)

その他のRedisコマンド詳細は https://redis.io/commands を参照してください。

Upstashのセットアップ

アカウント作成とデータベースのセットアップは公式ドキュメント https://docs.upstash.com/ に従って行ってください。

Nuxtプロジェクトを作成する前に、Upstashアカウントの準備を完了させておきます。無料ティアで1つのデータベースを作成できます。

データベース作成後、任意のRedisクライアントから接続可能です。また、Upstashコンソールに用意されているブラウザベースのCLIを使えば、すぐに試せます。

Nuxt 3とサーバーレスRedis(Upstash)で始めるページ訪問カウント実装

UpstashコンソールのブラウザベースCLI

redis-cli

ローカルマシンのターミナルにredis-cliをインストールすれば、コマンドラインから直接Redisデータベースを作成・アクセスできます。

Redis npmパッケージ

Node.js環境でRedisを操作するためのnpmパッケージも複数存在します。このプロジェクトでは以下の2つを使用します。

  1. @upstash/redis(Upstash公式のRESTクライアント)
  2. ioredis(汎用的なRedisクライアント)

Nuxt 3について

Nuxt 3では、プロジェクト作成用の新しいCLIツールnuxiが導入されています。

npx nuxi init nuxt3-app

/pagesディレクトリを作成し、シンプルなルートを追加します。

- pages
  -- index.vue
  -- about.vue
- app.vue

app.vueはNuxt 3で新たに導入されたメインコンポーネントで、/pages配下の全ルートで読み込まれます。

Nitroサーバーエンジン ⚙️

Nuxt 3には新しいサーバーエンジン「Nitro」が搭載されています。server/ディレクトリ配下にapi/middleware/を作るだけで、サーバーAPIエンドポイントサーバーミドルウェアを簡単に定義できます。Nuxt 2とは異なり、nuxt.config.jsでの設定は不要です。最小限のディレクトリ構造はGitHubリポジトリで確認できます。

- server
  -- api
  -- middleware

APIとミドルウェアはともに、リクエストを処理してPromiseやJSONデータを返すデフォルト関数をエクスポートします。

今回構築するアプリの大まかな手順は以下の通りです。

  1. Redisデータベースに接続する
  2. サーバーへの全ページリクエストをインターセプトし、該当ページのカウンターをインクリメントしてRedisに保存する
  3. クライアント側からAPIを呼び出し、訪問数を取得してページに表示する

概念的には以下の図のような流れになります。

Nuxt 3とサーバーレスRedis(Upstash)で始めるページ訪問カウント実装

Nuxt 3の詳細: https://v3.nuxtjs.org/getting-started/installation

REST Redisクライアント(@upstash/redis)を使用する

Upstashは独自のHTTP/RESTベースRedisクライアント@upstash/redisを提供しています。プロジェクトに依存関係として追加します。

yarn add @upstash/redis

Redis DBの認証

Upstashコンソールのデータベース詳細ページから以下の環境変数を取得します。

  • REST URL (UPSTASH_REDIS_REST_URL)
  • Token (UPSTASH_REDIS_REST_TOKEN)

プライベートランタイム設定

これらの環境変数をサーバーサイドで利用できるように、Nuxtのランタイム設定(nuxt.config.js)で定義します。※タイポ修正: privateRunimeConfigprivateRuntimeConfig

// nuxt.config.js
export default defineNuxtConfig({
  publicRuntimeConfig: {},
  privateRuntimeConfig: {
    UPSTASH_REDIS_REST_URL: process.env.UPSTASH_REDIS_REST_URL,
    UPSTASH_REDIS_REST_TOKEN: process.env.UPSTASH_REDIS_REST_TOKEN,
  },
});

その後、#configからインポートして直接アクセスできます。

import { Redis } from "@upstash/redis";
import config from "#config";

const redis = new Redis({
  url: config.UPSTASH_REDIS_REST_URL,
  token: config.UPSTASH_REDIS_REST_TOKEN,
});

あるいは、ゼロコンフィグアプローチとして、.envファイルにUPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKENを直接記述し、const redis = Redis.fromEnv()でインスタンスを作成することも可能です(@upstash/redis使用時のみ有効)。

Nuxtサーバーミドルウェアでリクエストをインターセプト

ここから必要なRedisコマンドを自由に使用できます。今回はページごとの訪問回数をスコアとして管理するため、一意性がありソート・スコア操作が可能なソート済みセットを使用します。

例えば、zincrbyコマンドでページへのリクエストごとにスコアを1増やします。

// server/middleware/pageCount.js
import { Redis } from "@upstash/redis";
import { getRedisKey } from "../utils";

const redis = Redis.fromEnv();

export default async function (req, res, next) {
  const redisKey = getRedisKey(req.url);
  await redis.zincrby("myPageCounts", 1, redisKey);
  next();
}

ネームスペースの生成

RedisはNoSQLのキー・バリューストアであり、自動採番キーやUUID生成の仕組みはありません。そこで、リクエストURLから一意のキーを生成するユーティリティ関数getRedisKey()を導入します。これにより、同一キーに対してカウントが重複して増えるのを防ぎます。

この例では、リクエストURLのスラッシュ/をドット.に置換して、ネームスペース付きのキーを作成します。

export const getRedisKey = (url: string) => {
  const reqURL = url?.replace(///g, ".");
  const redisKey = reqURL === "." ? "page.home" : `page${reqURL}`;
  return redisKey;
};

これにより、例えば/aboutページはpage.aboutというキーに変換されます。

REST APIエンドポイントでRedis DBにアクセス

現在のカウント(スコア)を取得するAPIエンドポイントを作成します。Nuxt 3ではデータ取得にuseAsyncDatauseFetchの2通りがあります。app.vueではuseAsyncDataohmyfetchライブラリの$fetchを組み合わせます。

<script setup>
const router = useRoute();
const { data: count } = await useAsyncData('Count', () =>
  $fetch('/api/count', { params: { path: router.path } })
);
</script>

API呼び出し時にルーターのパスをクエリパラメータとして渡し、どのページがアクセスされたかを特定します。

$fetch("/api/count", { params: { path: router.path } });

ミドルウェアとは異なり、このエンドポイント/api/countをクライアントから呼び出して訪問数を取得します。

server/api/count.tsとしてAPIエンドポイントを作成します。h3ライブラリのuseQueryでクエリパラメータを取得します。

// server/api/count.ts
import { useQuery } from "h3";
import { getRedisKey } from "../utils";
import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

export default async (req, res) => {
  const query = await useQuery(req);
  const redisKey = getRedisKey(query.path);
  const count = await redis.zscore("myPageCounts", redisKey);
  return { count };
};

ミドルウェアと同じgetRedisKey()を使用することで、キーが一致することを保証し、zscoreでスコア(訪問数)を取得します。

ioredisでRedis APIを直接使用する

同じことをioredisライブラリでも実現できます。

yarn add ioredis

ioredisではauthメソッドはありませんが、Upstashコンソールで取得できる接続文字列を使用して接続します。

ランタイム設定にUPSTASH_REDIS_CONNを追加します。

// nuxt.config.js
export default defineNuxtConfig({
  publicRuntimeConfig: {},
  privateRuntimeConfig: {
    UPSTASH_REDIS_CONN: process.env.UPSTASH_REDIS_CONN,
  },
});

ミドルウェアではnew Redis()で接続インスタンスを作成し、Redisコマンドを実行します。

// server/middleware/pageCount.js
import config from "#config";
import Redis from "ioredis";

const client = new Redis(config.UPSTASH_REDIS_CONN);

export default async function (req, res, next) {
  const redisKey = getRedisKey(req.url);
  await client.zincrby("myPageCounts", 1, redisKey);
  next();
}

APIエンドポイント側も同様にioredisクライアントを使用してzscoreを呼び出します。

// server/api/count.ts
import config from "#config";
import Redis from "ioredis";
import { IncomingMessage, ServerResponse } from "http";
import { getRedisKey } from "../utils";

const client = new Redis(config.UPSTASH_REDIS_CONN);

export default async (req: IncomingMessage, res: ServerResponse) => {
  const query = await useQuery(req);
  const redisKey = getRedisKey(query.path);
  const count = await client.zscore("myPageCounts", redisKey);
  return { count };
};

Upstash CLIで動作確認

データはソート済みセットに格納されているため、zrangeで全アイテムを取得できます。UpstashコンソールのCLIで以下を実行します。

zrange myPageCounts 0 -1
  • myPageCounts: ソート済みセットの名前
  • 0 -1: 範囲指定(0は先頭、-1は末尾)

スコアも含めて取得するにはWITHSCORESを付けます。

# スコア昇順
zrange myPageCounts 0 -1 WITHSCORES

# スコア降順
zrevrange myPageCounts 0 -1 WITHSCORES

# 特定キーのスコア取得
zscore myPageCounts page.home

APIやミドルウェアのテスト中は、Upstashコンソールのアクティビティやリソース使用状況を確認するとよいでしょう。

Nuxt 3とサーバーレスRedis(Upstash)で始めるページ訪問カウント実装

Upstashコンソールのリソース使用状況

Nuxt 3アプリのデプロイ

Nitroサーバーエンジンはデプロイ時に重要な役割を果たします。

Netlifyへのデプロイ

GitHubリポジトリをNetlifyに接続してデプロイします。以下の3点に注意してください。

  • ビルドコマンド: npm run build、公開ディレクトリ: dist
  • 環境変数(REST URL、Token、またはRedis接続文字列)をNetlifyの設定で事前に追加
  • プロジェクトルートにnetlify.tomlを作成(内容はこちらを参照)

yarn build実行時に生成される.outputディレクトリをFunctionsのパスとして指定します。

# netlify.toml
[build]
  # ...
  functions = ".output/server"

Netlifyでの動作例: https://thirsty-visvesvaraya-a09ab9.netlify.app/

Cloudflare Workersへのデプロイ

ターミナルからCloudflare Workersへ直接デプロイ可能です。ローカルテストにはMiniflare、プレビューと公開にはWranglerの使用が推奨されます。

プロジェクトルートにwrangler.tomlを作成し、Cloudflareのaccount_idと環境変数を設定します()。

デプロイ時はNITRO_PRESET=cloudflareを指定してビルドします。

# wrangler.toml
[site]
  bucket = ".output/public"
  entry-point = ".output"

[build]
  command = "NITRO_PRESET=cloudflare yarn nuxt build"
  upload.format = "service-worker"

Cloudflare Workersでの動作例: https://upstash-demo.krutie-patel.workers.dev/contact

結論

この記事では、Upstashを利用したRedisの基本的な活用方法として、SSRフレームワークであるNuxt 3(v3)と統合し、以下の一連の流れを実装しました。

  • Redisデータベースへの接続
  • 一意なキーの生成とキー・バリューペアの書き込み
  • Redisからのデータ読み取り
  • NetlifyおよびCloudflare Workersへのデプロイ

Upstashには、データベースごとのデータトラフィック暗号化、マルチAZへのデータレプリケーション、グローバルに分散したエッジロケーションでのREST APIレスポンスキャッシュといった機能も用意されています(コンソールの「Details」タブで確認可能)。

Redisをこれから学びたい初心者にとって、Upstashはすでに知っているフロントエンド技術と組み合わせて手軽に始められる優れた入り口となります。この記事が皆さんのRedis入門の一助となれば幸いです。

  1. Flutter、サーバーレスフレームワーク、Upstash(REDIS)を備えたフルスタックサーバーレスアプリ-パート1

    この投稿では、データを保存するためのFlutter、Serverless Framework、Upstash、Redisを使用してサーバーレスモバイルアプリケーションを構築します。 Upstashとは? Upstashは、Redis用のサーバーレスデータベースです。 Upstashを使用すると、リクエストごとに支払います。これは、データベースが使用されていないときに課金されないことを意味します。 Upstashはデータベースを構成および管理します。これは、DynamoDBやFaunaなどの他のデータベースの強力な代替手段であり、などの利点があります。 低レイテンシ REDISAPIと同

  2. サーバーレス・エッジ時代のグローバルデータベース──Upstash Global Database徹底解説

    近年、アプリケーションのデプロイにおいて、サーバーレスアーキテクチャとエッジコンピューティングが急速に普及しています。しかし、サーバーレス関数やエッジ関数の中でアプリケーションの状態やデータを保存するのは、まったく別の話です。データベースへの接続管理、複数拠点からの高速なデータアクセスの実現など、乗り越えるべき課題が数多くあります。サーバーレスアクセスに対応したデータベースサービスは限られており、さらにエッジ関数にも適したものとなると、ごくわずかしか存在しません(詳細な分析はこちらの記事をご覧ください)。Upstashでは、創業当初から低レイテンシかつリクエスト単位の従量課金モデルを採用したサ