Transmit

Transmit

Transmitは、AdonisJS向けに作られたネイティブな意見のあるServer-Sent-Event(SSE)モジュールです。通知、ライブチャットメッセージ、またはその他のリアルタイムデータなど、クライアントへのリアルタイムの更新を送信するためのシンプルで効率的な方法です。

データの送信はサーバーからクライアントへのみ行われます。クライアントからサーバーへの通信にはフォームまたはフェッチリクエストを使用する必要があります。

インストール

次のコマンドを使用してパッケージをインストールおよび設定します:

node ace add @adonisjs/transmit
  1. 検出されたパッケージマネージャを使用して@adonisjs/transmitパッケージをインストールします。

  2. adonisrc.tsファイル内で@adonisjs/transmit/transmit_providerサービスプロバイダを登録します。

  3. configディレクトリ内に新しいtransmit.tsファイルを作成します。

また、クライアント側でイベントを受信するためにTransmitクライアントパッケージもインストールする必要があります。

npm install @adonisjs/transmit-client

設定

Transmitパッケージの設定はconfig/transmit.tsファイルに保存されます。

参照:Config stub

import { defineConfig } from '@adonisjs/transmit'
export default defineConfig({
pingInterval: false,
transport: null,
})

pingInterval

クライアントにpingメッセージを送信するために使用される間隔です。値はミリ秒または文字列のDuration形式(例:10s)で指定します。pingメッセージを無効にするにはfalseに設定します。

transport

Transmitは複数のサーバーやインスタンス間でイベントを同期できます。この機能を有効にするには、必要なトランスポートレイヤー(現在はredisのみサポート)を参照するように設定します。同期を無効にするにはnullに設定します。

import env from '#start/env'
import { defineConfig } from '@adonisjs/transmit'
import { redis } from '@adonisjs/transmit/transports'
export default defineConfig({
transport: {
driver: redis({
host: env.get('REDIS_HOST'),
port: env.get('REDIS_PORT'),
password: env.get('REDIS_PASSWORD'),
keyPrefix: 'transmit',
})
}
})

redisトランスポートを使用する場合は、ioredisがインストールされていることを確認してください。

Register Routes

クライアントがサーバーに接続できるようにするためには、transmitルートを登録する必要があります。ルートは手動で登録されます。

start/routes.ts
import transmit from '@adonisjs/transmit/services/main'
transmit.registerRoutes()

各ルートを手動でコントローラーにバインドして手動で登録することもできます。

start/routes.ts
const EventStreamController = () => import('@adonisjs/transmit/controllers/event_stream_controller')
const SubscribeController = () => import('@adonisjs/transmit/controllers/subscribe_controller')
const UnsubscribeController = () => import('@adonisjs/transmit/controllers/unsubscribe_controller')
router.get('/__transmit/events', [EventStreamController])
router.post('/__transmit/subscribe', [SubscribeController])
router.post('/__transmit/unsubscribe', [UnsubscribeController])

ルート定義を変更して、たとえばRate Limiterや認証ミドルウェアを使用して一部のtransmitルートの乱用を防ぎたい場合は、ルート定義を変更するか、transmit.registerRoutesメソッドにコールバックを渡すことができます。

start/routes.ts
import transmit from '@adonisjs/transmit/services/main'
transmit.registerRoutes((route) => {
// クライアントを登録するために認証されていることを確認します
if (route.getPattern() === '__transmit/events') {
route.middleware(middleware.auth())
return
}
// 他のtransmitルートにスロットルミドルウェアを追加できます
route.use(throttle)
})

チャンネル

チャンネルはイベントをグループ化するために使用されます。たとえば、通知用のチャンネル、チャットメッセージ用の別のチャンネルなどがあります。クライアントがチャンネルに登録すると、チャンネルが動的に作成されます。

チャンネル名

チャンネル名はチャンネルを識別するために使用されます。大文字と小文字が区別され、文字列である必要があります。チャンネル名には/以外の特殊文字やスペースは使用できません。以下は有効なチャンネル名の例です:

import transmit from '@adonisjs/transmit/services/main'
transmit.broadcast('global', { message: 'Hello' })
transmit.broadcast('chats/1/messages', { message: 'Hello' })
transmit.broadcast('users/1', { message: 'Hello' })

チャンネル名はAdonisJSのルートと同じ構文を使用しますが、それらとは関係ありません。同じキーでHTTPルートとチャンネルを自由に定義できます。

チャンネルの認証

authorizeメソッドを使用して、チャンネルへの接続を承認または拒否できます。このメソッドはチャンネル名とHttpContextを受け取り、真偽値を返す必要があります。

start/transmit.ts
import transmit from '@adonisjs/transmit/services/main'
import Chat from '#models/chat'
import type { HttpContext } from '@adonisjs/core/http'
transmit.authorize<{ id: string }>('users/:id', (ctx: HttpContext, { id }) => {
return ctx.auth.user?.id === +id
})
transmit.authorize<{ id: string }>('chats/:id/messages', async (ctx: HttpContext, { id }) => {
const chat = await Chat.findOrFail(+id)
return ctx.bouncer.allows('accessChat', chat)
})

イベントのブロードキャスト

broadcastメソッドを使用して、チャンネルにイベントをブロードキャストできます。このメソッドはチャンネル名と送信するデータを受け取ります。

import transmit from '@adonisjs/transmit/services/main'
transmit.broadcast('global', { message: 'Hello' })

