ExpressでWebSocketを使う方法

単なるメモ。

まぁ、同じことで悩む人もいるだろうと思うので、ネット上のゴミを増やしておく。

この手のことをするには複数の方法が存在し、検索結果的にメジャーなのは、socket.ioを使う方法っぽい。あるいは、wsを使うという方法もある(こっちは生Nodeでやったことがある)。

いずれの方法であっても、end pointについてはExpress的なrouterが使えないので、生NodeとExpressが合体しただけの、文字通り「木に竹を継いだ」ようなコードになってしまう。

それでは残念なので、いいものがないかと探したら、

express-ws

というのを発見した。これはExpress的routerを拡張してくれる。詳細は、リンク先に。

router.ws('/kernels/:id/channels', (ws, req) => {

という感じのことが出来て強さを感じる。

ところで、Expressを使う時には、私はexpress-generatorを使うことにしている。別にこれを使う必要はあまりないと思うのだが(ライブラリ的に使えばよい)、こういったものを使うことで、

  • どうでもいい部分は他人と同じに出来る
  • 面倒なこと(起動とかディレクトリ構造とか)を考えなくて済む
  • 「フレームワーク」を提供してくれる

というメリットがあるので、このようなものがある時は積極的に使うことにしている。Expressをライブラリ的に使うという手もあるし、それはSinatraっぽいとも言えるのだが、それよりはRails的な方が気楽である。

それは良いのだが、これで生成されたコードはexpress-wsとは相性が良くない。と言うか、そのままでは動いてくれない。express-wsのページにあるサンプルコードと、./bin/wwwやapp.jsを見比べても、そのまますんなり動きそうなのだけど、動いてくれないのだ。

あれこれやってわかったのは、./bin/wwwのポートを開くところが、

var server = http.createServer(app);
server.listen(port);

となっているのだけど、これを

app.listen(port);

とすることで動いてくれた。

どっちがどう違うんだとゆー話があるのだが、httpについてあれこれやらない向きには、これで良いっぽい。

Express.js – app.listen vs server.listen

なお、express-wsを使う時には、

const express = require('express');
const app = express();
const expressWS = require('express-ws')(app);

のようにするのだが、これはapp.jsの中で一度だけやっておけば、他のrouterの中でexpressをrequireしたりする時にはこのような手順を踏む必要はないようである。普通にexpressをrequireするだけで、express-ws対応の動作になってくれる。