Denoでサーバーを建てる方法 2021年11月版
Denoでサーバーを建てる方法が以前とは変化しています。 本記事では、Deno 1.16 / std 0.114環境での手法を解説します。
記事執筆時点での結論:std/httpのserve
まずは結論から。 現在、推奨されているのはstd/httpのserve
メソッドを使う方法です。
0.114.0時点でのstd/httpのREADMEには説明が追加されていないのですが、JSDocや、Deno 1.16のリリース記事ではこのserve
メソッドが使われており、最近まで推奨されていたlistenAndServe
メソッドは非推奨となっています。
サンプルを作ってみました。
serve
の第一引数はHandler: (request: Request, connInfo: ConnInfo) => Response | Promise<Response>
、第二引数はServeInit: { addr?: string; signal?: AbortSignal; }
です。
import { serve } from "https://deno.land/std@0.114.0/http/server.ts";
const addr = ":8080";
console.log(`HTTP server listening on http://localhost${addr}`)
serve((request: Request) => {
const { pathname } = new URL(request.url);
return new Response(`Hello world from ${pathname}`);
}, { addr });
addr
が数値でなく文字列なのはhost:port
の形式で指定できるためで、この例のように:
を最初に持ってくることでport
部だけ設定することができます。デフォルト値は"0.0.0.0:8000"
です。
最新のAPIドキュメントは https://doc.deno.land/ で見られます。
試しにこの形式で作ったサーバーファイルをDeno Deployに上げてみましたが、問題なく動作しました。
Denoのサーバーの歴史
最初に添付したツイートの通り、これまでDenoでサーバーを建てる方法は複数提案されてきました。
以下ではそれらをざっくりまとめたいと思います。
std/httpのserve 旧版
当初使用されていたものです。
本記事で紹介したstd 0.114.0のserve
と同名ですが別物で、現在はserver_legacy
に入っているものです。
import { serve } from "https://deno.land/std/http/server_legacy.ts";
const body = "Hello World\n";
const server = serve({ port: 8000 });
for await (const req of server) {
req.respond({ body });
}
当初はserver_legacy.ts
ではなくserver.ts
から読み込まれていたため、バージョン指定がされていないコードでは現行のserve
と区別が付きません。
しばらく前に書かれたDenoの紹介記事はこれを使っていることが多く、「記事のサンプルとインポート元のファイル名もメソッド名も同じなのに引数や使い方がぜんぜん違って動かない…」という非常に厄介な状態になっています。
これが禍根になりそうだったので、今回の記事を書くに至りました。
addEventListener
Deno Deploy Beta 1時代に使用されていた手法です。
addEventListener("fetch", (event) => {
event.respondWith(new Response("Hello world"));
});
この頃はDeno Deployと通常のCLIのDenoではサーバーの書き方が全く異なるものでした。
Deno Native HTTP
前述の旧std/http
のサーバーのパフォーマンスが悪かったことにより、Deno自体にHTTPサーバー機能が組み込まれるに至ったものです。
Deno 1.9で導入され、1.13で安定化しました。
for await (const conn of Deno.listen({ port: 8080 })) {
(async () => {
for await (const { respondWith } of Deno.serveHttp(conn)) {
respondWith(new Response("Hello World"));
}
})();
}
Deno 1.13リリース時点で https://deno.land のサーバーにも使用されており、実績を積んでいました。
そして、この記法はDeno Deployでも使えるようになり、CLI側と同一の記述でサーバーを書けるようになりました。
Deno Deploy Beta 2のリリース記事でも、この書き方が使われています(前述のaddEventListener
も混在しています)。
この1.13時点では、Denoのサーバーはネイティブのものを利用するよう統一され、std/http
は廃止される見通しとなっていました。
std/httpのlistenAndServe
DenoのネイティブHTTPの更新により削除されると思われていたstd/http
ですが、Deno 1.14と並行してリリースされたstd 0.107にて、ネイティブHTTPを使用するlistenAndServe
が追加され、削除どころか推奨手法に返り咲きました。
ネイティブHTTPはlisten
した結果をfor await
で囲み、その中で実行するハンドラにもfor await
が入っている…という少し複雑な構造でしたが、listenAndServe
がそのあたりをラップしたことで、高いパフォーマンスを得つつ、より簡単にサーバーを記述できるようになりました。
import { listenAndServe } from "https://deno.land/std@0.107.0/http/server.ts";
listenAndServe(":8000", () => new Response("Hello World\n"));
Deno 1.16の半月前、Deno Deploy Beta 3のリリース記事では、こちらの記法が使われています。
std/httpのserve 現行版
そして時は現代。
最新の手法が、本記事で紹介しているstd/http
の新serve
メソッドです。
Deno 1.16のリリース記事では、fetch
メソッドの更新について紹介する部分で、この記法がしれっと使われています。
経緯
このメソッドは以下のPRで提案されています。
import { serve } from "https://deno.land/std/http/server.ts"; serve((req) => new Response("Hello world"));
🐯 「Deno Deploy用のサンプルをきれいにしたい ネーミングには自身がないけど」
で、そこで議論があり、以下のPRにて実装が取り込まれ、同時にlistenAndServe
が@deprecated
となりました。
おわりに
以上、Denoのサーバーの書き方と、その歴史でした。
stdも未だ0.xですし、Deno DeployもBetaなので、推奨手法はガンガン変わっていっています。
このため、途中にも書きましたが、「お手本と記述が同じなのに動かない」ことが起こりえます。
公式の最新版以外の資料を参考にする場合は、作成日・更新日を確認のうえ、注意して利用しましょう。
まあ、本記事執筆時点ではDeno Deployトップページに掲載されているサンプルスクリプトも更新されていない状態なんですけどね…。
つまりこの記事は公式より先を走っている
Discussion
まさに std/httpのserve が新しいのか古いのか良くわからない状態になっていました。
いつもdeno関連の最新情報がまとまっていて助かります。
あとは各ライブラリの対応が気になるところです。
oak
drash
等の有名どころは頻繁に更新されているので、きっちり追いついていると信じたい…(ソースを読み込む気力は無い)コメントありがとうございます。
私も読み込むまでは行っていませんが、各ライブラリの現時点の印象としてはこんな感じですね:
oak
std/http
ではなく、ネイティブHTTPサーバーを直接使用しているっぽいので今回の変更の影響はなさそう:https://github.com/oakserver/oak/blob/5ad9c828368b1faa530ec481c7a445f5e11187cd/http_server_native.ts#L214std
のバージョンは0.107.0で、最新版に追従できていないdrash
std@0.113.0/http/server.ts
を使用しているため、今回の更新の対応が必要になりそうlistenAndServe()
を使っている:https://github.com/drashland/drash/blob/98139b11f2067f49fdbb26ed15e139aa078eab4c/src/http/server.ts#L155この辺の確認には、とりあえず
deps.ts
を見るのがオススメです。