また、broadcastExceptメソッドを使用して、特定のUIDを除くすべてのチャンネルにイベントをブロードキャストすることもできます。このメソッドはチャンネル名、送信するデータ、および無視するUIDを受け取ります。

transmit.broadcastExcept('global', { message: 'Hello' }, 'uid-of-sender')

複数のサーバーやインスタンス間での同期

デフォルトでは、イベントのブロードキャストはHTTPリクエストのコンテキスト内でのみ動作します。ただし、設定でtransportを登録することで、バックグラウンドからイベントをブロードキャストすることもできます。

トランスポートレイヤーは、複数のサーバーやインスタンス間でイベントを同期する責任を持ちます。これは、ブロードキャストされたイベント、サブスクリプション、およびアンサブスクリプションなどのイベントを、接続されているすべてのサーバーやインスタンスに対してMessage Busを使用してブロードキャストすることによって機能します。

クライアント接続に対して責任を持つサーバーまたはインスタンスは、イベントを受信し、クライアントにブロードキャストします。

Transmitクライアント

@adonisjs/transmit-clientパッケージを使用して、クライアント側でイベントを受信できます。このパッケージはTransmitクラスを提供します。クライアントはデフォルトでEventSource APIを使用してサーバーに接続します。

import { Transmit } from '@adonisjs/transmit-client'
export const transmit = new Transmit({
baseUrl: window.location.origin
})

Transmitクラスのインスタンスは1つだけ作成し、アプリケーション全体で再利用するべきです。

Transmitインスタンスの設定

Transmitクラスは、次のプロパティを持つオブジェクトを受け入れます:

baseUrl

サーバーのベースURLです。URLにはプロトコル(httpまたはhttps)とドメイン名を含める必要があります。

uidGenerator

クライアントのための一意の識別子を生成する関数です。関数は文字列を返す必要があります。デフォルトではcrypto.randomUUIDです。

eventSourceFactory

新しいEventSourceインスタンスを作成する関数です。デフォルトではWebAPIのEventSourceを使用します。EventSource APIをサポートしていないNode.jsReact Native、または他の環境でクライアントを使用する場合は、カスタムの実装を提供する必要があります。

eventTargetFactory

新しいEventTargetインスタンスを作成する関数です。デフォルトではWebAPIのEventTargetを使用します。EventTarget APIをサポートしていないNode.jsReact Native、または他の環境でクライアントを使用する場合は、カスタムの実装を提供する必要があります。EventTarget APIを無効にするにはnullを返します。

httpClientFactory

新しいHttpClientインスタンスを作成する関数です。主にテスト目的で使用されます。

beforeSubscribe

チャンネルに登録する前に呼び出される関数です。チャンネル名とサーバーに送信されるRequestオブジェクトが渡されます。この関数を使用してカスタムヘッダーを追加したり、リクエストオブジェクトを変更したりするために使用します。

beforeUnsubscribe

チャンネルから登録解除する前に呼び出される関数です。チャンネル名とサーバーに送信されるRequestオブジェクトが渡されます。この関数を使用してカスタムヘッダーを追加したり、リクエストオブジェクトを変更したりするために使用します。

maxReconnectAttempts

再接続試行の最大回数です。デフォルトは5です。

onReconnectAttempt

各再接続試行の前に呼び出される関数で、これまでに行われた試行回数が渡されます。カスタムロジックを追加するためにこの関数を使用します。

onReconnectFailed

再接続試行が失敗したときに呼び出される関数です。カスタムロジックを追加するためにこの関数を使用します。

onSubscribeFailed

サブスクリプションが失敗したときに呼び出される関数です。Responseオブジェクトが渡されます。カスタムロジックを追加するためにこの関数を使用します。

onSubscription

サブスクリプションが成功したときに呼び出される関数です。チャンネル名が渡されます。カスタムロジックを追加するためにこの関数を使用します。

onUnsubscription

アンサブスクリプションが成功したときに呼び出される関数です。チャンネル名が渡されます。カスタムロジックを追加するためにこの関数を使用します。

サブスクリプションの作成

subscriptionメソッドを使用して、チャンネルに対するサブスクリプションを作成できます。このメソッドはチャンネル名を受け取ります。

const subscription = transmit.subscription('chats/1/messages')
await subscription.create()

createメソッドはサブスクリプションをサーバーに登録します。awaitまたはvoidできるプロミスを返します。

createメソッドを呼び出さない場合、サブスクリプションはサーバーに登録されず、イベントを受信しません。

イベントのリスニング

onMessageメソッドを使用して、サブスクリプションでイベントをリスンできます。このメソッドはコールバック関数を受け取ります。異なるコールバックを追加するために、onMessageメソッドを複数回呼び出すことができます。

subscription.onMessage((data) => {
console.log(data)
})

また、onMessageOnceメソッドを使用して、コールバック関数を受け取ることで、チャンネルを一度だけリスンすることもできます。

subscription.onMessageOnce(() => {
console.log('一度だけ呼び出されます')
})

イベントのリスニングを停止する

onMessageおよびonMessageOnceメソッドは、特定のコールバックのリスニングを停止するために呼び出すことができる関数を返します。

const stopListening = subscription.onMessage((data) => {
console.log(data)
})
// リスニングを停止する
stopListening()

サブスクリプションの削除

deleteメソッドを使用して、サブスクリプションを削除できます。このメソッドはawaitまたはvoidできるプロミスを返します。このメソッドはサーバー上でサブスクリプションの登録を解除します。

await subscription.delete()