DenoのWebSocketサーバーの書き方の方法をまとめます。
2022年10月現在、WebSocketサーバーの書き方としてメジャーなものは以下の2つです。
- 標準ライブラリを使用する方法
- oakやhonoなどのミドルウェアを使う方法
この記事ではそれぞれ使い方をまとめたいと思います。
標準ライブラリのws
モジュールは廃止されました。
標準ライブラリを使う場合
かつては標準ライブラリのws
モジュールを使うやり方が主流だったのですが、現在はこれは廃止され、http
モジュールとDeno.upgradeWebSocket()という関数を使う書き方が推奨されています。
この書き方の特徴は、サードパーティ製のライブラリは必要なく完全にDenoの組み込みAPIだけで書けるという点です。
標準ライブラリを使ってWebSocketサーバーを作成するには、以下のようなコードになります。
import { serve } from "https://deno.land/std@0.156.0/http/server.ts";
serve((req) => {
// `Deno.upgradeWebSocket(req)`でHTTP接続をWebSocket用に切り替え
const { response, socket } = Deno.upgradeWebSocket(req);
// ↓socketという変数に対してaddEventListenerなどを設定し、WebScoketをハンドリング
socket.addEventListener("message", (event) => console.log(event.data));
// レスポンスを返してWebSocket通信スタート
return response;
});
コード中の変数socket
の中にはWebSocketを処理する変数が入っています。例えば
// 接続を開いた時の処理
socket.addEventListener("open", () => console.log("open!"));
// メッセージを受信した時の処理
socket.addEventListener("message", () => console.log("message!"));
// エラー発生時の処理
socket.addEventListener("error", () => console.log("error!"));
// 接続を閉じた時の処理
socket.addEventListener("close", () => console.log("close!"));
// メッセージを送信する
socket.send("message");
// 接続を閉じる
socket.close();
という風に書くことができます。勘が言い方はもうお分かりだと思いますが、ブラウザでnew WebSocket()
した時と同じように使うことができます。
WebSocket以外もハンドリングする方法
一般的なサーバーではWebSocket以外のリクエストも捌く必要があると思います。
その場合は以下のように、url.protocol
やurl.pathname
を見てif文で条件分岐させます。
import { serve } from "https://deno.land/std@0.156.0/http/server.ts";
serve((req) => {
const url = new URL(req.url);
// if (url.pathname==="/foo") {...などで更に条件分岐させることもできる
if (url.protocol === "wss:" || url.protocol === "ws:") {
// websocketのハンドリング
const { response, socket } = Deno.upgradeWebSocket(req);
socket.addEventListener("message", (event) => console.log(event.data));
return response;
} else {
// websocket以外のハンドリング
return new Response("ok!");
}
});
oakを使う場合
oakはexpressライクなDeno向けミドルウェアです。現時点(2022年)では一番普及しており、安定して使うことができます。
oakの場合は、ctx.upgrade()
というメソッドを呼ぶことでWebSocket通信をすることができます。
import { Application, Router } from "https://deno.land/x/oak@v11.1.0/mod.ts";
const app = new Application();
const router = new Router();
router.get("/socket", (ctx) => {
const socket = ctx.upgrade(); // ここでwebsocket接続を開始
// ここでwebsocketのハンドリング
socket.addEventListener("message", (e) => console.log(e.data));
});
app.use(router.routes());
app.use(router.allowedMethods());
app.listen({ port: 8080 });
Honoを使う場合
Honoもexpressのようなミドルウェアですが、他のミドルウェアより高速らしいです。Deno以外のCloudflare WorkersやBunといったランタイムでも動作することが特徴です。
HonoはWeb標準のResponse
/Request
を使用しているため、Deno.upgradeWebSocket()
を使ってWebSocket通信するのがよさそうです。
import { serve } from "https://deno.land/std@0.158.0/http/server.ts";
import { Hono } from "https://deno.land/x/hono@v2.2.2/mod.ts";
const app = new Hono();
app.get("/", (c) => c.text("Hello! Hono!"));
app.get("/ws/", (c) => {
const { response, socket } = Deno.upgradeWebSocket(c.req);
// websocketのハンドリング
socket.addEventListener("message", (e) => console.log(e));
return response;
});
serve(app.fetch);
まとめ
- 標準ライブラリやHonoなど、Web標準
Request
/Response
を使用するサーバーにおいては、Deno.upgradeWebSocket()
を使用します。 - oakなどの、ライブラリ固有の方法が用意されている場合はそちらを使用します。
WebSocketを使おうとしたときにDeno.upgradeWebSocket
がなかなか検索に引っかからず使い方が分からないという場面もありそうなので、今回まとめてみました。