TT Lab
はじめる
学ぶ 学習パス コース

Node.jsバックエンド — フレームワークが隠したもの

Nodeの標準ライブラリでAPIを立てる

TT Labで続きを見る

目標

Nestを使わず、Nodeの標準ライブラリだけで小さな注文APIを作ります。ルーティング表、 入力検証、出力の形、依存性の注入、イベントループまでの8つのステップです。

なぜこうするのか

ラボのPodはDNSしか開いていないので、npm installができません。ところがその制約が かえってこのコースに合っています。フレームワークが代わりにしてくれていた判断を自分で 下してみると、あとでNestを読むときに、各仕組みが何を代わりにしているのかが見えてきます。

形式

/root/work/api/app.mjsを作り、次をexportします。

ソケットを開かないのは、採点がポートの衝突や起動タイミングに左右されてはいけないからです。 失敗が本当の失敗を意味してこそ、テストが役に立ちます。

サーバーの骨組みを作る

mkdir -p /root/work/apiを実行してから、app.mjsにexport async function handle(req)を置いてください。reqは{method, path, body, headers}で、返す値は{status, body}です。/healthzは200と{status:'ok'}を返します。

handle('/healthz')が200と{status:'ok'}を返しています。

ルーティング表を作る: 404と405を分ける

パスの一覧を配列で持ち、まずパスが合うものを集めてからメソッドを見てください。パスがなければ404、パスはあるのにメソッドがなければ405です。GET /itemsは配列を返します。

存在しないパスは404、存在するパスに対する別のメソッドは405です。

入力を検証し、なぜ誤りなのかを知らせる

POST /itemsは{name: string, qty: number}を受け取ります。誤っていれば400と{errors:[...]}で、どのフィールドがなぜ誤っているのかを入れてください。正常なら201です。

qtyが文字列なら400でerrorsにqtyが含まれ、正常なら201です。

保存した形と外へ出す形を分ける

ストアにはsecretがありますが、レスポンスにはあってはいけません。ハンドラーごとに消すのではなく、出力するフィールドを1か所で選んでください。許可リストです。

GET /itemsのレスポンスにsecretがなく、id・name・qtyはあります。

存在しないものには404を返す

GET /items/:idを作ります。存在しなければ404です。200に{error:...}を入れると、クライアントは成功と読んでしまいます。ステータスコードが契約です。

存在しないidは404、存在するidは200とその項目です。

ストアを注入で受け取る

export function createApp({store})を作り、ハンドラーがそのstoreだけを使うようにしてください。グローバルを直接参照すると、次のステップで差し替えられません。

createApp({store})に渡したストアが実際に使われ、出力の形も保たれています。

イベントループを塞がない

GET /slowを作り、最低でも40msかかるようにしてください。whileで時間を消費すると、その間はタイマーまで止まります。await new Promise(r => setTimeout(r, 50))で処理を譲ってください。

4つのリクエストを同時に処理している間も、5msのタイマーが動き続けています。

サーバーを起動せずに契約をテストする

app.test.mjsに、node --testで動くテストを2件以上書いてください。createAppで偽のストアを注入してテストします。ポートを開いてはいけません。

node --testが2件以上成功し、サーバーを起動していません。