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

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

この投稿では、Upstash Redis、Next.js Server Actions、Vercel で Server-Sent Events を使用してリアルタイム通知を構築した方法について説明します。 Upstash Redis でメッセージ チャネルを活用すると、アプリケーションの通信アーキテクチャが大幅に強化され、アプリケーションの応答性と動的さが向上します。

デモ

使用するもの

  • Next.js(フロントエンドとバックエンド)
  • Upstash Redis (PUBLISH コマンドを使用したサーバー送信イベント)
  • Tailwind CSS(スタイリング)
  • Vercel(導入)

必要なもの

  • Node.js 18
  • Upstash アカウント
  • Vercel アカウント

Upstash Redis のセットアップ

Upstash アカウントを作成してログインしたら、[Redis] タブに移動してデータベースを作成します。

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

データベースを作成したら、「詳細」タブに移動します。 「データベースに接続する」セクションが見つかるまで下にスクロールします。 Redis URL をコピーし、安全な場所に保存します。UPSTASH_REDIS_URL として使用します。

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

また、REST API セクションが見つかるまで下にスクロールし、.env を選択します。 ボタン。コンテンツをコピーして安全な場所に保存します。UPSTASH_REDIS_REST_URL として取得した変数を使用します。 と UPSTASH_REDIS_REST_TOKEN .

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

プロジェクトのセットアップ

セットアップするには、アプリ リポジトリのクローンを作成し、このチュートリアルに従ってそこに含まれる内容をすべて学習するだけです。プロジェクトをフォークするには、次を実行します。

git clone https://github.com/rishi-raj-jain/upstash-nextjs-publish-messages-with-sse-example
cd upstash-nextjs-publish-messages-with-sse-example
npm install

リポジトリのクローンを作成したら、.env ファイルを作成します。上記のセクションで保存したアイテムを追加します。

次のようになります:

# .env
 
# Obtained from the steps as above
 
# Upstash Redis Secrets
UPSTASH_REDIS_URL="rediss://default:...@...-...-...-....upstash.io:..."
UPSTASH_REDIS_REST_URL="https://...-...-...-....upstash.io"
UPSTASH_REDIS_REST_TOKEN="...="

UPSTASH_REDIS_URL での動作に注目してください。 変数には「redis」ではなく「rediss」と書かれています。これは、TLS/SSL オプションを使用するためです。

これらの手順を完了すると、次のコマンドを使用してローカル環境を起動できるようになります。

npm run dev

リポジトリ構造

これはプロジェクトのメインのフォルダー構造です。以下を扱うこの投稿でさらに説明するファイルを赤色でマークしました。

  • Upstash Redis のメッセージ チャネルを理解する
  • Next.js App Router での Server-Sent Events API の作成
  • 通知を公開するために Upstash Redis を使用して Next.js サーバー アクションを設定する
  • リアルタイムで通知を永続的にリッスンして表示するように Next.js フロントエンドをセットアップする

Upstash Redis、Next.js サーバー アクション、Vercel を使用したリアルタイム通知:ステップバイステップ ガイド

Upstash Redis のメッセージ チャネルについて

Upstash Redis では、パブリッシュ/サブスクライブ モデルがメッセージ チャネルの中核です。パブリッシャーは指定されたチャネルにメッセージをブロードキャストし、サブスクライバーは特定のチャネルからリアルタイムでメッセージを受信できます。このモデルにより、アプリケーションの異なる部分間のシームレスな通信が可能になります。

Edge 互換ライブラリ @upstash/redis を使用してメッセージをチャネルにパブリッシュする方法を次に示します。 👇🏻

import { Redis } from '@upstash/redis'
 
// Connect to an Upstash Redis instance
const redis = Redis.fromEnv()
 
// Publish a message to the Upstash Redis instance
await redis.publish('posts', JSON.stringify({ date: new Date().toString(), message: "I am a new message." }))

サブスクライバーが Upstash Redis チャネル (ここでは posts) を聞く方法は次のとおりです。 ) ノード互換ライブラリ、ioredis を使用 👇🏻

