MeWrite Docs

Node.js: Cannot read properties of undefined

Node.jsでreq.body/環境変数/APIレスポンス処理時に発生するTypeError

概要

Node.jsでTypeError: Cannot read properties of undefinedが発生する主な原因は、リクエストボディの欠損、環境変数の未設定、外部APIレスポンスの想定外の形式などです。サーバーサイドでは入力値の検証が特に重要になります。

エラーメッセージ

TypeError: Cannot read properties of undefined (reading 'name')
TypeError: Cannot read properties of undefined (reading 'user')

発生パターン

パターン1: req.bodyのパース設定忘れ

1
2
3
4
5
6
7
8
const express = require('express');
const app = express();

// NG: express.json()がないとreq.bodyはundefined
app.post('/users', (req, res) => {
  const name = req.body.name; // TypeError
  res.json({ name });
});

パターン2: 期待するプロパティがリクエストにない

1
2
3
4
5
app.post('/users', (req, res) => {
  // クライアントが { user: { firstName: "John" } } を送った場合
  // req.body.user.name は undefined
  const name = req.body.user.name; // undefinedのプロパティアクセス
});

パターン3: 環境変数が未設定

1
2
3
// .envファイルに DATABASE_URL がない場合
const dbUrl = process.env.DATABASE_URL;
const host = dbUrl.split('@')[1]; // TypeError: Cannot read properties of undefined

パターン4: 外部APIレスポンスの形式が想定外

1
2
3
4
5
const response = await fetch('https://api.example.com/user/1');
const data = await response.json();

// APIがエラー時に { error: "Not found" } を返した場合
const userName = data.user.name; // TypeError

解決策

1. ミドルウェアの設定確認

Express.jsでJSONボディをパースするには設定が必要です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
const express = require('express');
const app = express();

// JSONボディのパース(必須)
app.use(express.json());

// URLエンコードされたボディのパース
app.use(express.urlencoded({ extended: true }));

app.post('/users', (req, res) => {
  console.log(req.body); // パースされたオブジェクト
});

2. オプショナルチェイニングとバリデーション

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
app.post('/users', (req, res) => {
  // オプショナルチェイニングで安全にアクセス
  const name = req.body?.user?.name;

  if (!name) {
    return res.status(400).json({ error: 'Name is required' });
  }

  res.json({ message: `Hello, ${name}` });
});

3. バリデーションライブラリを使用

Joi や Zod でスキーマ検証を行います。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// Zodの例
const { z } = require('zod');

const userSchema = z.object({
  user: z.object({
    name: z.string().min(1),
    email: z.string().email()
  })
});

app.post('/users', (req, res) => {
  const result = userSchema.safeParse(req.body);

  if (!result.success) {
    return res.status(400).json({ errors: result.error.issues });
  }

  const { name, email } = result.data.user;
  res.json({ name, email });
});

4. 環境変数のチェックと早期エラー

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// 起動時に必須環境変数をチェック
const requiredEnvVars = ['DATABASE_URL', 'API_KEY', 'JWT_SECRET'];

for (const envVar of requiredEnvVars) {
  if (!process.env[envVar]) {
    console.error(`Missing required environment variable: ${envVar}`);
    process.exit(1);
  }
}

// または dotenv-safe を使用
require('dotenv-safe').config({
  example: '.env.example'  // 必須変数を定義したファイル
});

5. Null合体演算子でデフォルト値

1
2
3
4
5
6
// 環境変数にデフォルト値
const port = process.env.PORT ?? 3000;
const nodeEnv = process.env.NODE_ENV ?? 'development';

// APIレスポンスにデフォルト値
const userName = data?.user?.name ?? 'Anonymous';

6. 外部APIレスポンスの検証

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
async function fetchUser(userId) {
  const response = await fetch(`https://api.example.com/users/${userId}`);

  if (!response.ok) {
    throw new Error(`API error: ${response.status}`);
  }

  const data = await response.json();

  // レスポンス形式を検証
  if (!data?.user?.name) {
    throw new Error('Invalid API response format');
  }

  return data.user;
}

// 使用時
app.get('/users/:id', async (req, res) => {
  try {
    const user = await fetchUser(req.params.id);
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

7. TypeScriptで型安全にする

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import express, { Request, Response } from 'express';
import { z } from 'zod';

const userSchema = z.object({
  user: z.object({
    name: z.string(),
    email: z.string().email()
  })
});

type UserRequest = z.infer<typeof userSchema>;

app.post('/users', (req: Request, res: Response) => {
  const result = userSchema.safeParse(req.body);

  if (!result.success) {
    return res.status(400).json({ errors: result.error.issues });
  }

  // result.data は型安全
  const { name, email } = result.data.user;
  res.json({ name, email });
});

グローバルエラーハンドリング

予期しないエラーをキャッチして適切に処理します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 非同期エラーをキャッチするラッパー
const asyncHandler = (fn) => (req, res, next) => {
  Promise.resolve(fn(req, res, next)).catch(next);
};

app.get('/users/:id', asyncHandler(async (req, res) => {
  const user = await fetchUser(req.params.id);
  res.json(user);
}));

// エラーハンドリングミドルウェア
app.use((err, req, res, next) => {
  console.error(err.stack);

  if (err instanceof TypeError) {
    return res.status(400).json({
      error: 'Invalid data format',
      message: err.message
    });
  }

  res.status(500).json({ error: 'Internal server error' });
});

よくある間違い

  • express.json()の設定忘れ: Content-Type が application/json でないリクエストでもエラーになる
  • 環境変数のロード順序: dotenvのconfig()はアプリ起動の最初に呼ぶ
  • awaitのつけ忘れ: 非同期関数の結果がPromiseのまま処理される

関連エラー

関連エラー

Node.js の他のエラー

最終更新: 2026-02-03