Expressでは、req.params がルートパスの名前付きセグメントの値(/users/:id なら req.params.id)を、req.query が ? の後のクエリ文字列(/search?q=node なら req.query.q)を、req.body がリクエストボディで送られたデータを持ちます。req.params と req.query はどのリクエストでも埋まりますが、req.body は app.use(express.json()) のようなボディパーサーが読むまで undefined のままです。paramsとqueryの値は常に文字列です。
最初のリクエストは {"params":{"userId":"7"},"query":{"sort":"new","limit":"2"}} を返します。引用符に注目してください。7 と 2 は文字列なので、計算や数値との比較の前に Number() で変換しましょう。次にメソッドをPOSTに切り替え、パスは /users/7/posts のまま、ボディ {"title": "Hello", "draft": true} を送ってください。201 と "body":{"title":"Hello","draft":true} が返ります。この true はJSONから来たので、本物の真偽値です。
req.params、req.query、req.bodyの違い
req.params | req.query | req.body | |
|---|---|---|---|
| 出どころ | パス、/users/:id | クエリ文字列、?sort=new | リクエストボディ |
| URLやボディの例 | /users/7 | /users?sort=new | {"name": "Ada"} |
| 値 | { id: '7' } | { sort: 'new' } | { name: 'Ada' } |
| 型 | 文字列 | 文字列と文字列の配列 | JSONの中身のまま |
| 必要な設定 | ルートに :name | なし | app.use(express.json()) |
| 主な用途 | どのリソースか | 絞り込み、並べ替え、ページ | 作成や更新のデータ |
ルートはパラメーターを複数持てて、それぞれの名前が req.params のキーになります。/teams/:teamId/members/:memberId なら両方のidが手に入ります。ハイフンやドットでも区切れるので、/flights/:from-:to は /flights/LAX-SFO を { from: 'LAX', to: 'SFO' } にします。
重複したキーと欠けたキーのreq.query
2回出てくるキーは配列になり、ないキーは undefined になるので、オプションにはデフォルト値を与えましょう。
返事は {"page":1,"tags":["red","sale"],"raw":{"tag":["red","sale"]}} です。GET /products?tag=red&page=3 を送っても tags は配列 ["red"] のままです。[].concat() が文字列1つでも配列でも、配列に変えてくれるからです。Express 5はNode.jsのシンプルなパーサーでクエリ文字列をパースするので、?filter[size]=m はネストしたオブジェクトではなく "filter[size]" というキーになります。Express 4ではネストしていました。
req.bodyにはexpress.json()が必要
Node.jsはボディをバイトのストリームとして渡します。express.json() はそれを読んでパースし、req.body を設定しますが、リクエストに Content-Type: application/json があるときだけです。HTMLのフォームは application/x-www-form-urlencoded で送るので、代わりに express.urlencoded() が必要です。
ボディ {"email": "ada@example.com"} で POST /signup を送ると、201 と {"email":"ada@example.com","name":"anonymous"} が返ります。空のボディを送ると 400 です。?? {} が大事です。これがないと、JSONボディのないリクエストでは undefined を分割代入しようとして TypeError が投げられます。形や型をきちんとバリデーションするなら、zod のようなスキーマライブラリを使いましょう。
req.headersとreq.get()
req.headers はすべてのリクエストヘッダーを小文字の名前で持つオブジェクトです。req.get(name) はヘッダーを1つ読み、大文字と小文字を区別しないので、req.get('Content-Type') と req.get('content-type') は同じ値を返します。ほかにもよく使うプロパティがあります。
path は /whoami、originalUrl は /whoami?debug=1 です。リクエストバーでヘッダー Authorization: Bearer abc を足して送り直すと、token は "Bearer abc" になります。app.use('/api', router) でマウントしたルーターの中では、req.path から /api のプレフィックスが消え、req.originalUrl には残ります(マウントについてはExpressのルーティングで説明しています)。
よくある間違い
GETリクエストで req.body を読む。 ブラウザもほとんどのクライアントも、GETにボディを付けません。GETのオプションはクエリ文字列に入れましょう。
クエリのフラグをtruthyかどうかで判定する。 ?admin=false は文字列 'false' になり、これはtruthyなので if (req.query.admin) は通ってしまいます。文字列で比較しましょう。req.query.admin === 'true' です。
ヘッダーなしでJSONを送る。 JSONを text/plain として送るクライアントは req.body === undefined になります。クライアント側で Content-Type: application/json を設定しましょう。
よくある質問
ExpressでクライアントのIPアドレスを取得するには?
req.ip を読みます。プロキシやロードバランサーの後ろでは、app.set('trust proxy', 1) を設定するまではプロキシのアドレスになります。設定すると、Expressは代わりに X-Forwarded-For ヘッダーを読みます。
Expressでリクエストの完全なURLを取得するには?
req.originalUrl は、クライアントが送ったとおりのパスとクエリ文字列です。絶対URLは `${req.protocol}://${req.get('host')}${req.originalUrl}` で組み立てます。
Expressでファイルのアップロードを扱うには?
ファイルのアップロードは multipart/form-data で届き、express.json() や express.urlencoded() ではパースされません。multer のようなパッケージを使えば、ファイルは req.file か req.files に、ほかのフィールドは req.body に入ります。
リクエストからCookieを読むには?
生のヘッダーは req.get('cookie') にあります。req.cookies でオブジェクトとして受け取りたいなら、cookie-parser パッケージをインストールして app.use(cookieParser()) を追加します。