// Use ioredis to be able to subscribe to an Upstash Redis instance
import Redis from 'ioredis'
 
// Create an Upstash Redis Subscriber instance
const redisSubscriber = new Redis(process.env.UPSTASH_REDIS_URL)
 
// Define the key to listen and publish messages to
const setKey = 'posts'
 
// Subscribe to Redis updates for the key: "posts"
// In case of any error, just log it
redisSubscriber.subscribe(setKey, (err) => {
 if (err) console.log(err)
})
 
// Listen for new posts from Redis
redisSubscriber.on('message', (channel, message) => {
 // Log the data when the channel message is received is same as the message is published to
 if (channel === setKey) console.log(mesage)
})

メッセージをチャネルに公開すると、すべてのサブスクライバーが即座にメッセージを受信するため、Upstash Redis 内での効率的かつリアルタイムのコミュニケーションが可能になります。

Next.js App Router での Server-Sent Events API の作成

サーバー送信イベントは、複数のクライアント要求を必要とせずに、新しいデータをリアルタイムで送信する強力な方法です。従来のリクエスト/レスポンス メカニズムとは異なり、SSE では、単一の存続期間の長い HTTP 接続を介して、サーバーからクライアントへの一方向のデータ フローが可能になります。

Next.js App Router でサーバー送信イベントを実装する方法は次のとおりです 👇🏻

// File: app/api/stream/route.js
 
// Prevents this route's response from being cached on Vercel
export const dynamic = 'force-dynamic'
 
export async function GET() {
 const encoder = new TextEncoder()
 // Create a streaming response
 const customReadable = new ReadableStream({
 start(controller) {
 const message = 'Hey, I am a message.'
 controller.enqueue(encoder.encode(`data: ${message}\n\n`))
 },
 })
 // Return the stream response and keep the connection alive
 return new Response(customReadable, {
 // Set the headers for Server-Sent Events (SSE)
 headers: {
 'Content-Type': 'text/event-stream; charset=utf-8',
 Connection: 'keep-alive',
 'Cache-Control': 'no-cache, no-transform',
 'Content-Encoding': 'none'
 },
 })
}

Next.js サーバー アクションを使用した Upstash Redis へのメッセージの公開

Next.js サーバー アクションを使用すると、Next.js のフロントエンド コード内でサーバー側ロジックを直接定義できます。これにより、API ルートを手動で作成するプロセスと、フォームの送信ステータスの送信と追跡の手間が省けます。

use server の場合 関数の先頭で、関数がサーバー側でのみ実行されることを確認できます。フォーム送信サーバー アクション内で、message を抽出します。 フォームから値を取得し、Vercel ヘッダーを使用してユーザーの国を取得し、その情報をメッセージとして posts に発行します。 Upstash Redis メッセージ チャネル。

// File: app/actions.jsx
 
'use server'
 
import { Redis } from '@upstash/redis'
import { headers } from 'next/headers'
 
// The function that takes care of obtaining the country code from Vercel headers
// And publishing messages to the Upstash Redis database with the current timestamp
export async function publishNotification(formData) {
 'use server'
 const redis = Redis.fromEnv()
 
 // Extract the message in the form submitted
 const message = formData.get('message')
 
 // Obtain country of the user using Vercel's x-vercel-ip-country header
 const headersList = headers()
 const country = headersList.get('x-vercel-ip-country')
 
 // Publish the message to the "posts" channel in Upstash Redis
 await redis.publish(
 'posts',
 JSON.stringify({
 message,
 country,
 date: new Date().toString(),
 }))
}

フォームの送信時にこのサーバー アクションを呼び出すには、それをハンドラーとしてフォーム action に渡します。 イベント。

// File: app/page.jsx
 
// Import the server action
import { publishNotification } from './actions'
 
const Home = () => {
 return (
 <>
 <div>
 <form
 
 /* set the server action to invoked as form is submitted */
 action={publishNotification}
 
 >
 {/* Place a client side form component here */}
 </form>
 </div>
 </>
 )
}
 
