0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローカルAPIに curl を投げても「何も起きない」—原因の特定と直し方(Express + MongoDB 版)

Posted at

ChatGPT Image Aug 30, 2025 at 10_14_37 PM.png

1. 序論

npm run dev でサーバーを起動し、curl ... | jq ... でAPIの形を確認したのに 何も表示されない。初心者だけでなく経験者でもハマりやすい落とし穴です。今回は、実際に遭遇したケースをもとに、原因の切り分け→修正→動作確認までを一気に整理します。結論から言うと、主因は サーバーが待受前に落ちていた(DB接続失敗) と IPv4/IPv6 の食い違い でした。この記事では、中学生でもイメージできるように、ネットワークを「家の呼び鈴(ポート)」「住所(IP)」にたとえて説明します。

2. 本論

2-1. まず全体像:よくある原因4つ

  • サーバーが待受していない
    「呼び鈴(ポート)」自体が鳴らない状態です。で誰も3000番に座っていない=リスンしていないのが一発でわかります。
  • IPv4/IPv6 のズレ
    localhost は環境によって ::1(IPv6)に解決されることがあります。サーバーが 127.0.0.1(IPv4)だけで待受すると、localhost には繋がりません。接続先は住所(IP)を明示してあげましょう。
  • DB接続エラーで即終了
    app.listen() より後でDB接続に失敗→process.exit(1) でサーバーが即死。ログでは「立っているように見えて」すぐ消える、という紛らわしい症状になります。Mongooseの接続は成功してから listen するのが王道です。
  • パスやJSON構造の勘違い
    ルートが /api/todos なのに /todos を叩く、JSONが items ではなく data/meta なのに jq で .items を参照してしまう、など。Next.jsのAPIは /api/... 配下です。

たとえ話:

家(サーバー)が開いていても、呼び鈴(ポート)が鳴らなければ反応は返りません。住所(IP)が違っても当然届かないし、訪問先(パス)が違えば目的の人(エンドポイント)には会えません。

2-2. 最短の切り分けコマンド(コピペOK)

  • A. とにかく“エラーを見える化”(-s はいったん封印)
curl -v -m 5 http://127.0.0.1:3000/       # 詳細ログ + 5秒タイムアウト
  • B. 今、誰がベルを見張ってる?(3000番のLISTEN)
lsof -nP -iTCP:3000 -sTCP:LISTEN

何も出なければ 誰も待受していません。

  • C. IPv4/IPv6の両面チェック
curl -v -m 5 http://127.0.0.1:3000/
curl -v -m 5 http://[::1]:3000/

どちらか片方だけ通るなら サーバーのlistenアドレス を合わせます。

  • D. 代表的なポートを一括確認
for p in 3000 3001 5173 8080 4321 8787 4200 5000; do
  echo "---- $p ----"
  curl -I -m 2 -sS http://127.0.0.1:$p/ || true
done
  • E. JSONの形を必ず目視
curl -sS http://127.0.0.1:3000/todos | jq      # 全体を整形して見る
curl -sS http://127.0.0.1:3000/todos | jq 'keys'

jq の基本は公式マニュアルがわかりやすいです。

  • F. curl の基礎オプションは公式で
    -v(詳細)/-sS(静かだがエラーは表示)などは、公式「everything curl」やmanページを参照。

2-3. サーバー側の“落ちない起動”にする(Express + Mongoose)

問題点:DB接続に失敗したら process.exit(1) で即終了、listen が生き残らない。
解法:DB接続が成功してから app.listen()。ついでに IPv4へバインド、/health ルート、起動時エラーを可視化。

// server/server.js (CommonJS)
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const { connectDB } = require('./config/db');
const todosRouter = require('./routes/todos');
const errorHandler = require('./middlewares/error');

const app = express();
const PORT = process.env.PORT || 3000;
const HOST = '127.0.0.1'; // まずはIPv4に固定(安定動作)

// 最小の可観測性
app.use((req, _res, next) => { console.log(req.method, req.url); next(); });
app.get('/health', (_req, res) => res.json({ ok: true }));

app.use(cors());
app.use(express.json());
app.use('/todos', todosRouter);
app.use(errorHandler);

// 起動時の未処理エラーも表示
process.on('unhandledRejection', (r) => console.error('UNHANDLED REJECTION:', r));
process.on('uncaughtException', (e) => { console.error('UNCAUGHT EXCEPTION:', e); process.exit(1); });

async function start() {
  try {
    await connectDB(); // ✅ 成功してから listen
    const server = app.listen(PORT, HOST, () => {
      const addr = server.address();
      console.log(`Listening on ${addr.address}:${addr.port} (${addr.family})`);
    });
    server.on('error', (err) => console.error('Server listen error:', err));
  } catch (err) {
    console.error('Startup error (DB connection failed):', err);
    process.exit(1);
  }
}
start();
  • Expressのミドルウェア/エラーハンドリングは公式ガイド準拠です
  • Mongooseは127.0.0.1 を推奨(Node 18+ は localhost が IPv6 優先のため)

2-4. 動作確認(POST→GET→検索・ソート)

  • A. ダミーTODOを追加
curl -s -X POST http://127.0.0.1:3000/todos \
  -H "Content-Type: application/json" \
  -d '{"title":"テストタスク","status":"pending","tags":["work"]}' | jq
  • B. 一覧を取得(トップレベル形の確認)
curl -s http://127.0.0.1:3000/todos | jq
# 期待されるキー: items/page/limit/total/pages/sort/filters ...
  • C. 仕様ごとのスモークテスト
# 形チェック
curl -s "http://127.0.0.1:3000/todos?limit=5&page=1" \
| jq '.items|length,.page,.limit,.total,.pages'

# フィルタ
curl -s "http://127.0.0.1:3000/todos?status=pending&limit=3" \
| jq -r '.items[].status'

# タグ(OR)
curl -s "http://127.0.0.1:3000/todos?tag=work,urgent&limit=10" \
| jq -c '.items[].tags'

# タイトル検索 & ソート(昇順)
curl -s "http://127.0.0.1:3000/todos?q=readme&sort=createdAt:asc&limit=10" \
| jq -r '.items[].title'

補足:Next.jsなどAPIが /api/... の場合はベースパスを置き換えてください。
Next.js

3. まとめ

  • サーバーが待受していないと curl は沈黙します。まず lsof で LISTENの有無、curl -v で 接続可否 を可視化しよう
  • localhost の解決先は環境で異なります。127.0.0.1 / [::1] を明示して確認するのが近道
  • Mongoose接続は 成功後に app.listen()。/health で疎通を固定化し、起動時エラーは必ずログに出すようにしよう
  • JSONの「形」を jq でまず目視。そのうえで .items などのパスを合わせれば、仕様テストが安定します

4. 参考情報(情報元)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?