Deno で HTTP サーバーを手軽にインターネットへ公開する

(※この記事は 別媒体に投稿した記事 のバックアップです。canonical も設定しています)

はじめに

開発している時にテスト用の HTTP サーバーを https で一時的にインターネットに公開したい、そんなことってありませんか?
例えば私の場合、App Store Server Notifications のテストをする際に、以下のようなテスト用の HTTP サーバーを用意しました。

その時は express を使った適当なプロジェクトを作って、ngrok を使って一時的にインターネットに公開してテストしました。
しかし、この方法はローカルPCで上げっぱなにしておかねばならず、うっかりスリープ状態になってしまうとちゃんとリクエストが受け付けられなかった、なんてことがあったりしてちょっと面倒でした。

そこで、今回は Deno (+ Deno Deploy) を使って手軽にテスト用の HTTP サーバーをインターネットへ公開する方法を試したので、その記録をまとめます。

Deno を使って HTTP サーバーを作る

Deno には標準ライブラリに HTTP Server APIs というのがあるので、それを使うと簡単に HTTP サーバーを構築できます。
今回は以下のようなソースコードを作成してみました。

import { serve } from "https://deno.land/std@0.114.0/http/server.ts";

const PORT = 8080;

const handler = async (request: Request): Promise<Response> => {
  console.log("Request:", request.method, request.url);
  const { pathname, search } = new URL(request.url);

  if (request.method === "GET") {
    // GET リクエストの場合は、クエリパラメータを返す
    const data = JSON.stringify({ pathname, search }, null, 2);
    console.log("Request Data:", data);
    return new Response(data, {
      headers: { "Content-Type": "application/json" },
    });
  } else if (request.method === "POST") {
    // POST リクエストの場合は、リクエストボディを返す
    const payload = await request.text();
    const data = JSON.stringify({ pathname, payload }, null, 2);
    console.log("Request Data:", data);
    return new Response(data, {
      headers: { "Content-Type": "application/json" },
    });
  }
  return new Response("Not Found", { status: 404 });
};

console.log(`Listening on http://localhost:${PORT}/`);
await serve(handler, { addr: `:${PORT}` });

今回はシンプルに以下のような処理にしてみました。

ここは、テストしたい内容に合わせて適宜変えればOKです。

実装した HTTP サーバーをローカルで動作確認するには、以下のコマンドで実行します。

$ deno run --allow-net ./index.ts

あとは curl を実行して、以下のようなレスポンスが返ってくれば確認OKです。

$ curl http://localhost:8080/test?foo=bar
{
  "pathname": "/test",
  "search": "?foo=bar"
}

補足:HTTP サーバーの立て方の変遷

Deno で HTTP サーバーを立てるための推奨される方法は色々変わってきたようです。
こちらの記事が詳しくまとまっていて、非常に助かりました。
Denoでサーバーを建てる方法 2021年11月版

実はこの記事を書いている途中に、現在は serve が推奨されていることを知りました。
(それまでは Deno.serveHttp を使う方法でやっていました)
まだメジャーバージョンが 0 なので、色々変わってくるのは仕方ないところではありますね。

Deno Deploy で公開

Deno Deploy は Deno Land Inc. が提供している Web サービスで、サーバーレス関数を提供しているプラットフォームで、現在はベータ版です。
詳しくは、こちらのスライドを参照していただけると把握しやすいと思います。
Deno Deploy の話 - toranoana.deno #0

下準備として「Deno を使って HTTP サーバーを作る」を GitHub のリポジトリにプッシュしておきます。
私は以下のリポジトリを用意しました。

https://github.com/mryhryki/example-deno-http-server

Deno Deploy にデプロイするのはとても簡単です。

  1. https://deno.com/deploy にアクセス
  2. GitHub アカウントでサインアップ or サインイン
  3. "New Project" からチュートリアルに従って GitHub リポジトリと連携する
  4. デプロイ完了

本当に簡単にデプロイできるので、今回のようにサクッとサーバーを立てたい場合にも非常に便利です。
コード量が少ないというのもあるとは思いますが、GitHub にプッシュしてから数秒で反映されるのも嬉しいポイントです。

公開したHTTPサーバーにアクセス

デプロイが完了したら、実際にアクセスしてみます。
私のプロジェクトでは https://example-deno-http-server.deno.dev という URL が割り当てられたので、そこに対して GETPOST リクエストを発行してみます。

$ curl 'https://example-deno-http-server.deno.dev/?uuid=0a38784d-7f81-49ae-9a91-454f67f05e6f'
{
  "pathname": "/",
  "search": "?uuid=0a38784d-7f81-49ae-9a91-454f67f05e6f"
}
$ curl -XPOST -d 'UUID: 0a38784d-7f81-49ae-9a91-454f67f05e6f' 'https://example-deno-http-server.deno.dev/post_request'
{
  "pathname": "/post_request",
  "payload": "UUID: 0a38784d-7f81-49ae-9a91-454f67f05e6f"
}

意図したレスポンスが返ってきました。

ログを確認

Deno Deploy では、Web UI から簡単にログが確認できます。
以下のように console.log の内容が出力されていることが確認できました。

0	2022-01-02 14:17:19	[Info]	Request Data: {
  "pathname": "/post_request",
  "payload": "UUID: 0a38784d-7f81-49ae-9a91-454f67f05e6f"
}
1	2022-01-02 14:17:19	[Info]	Request: POST https://example-deno-http-server.deno.dev/post_request
2	2022-01-02 14:17:12	[Info]	Request Data: {
  "pathname": "/",
  "search": "?uuid=0a38784d-7f81-49ae-9a91-454f67f05e6f"
}
3	2022-01-02 14:17:12	[Info]	Request: GET https://example-deno-http-server.deno.dev/?uuid=0a38784d-7f81-49ae-9a91-454f67f05e6f
4	2022-01-02 14:17:12	[Info]	Listening on http://localhost:8080/
5	2022-01-02 14:17:12	[Debug]	isolate start time: 128 milliseconds

Deno Deploy Log

おまけ: Docker イメージにする

Deno Deploy が使えない時のために、汎用的に使える Docker イメージも作ってみました。
今回の用途であれば5行程度の簡単な Dockerfile で作れます。

FROM denoland/deno:1.17.1
COPY ./index.ts ./index.ts

EXPOSE 8080
RUN deno cache ./index.ts
CMD ["run", "--allow-net", "./index.ts"]

あとはビルドして、イメージを適当なサービスで走らせればOKです。
私は AWS App Runner を使って試してみましたが、AWS アカウントを持っていてば非常に簡単に動かすことができました。

とはいえ、Deno Deploy の方が更に簡単ではあるので、Deno で作るなら Deno Deploy を積極的に使っていきたいです。

おわりに

最近 Deno に慣れるために、簡単なスクリプトや使い捨てのコードを Deno でよく作っているんですが、非常に使い勝手が良いです。

Deno Deploy は現在ベータ版で、無料で使えるところも嬉しいですね。
正式リリース後もある程度の無料枠がある予定 ということなので、こちらも期待したいところです。

参考リンク