export default Home

React の useFormStatus フックを使用して、フォーム送信中に保留状態を表示するように Next.js フロントエンドをセットアップします

次のコードは、フォームの送信を処理し、pending を表示する Next.js Form クライアント側コンポーネントのセットアップを示しています。 React の useFormStatus を使用した状態 フック。コードの主要な要素を分析してみましょう。

  • 最近リリースされた [useFormStatus をインポートしています React](https://react.dev/reference/react-dom/hooks/useFormStatus) のフック。これにより、最後のフォーム送信のステータス情報が得られます。
  • pending の使用 フォーム送信が進行中かどうかを示す状態リアクティブ変数。
  • 送信が保留中でない場合は、reset フォーム。
  • pending を使用します。 フォームの条件付き状態を表示するブール値。
'use client'
 
import { useEffect } from 'react'
import { useFormStatus } from 'react-dom'
 
const Form = () => {
 
 // Use React's useFormStatus hook to detect form submission state
 const { pending } = useFormStatus()
 
 useEffect(() => {
 // If the form is not pending, reset the form
 if (!pending) document.getElementById('publish-form').reset()
 }, [pending])
 
 return (
 <>
 <input placeholder="Your message" className="border rounded px-3 outline-none focus:border-black/50 py-2" type="text" name="message" required />
 <button
 /* Disable button click while the form submission is pending */
 disabled={pending}
 className="hover:border-black/50 max-w-max border rounded py-1 px-3" type="submit"
 >
 {/* Display "pending" state placeholder */}
 {pending ? (
 <div className="flex flex-row gap-x-2 items-center">
 <div className="animate-spin border border-gray-800 rounded-full h-[15px] w-[15px]"></div>
 <span>Publishing</span>
 </div>
 ) : (
 <>Publish Notification &rarr;</>
 )}
 </button>
 </>
 )
}
 
export default Form

サーバー送信イベントを永続的にリッスンするために Next.js フロントエンドをセットアップする

このセクションでは、Server-Sent Events API メッセージに対する最小限のリスナーをセットアップする方法と、SSE API への接続を維持するアプローチを学びます。

React フロントエンドでサーバー送信イベント API をリッスンする

React のクライアント側コンポーネントで SSE API をリッスンするには、useEffect を使用します。 フック。 SSE API への接続を確立するには、新しい EventSource を作成します。 /api/stream を指すインスタンス エンドポイント。次に、message のイベント リスナーをアタッチします。 イベントでは、ストリームから受信したデータが JSON として解析され、コンポーネントでさらに処理または表示されます。

最後に、このコードには、コンポーネントがアンマウントされたときに SSE 接続を閉じ、メモリ リークの可能性を防ぐクリーンアップ関数が含まれています。

import { useEffect, useState } from 'react'
 
const ClientSideComponent = () => {
 
 useEffect(() => {
 
 // Initiate the first call to connect to SSE API
 const eventSource = new EventSource('/api/stream')
 
 eventSource.addEventListener('message', (event) => {
 // Parse the data received from the stream into JSON
 // Add it the list of messages seen on the page
 const tmp = JSON.parse(event.data)
 // Do something with the obtained message
 })
 
 // As the component unmounts, close listener to SSE API
 return () => {
 eventSource.close()
 }
 
 }, [])
 
 return <></>
}
 
export default ClientSideComponent

React フロントエンドでのサーバー送信イベント API への接続の維持

React コンポーネントの拡張バージョンでは、エラーを処理し、自動的に再接続することで、SSE API への継続的かつ永続的な接続を確保するメカニズムを実装しました。

これは、connectToStream によって実現されます。 SSE API への接続の確立と維持を担当する関数。

その機能の内訳は次のとおりです 👇🏻

<オル>
  • 初期接続とメッセージ処理:
  • この関数は新しい EventSource を作成します。 インスタンス、/api/stream に接続 エンドポイント。

    // Function to take care of initial connect to the SSE API
    // Also, it reconnects to the SSE API as soon as it shuts down
    // This keeps the connection alive - forever with micro second delays
    const connectToStream = () => {
     // Connect to /api/stream as the SSE API source
     const eventSource = new EventSource('/api/stream')
     // ..
    }

    さらに、message のイベント リスナーを設定します。 イベントでは、ストリームから受信したデータが JSON として解析されます。解析されたデータは、React コンポーネントで処理または表示できます。

    const connectToStream = () => {
     // ...
     eventSource.addEventListener('message', (event) => {
     // Parse the data received from the stream into JSON
     // Add it the list of messages seen on the page
     const tmp = JSON.parse(event.data)
     setPosts((prevPosts) => [...prevPosts, tmp])
     })
     // ...
    }
    <オル>
  • エラー処理と自動再接続:
  • 追加のイベント リスナーが error に設定されています イベント。接続失敗などのエラーが発生した場合、イベント ソースは閉じられます。

    閉じた後、関数は setTimeout を使用します。 1 ミリ秒の最小遅延後に再接続をトリガーします。この小さな遅延は、急速な接続試行でサーバーに負荷をかけることなく、よりスムーズで継続的な再接続プロセスを作成するのに役立ちます

    // In case of any error, close the event source
    // So that it attempts to connect again
    eventSource.addEventListener('error', () => {
     eventSource.close()
     setTimeout(connectToStream, 1)
    })
    <オル>
  • SSE API ソース クロージャの処理:
  • onclose イベントは、SSE API ソースが閉じられたことを検出するために利用されます。終了すると、関数は少し遅れてストリームへの接続を再度試行するようスケジュールします。

    // As soon as SSE API source is closed, attempt to reconnect
    eventSource.onclose = () => {
     setTimeout(connectToStream, 1)
    }

    これらの戦略を組み合わせることで、この関数は SSE API への接続を永続的に維持します。エラーやクローズが発生した場合でも、React コンポーネントは最小限の遅延で再接続を試行し続け、継続的な接続を効果的に維持します。

    Vercel に展開

    リポジトリを Vercel にデプロイする準備ができました。 Vercel をシームレスに導入するには、以下の手順に従ってください 👇🏻

    • GitHub Repository を作成します アプリコードを使用して
    • New Project を作成します Vercel ダッシュボード内
    • 作成した GitHub Repository をリンクします。 新しいプロジェクトとして
    • 下にスクロールして、Environment Variables を更新します。 .env から ローカル
    • 展開してください! 🚀

    参考文献


    1. Redisで実現するインメモリキャッシュ入門:高速かつ信頼性の高いデータ取得をマスターする

      高速なレスポンスが求められるWebアプリやAPIを開発する際、キャッシュは成功を左右する重要な要素となります。 キャッシュがない場合、サーバーは同じデータを何度も取得するために時間を浪費してしまいます。データベース、サードパーティAPI、あるいは低速なストレージシステムへのアクセスがその典型例です。 しかし、そのデータをメモリ上に保存すれば、同じ情報をミリ秒単位で提供できるようになります。そこで活躍するのがRedisです。 Redisは、データをRAMに保存し瞬時に取り出せる高速かつ柔軟なツールです。ダッシュボードの構築、SNS投稿の自動化、ユーザーセッションの管理など、あらゆる場面でシステ

    2. Redis EnterpriseとCohesity SmartFiles連携で実現するデータベースの永続性とバックアップ

      私たちはこのほど、Redis Technical Partner Programへのオンボーディングの一環として、Cohesityチームと協力し、HeliosとRedis Enterpriseの統合検証を行う機会を得ました。Helios上で動作するCohesity SmartFilesは、データの保存場所に関わらず、非構造化データを一元的に可視化し、グローバルに管理できるサービスです。Redis Enterpriseの文脈では、SmartFilesはデータベーススナップショットの単一ビューを提供します。 なぜ永続性(Persistence)やバックアップにこだわる必要があるのでしょうか? 実