Express で req.body が undefined になり POST の値を受け取れない
req.body はボディを解析するミドルウェアを通ったときだけ値が入る。
express.json() の登録漏れや登録順、Content-Type の不一致、multipart の送信が主な原因で、Express 5 では解析されないと {} ではなく undefined になる。
公開: 更新:
要約
Express の req.body は、ボディを解析するミドルウェアを通ったときだけ値が入る。公式リファレンス(新しいタブで開く)のとおり既定は undefined で、express.json() や express.urlencoded() を登録して初めて中身が入る。
const express = require("express");
const app = express();
app.use(express.json()); // これが無いと req.body は undefined のまま
app.post("/users", (req, res) => {
res.json({ received: req.body });
});ミドルウェアを登録していても、リクエストの Content-Type がパーサーの対象と一致しなければ解析されない。
このとき Express 4 では req.body が {} になり、Express 5 では undefined のままになる。
実行例
Express 5 のサーバーで express.json() を登録しないまま JSON を POST すると Internal Server Error が返り、サーバーのログには TypeError: Cannot read properties of undefined (reading 'name') が出た。express.json() を登録すると Content-Type: application/json 付きの送信は値を受け取れたが、curl の -d だけで送ると同じ TypeError に戻り、req.body ?? {} で読む形に書き換えると 400 Bad Request を返せた。
$ NODE_ENV=production node server.js > server.log 2>&1 &$ curl -s -X POST -H "Content-Type: application/json" -d '{"name":"taro"}' http://localhost:3000/users
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Internal Server Error</pre>
</body>
</html>$ grep -m1 TypeError server.log
TypeError: Cannot read properties of undefined (reading 'name')$ NODE_ENV=production node server.js > server.log 2>&1 &$ curl -s -X POST -H "Content-Type: application/json" -d '{"name":"taro"}' http://localhost:3000/users
{"name":"taro"}$ curl -s -X POST -d '{"name":"taro"}' http://localhost:3000/users
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Internal Server Error</pre>
</body>
</html>$ grep -m1 TypeError server.log
TypeError: Cannot read properties of undefined (reading 'name')$ NODE_ENV=production node server.js > server.log 2>&1 &$ curl -s -i -X POST -d '{"name":"taro"}' http://localhost:3000/users
HTTP/1.1 400 Bad Request
X-Powered-By: Express
Content-Type: application/json; charset=utf-8
Content-Length: 28
ETag: W/"1c-Rot2BI1sDts2rb2R9pfKSqROplo"
Date: Wed, 30 Sep 2026 01:03:46 GMT
Connection: keep-alive
Keep-Alive: timeout=5
{"error":"name is required"}— 2026-09-30 時点の出力
検証環境
- 検証日
- 実行環境
node:20Debian GNU/Linux 12 (bookworm)- バージョン
- Node.js 20.20.2
- npm 10.8.2
- Python 3.11.2
- Git 2.39.5
この記事の「実行例」は、上記の環境で実際にコマンドを実行して得られた出力をそのまま掲載しています。 再現手順はリポジトリの検証スクリプトとして管理し、定期的に再実行して出力を更新しています。
よくある原因
- パーサーを登録していない: Express は最小構成で、ボディを読む処理は自分で差し込む。
express.json()とexpress.urlencoded()は Express 4.16.0 から組み込まれているので、body-parserを別途入れる必要はない。 - 登録の順番が遅い: ミドルウェアは登録した順に実行される。
app.post()の後にapp.use(express.json())を書くと、そのルートに届いた時点ではまだ解析されていない。 Content-Typeが一致していない:express.json()のtypeオプションの既定はapplication/json。
ヘッダーを付けずにfetch()へ文字列を渡すとtext/plain;charset=UTF-8、curl の-dだけならapplication/x-www-form-urlencodedで送られ、どちらも対象から外れる。multipart/form-dataを送っている:FormDataをbodyに渡すと multipart で送られるが、express.json()もexpress.urlencoded()も multipart は解析しない。- Express 5 への移行: 移行ガイド(新しいタブで開く)にあるとおり、解析されなかったときの
req.bodyは Express 4 の{}から Express 5 ではundefinedに変わった。
パーサーを登録していれば 4 系ではundefinedを返すだけだったreq.body.nameが、5 系ではTypeError: Cannot read properties of undefined (reading 'name')で落ちる。
解決策
1. パーサーをルートより前で登録する
const express = require("express");
const app = express();
app.use(express.json()); // application/json
app.use(express.urlencoded({ extended: false })); // HTML フォーム
app.post("/users", (req, res) => {
res.json(req.body);
});Express 5 では express.urlencoded() の extended の既定が false になった。user[name]=taro のような入れ子のキーをオブジェクトとして受け取りたいときは { extended: true } を明示する。
2. クライアントで Content-Type を付けて送る
await fetch("/users", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "taro" }),
});curl で試すときも同じヘッダーを付ける。
curl -X POST -H "Content-Type: application/json" -d '{"name":"taro"}' http://localhost:3000/users3. multipart は multer で解析する
const multer = require("multer");
const upload = multer();
app.post("/profile", upload.none(), (req, res) => {
res.json(req.body); // テキストのフィールドが入る
});ファイルも受け取るなら upload.single("avatar") などを使い、ファイルは req.file に入る。
4. 解析されていない場合を考えてから読む
app.post("/users", (req, res) => {
const { name } = req.body ?? {};
if (!name) return res.status(400).json({ error: "name is required" });
res.json({ name });
});