LoginSignup
3
1

More than 1 year has passed since last update.

Deno標準ライブラリでServer-Sent Events

Last updated at Posted at 2021-12-05

この記事はDeno Advent Calendar 2021とは全く関係ない記事です。

Denoの標準ライブラリを使って、Server-Sent Eventsを送信するサーバーを書いていきたいと思います。

Server-Sent Eventsとは?

Server-Sent Events(SSE)とは、サーバーからクライアントへイベントを受け渡しできる接続方法のことです。WebSocketと似ていますが、WeSocketが双方向通信なのに対し、SSEはデータがサーバーからクライアントへの1方向しか送信できないという違いがあります。

Deno標準ライブラリのサーバーでServer-Sent Eventsを送信する

SSEを送信するにはReadableStreamを使用します。

SSEの送信
import { serve } from "https://deno.land/std@0.117.0/http/mod.ts";

const encoder = new TextEncoder();

console.log("http://localhost:8000");
serve(() => { // デフォルトではポート8000番を使用
  let timeoutId: number | undefined;
  const stream = new ReadableStream({
    start(controller) {
      timeoutId = setInterval(() => {
        // utf8でエンコード
        const data = encoder.encode(`data: ${new Date()}\n\n`);
        // 送信
        controller.enqueue(data);
      }, 1000); // 1秒おきに送信
    },
    cancel() {
      // 接続切断時のクリーンアップ
      clearInterval(timeoutId);
    },
  });
  return new Response(stream, {
    headers: { "Content-Type": "text/event-stream; charset=utf-8" },
  });
});

上記のコードを実行して、ブラウザからアクセスすると、1秒おきにデータが送信されているのが確認できると思います。

image.png

SSEで送信するメッセージは一定のプロトコルに従う必要があります。 1. メッセージ「フィールド名、コロン、テキストデータ」の形式で送らなければならない 2. フィールド名は`event`、`data`、`id`、`retry`のいずれか 3. メッセージ同士は2つの改行文字で区切られる このプロトコルに従っていないと、ブラウザでデータを受け取ることができません。 詳しくはMDNや仕様書を参照してください。 https://developer.mozilla.org/ja/docs/Web/API/Server-sent_events/Using_server-sent_events#examples

DenoでServer-Sent Eventsを受信する

SSEの受信には、EventSource APIを使います。しかし、このAPIはまだDenoには実装されていません。
#12350によると、現在実装中のようです。

そのため、SSEの受信には、fetch APIを使用してReadableStreamを取得する必要があります。
そのため、SSEの受信にはpolyfillをimportする必要があります。

SSEの受信
import { EventSource } from "https://deno.land/x/eventsource@v0.0.2/mod.ts"

const source = new EventSource("http://localhost:8000/");
source.addEventListener("message", event=>console.log(event.data));

fetch APIを使用してReadableStreamを取得する方法もあります。
SSEの受信
const decoder = new TextDecoder();
const response = await fetch("http://localhost:8000/");
if (!response.body) {
  throw new Error("response.body is null.");
}
const reader = response.body.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) {
    break;
  }
  console.log(decoder.decode(value));
}

(おまけ)ブラウザでServer-Sent Eventsを受信する

const source = new EventSource("http://localhost:8000/");
source.addEventListener("message", event=>console.log(event.data));

image.png
▲こんな感じです。

まとめ

Server-Sent Eventsの送信にはReadableStreamを使います。
Server-Sent Eventsの受信にはEventSourceを使います。

3
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1