404 Not Found

404 Not Found


nginx

「エクスプレス・ベーシックス」

1. ネイティブHTTPからExpressへ

アリスは、ネイティブのHTTPモジュールを使ってAPIを記述するのに3日間を費やしましたが、コードは長くなり、メンテナンスが困難でした。URLを手作業で解析し、ルートマッチを手作業で記述し、リクエストボディを1行ずつ処理しなければならなかったからです。Expressに切り替えたところ、明確に定義されたルートと自動的にチェーンされるミドルウェアにより、同じ機能をわずか30行のコードで実装できるようになり、メンテナンス効率が10倍に向上しました。

ネイティブHTTPとExpressのコードの比較

比較 ネイティブHTTP Express
ルートの定義 手動 if/else マッチ req.url 宣言型 app.get('/path', fn)
リクエストボディの解析 data/end イベントを手動で監視し、バッファを構築 express.json() 1行で完了
応答送信済み res.writeHead() + res.end() res.json() / res.send()
静的ファイル ファイルを手動で読み込み、Content-Type を設定 express.static('public')
ミドルウェア 組み込みサポートなし app.use() パイプライン形式の構成
コード行数(同等の機能の場合) 約80行 約30行

2. インストールと「Hello World」

(1) プロジェクトの初期化とExpressのインストール

BASH
mkdir my-express-app && cd my-express-app
npm init -y
npm install express

(2) 最小限の「Hello World」

JAVASCRIPT
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});
BASH
node app.js

お使いのブラウザで http://localhost:3000 にアクセスするだけで、Hello World! を表示できます。

▶ 例:JSONレスポンスを返す

JAVASCRIPT
app.get('/api/hello', (req, res) => {
  res.json({ message: 'Hello from Express!', status: 'ok' });
});

▶ 例:ルーティングパラメータ付きの「Hello」

JAVASCRIPT
app.get('/hello/:name', (req, res) => {
  res.send(`Hello, ${req.params.name}!`);
});

3. ルーティング方式

エクスプレスルーティング方式のクイックリファレンス

手法 目的 冪等性 代表的なシナリオ
app.get() リソースを取得 はい リスト/詳細を表示
app.post() リソースを作成 いいえ フォームを送信/レコードを追加
app.put() 完全更新 はい レコード全体を置き換える
app.delete() リソースを削除 はい レコードを削除
app.patch() 部分更新 いいえ 個々のフィールドを編集
app.all() すべてのメソッドに一致 一般的な前処理

(1) RESTfulスタイルのルーティング

JAVASCRIPT
app.get('/users', (req, res) => { res.json({ users: [] }); });
app.post('/users', (req, res) => { res.status(201).json({ created: true }); });
app.put('/users/:id', (req, res) => { res.json({ updated: req.params.id }); });
app.delete('/users/:id', (req, res) => { res.json({ deleted: req.params.id }); });

(2) ルートマッチング

▶ 例:クエリパラメータの読み取り

JAVASCRIPT
app.get('/search', (req, res) => {
  const { q, page = '1' } = req.query;
  res.json({ keyword: q, page: Number(page) });
});

▶ 例:複数のルートパラメータ

JAVASCRIPT
app.get('/posts/:postId/comments/:commentId', (req, res) => {
  res.json(req.params);
});

4. ミドルウェアの概念

Express の設計の中核をなすのは、ミドルウェア・パイプラインです。各リクエストは一連の関数を順次通過し、各関数はリクエストやレスポンスを読み取ったり変更したり、あるいはリクエストを途中で打ち切ったりすることができます。

100%
flowchart LR
  A[Request Request] --> B[Middleware1<br/>Logging]
  B --> C[Middleware2<br/>JSONAnalysis]
  C --> D[Middleware3<br/>Authentication and Validation]
  D --> E[Route Handling<br/>Business Logic]
  E --> F[Response Response]

(1) app.use() を使用してミドルウェアを登録する

JAVASCRIPT
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
};
app.use(logger);

(2) ミドルウェアの実行順序

▶ 例:認証ミドルウェア

JAVASCRIPT
const auth = (req, res, next) => {
  const token = req.headers['authorization'];
  if (!token) return res.status(401).json({ error: 'No token' });
  next();
};
app.use('/api', auth);

▶ 例:エラー処理ミドルウェア

JAVASCRIPT
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});

5. 組み込みミドルウェア

組み込みミドルウェアの一覧

ミドルウェア 目的 実装 一般的な構成
express.json() JSONリクエスト本文の解析 app.use(express.json()) { limit: '10kb' }
express.urlencoded() URLエンコードされたリクエスト本文の解析 app.use(express.urlencoded({ extended: true })) { extended: true/false }
express.static() 静的ファイルのホスティング app.use(express.static('public')) { maxAge: '1d' }

(1) express.json() を使用したリクエスト本文の解析

JAVASCRIPT
app.use(express.json());

app.post('/api/users', (req, res) => {
  console.log(req.body);
  res.json({ received: req.body });
});

(2) 静的ファイルの配信には express.static() を使用する

想定されるプロジェクト構成:

TEXT
my-express-app/
├── public/
│   ├── index.html
│   └── style.css
├── app.js
└── package.json
JAVASCRIPT
app.use(express.static('public'));

静的ファイルを直接読み込むには、http://localhost:3000/index.html にアクセスしてください。

▶ 例:複数のディレクトリを持つ静的ホスティング

JAVASCRIPT
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));

▶ 例:JSONリクエスト本文のサイズ制限

JAVASCRIPT
app.use(express.json({ limit: '100kb' }));

6. nodemon ホットリロード

nodemon と node の比較

比較項目 node nodemon
ファイルの変更後 手動再起動 自動再起動
設置方法 組み込み型 npm i -D nodemon
起動コマンド node app.js npx nodemon app.js
本番環境 適用 非適用
ディレクトリの指定 なし デフォルト:現在のディレクトリ;設定可能

(1) インストールと使用方法

BASH
npm install --save-dev nodemon
npx nodemon app.js

(2) package.json でのスクリプトの設定

JAVASCRIPT
{
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  }
}

開発時には npm run dev(自動リロード)を、本番環境では npm start(安定稼働)を使用してください。

▶ 例:カスタム監視ディレクトリ

BASH
npx nodemon --watch src --ext js,ets app.js

7. 総合的な例:Express API アプリケーション

これまでに学んだ概念をすべて結びつけ、完全な小規模なAPIアプリケーションをゼロから構築してみましょう。

JAVASCRIPT
const express = require('express');
const app = express();

app.use(express.json());
app.use(express.static('public'));

let todos = [
  { id: 1, task: 'Learn Express', done: false },
  { id: 2, task: 'Build an API', done: false },
];

app.get('/api/todos', (req, res) => res.json(todos));

app.post('/api/todos', (req, res) => {
  const todo = { id: todos.length + 1, ...req.body, done: false };
  todos.push(todo);
  res.status(201).json(todo);
});

app.put('/api/todos/:id', (req, res) => {
  const idx = todos.findIndex(t => t.id === Number(req.params.id));
  if (idx === -1) return res.status(404).json({ error: 'Not found' });
  todos[idx] = { ...todos[idx], ...req.body };
  res.json(todos[idx]);
});

app.delete('/api/todos/:id', (req, res) => {
  todos = todos.filter(t => t.id !== Number(req.params.id));
  res.json({ deleted: true });
});

app.listen(3000, () => console.log('API running at http://localhost:3000'));

動作を開始したら、ブラウザから静的ページにアクセスしたり、APIツールを使用して/api/todosに対してCRUD操作を実行したりすることができます。


❓ よくある質問

Q ExpressとKoaの違いは何ですか?
A Expressはコールバック・ミドルウェアを基盤としており、充実したエコシステムを備えています。一方、Koaはasync/awaitを基盤としており、より軽量です。初心者にとっては、エコシステムがより成熟しているExpressの方が適しています。
Q app.useapp.get の違いは何ですか?
A app.use はすべてのリクエストメソッドに一致し、一般的にミドルウェアに使用されます。一方、app.get は GET リクエストにのみ一致し、ルートの定義に使用されます。
Q ミドルウェアの実行順序はどのようになっていますか?
A ミドルウェアは、app.use または app.get を通じて登録された順序で実行されます。next() を呼び出すと次のミドルウェアに進み、next() が呼び出されない場合、実行は終了します。
Q 404エラーにはどのように対処すればよいですか?
A すべてのルートの後にパスレスミドルウェアを登録し、404エラーを生成して、それをエラー処理ミドルウェアに渡します。
Q Expressは並行処理に対応していますか?
A Express自体はシングルスレッドですが、Node.jsのイベントループにより、I/Oの並行処理を効率的に行うことができます。CPU負荷の高いタスクには、ワーカースレッドが必要です。

Q: Expressだけが選択肢なのでしょうか? A: いいえ。Koaは軽量で、Fastifyは高速、Honoはエッジコンピューティングに対応していますが、Expressはエコシステムが最も成熟しており、学習リソースも最も充実しているため、初心者にとっては最適な選択肢と言えます。

Q: express.json() はルートの前に呼び出す必要がありますか? A: はい、app.use(express.json()) はルートの前に記述する必要があります。そうしないと、ミドルウェアは登録された順序で実行されるため、req.bodyundefined になってしまいます。

Q: 4.xと5.xの違いは何ですか? A: Express 5.xでは、非推奨となったAPI(app.delなど)が削除され、ルートマッチングが改善され(path-to-regexp v8に対応)、一部の動作が非同期処理に適した形に改良されていますが、基本的な使い方はほぼ同じです。

Q: サービスを自動的に再起動するにはどうすればよいですか? A: nodemon (npm i -D nodemon) をインストールし、npx nodemon app.js で起動すると、ファイルの保存後に自動的に再起動されます。これは開発中に非常に便利です。

Q: app.useapp.get の違いは何ですか? A: app.use はすべての HTTP メソッドに一致し、パスのプレフィックスにも一致します(/api/api/anything に一致します)。一方、app.get は GET メソッドにのみ一致し、パスが完全に一致する必要があります。前者はミドルウェア用、後者はルーティング用として使用されます。

Q: express.urlencodedextended オプションとはどういう意味ですか? A: extended: trueは解析にqsライブラリを使用します(ネストされたオブジェクトに対応)。extended: falsequerystringライブラリを使用します(ネストされたオブジェクトには対応していません)。また、trueはフォーム送信には通常、十分です。


📖 まとめ


📝 練習問題

  1. このレッスンにあるすべてのコード例を完成させ、それぞれが正しく動作することを確認してください。
  2. 包括的な例を修正し、独自の拡張機能を追加する
  3. 公式ドキュメントを確認し、このレッスンで取り上げられていないAPIを1~2つ見つけ、それらのテストコードを作成してください。
  4. 振り返り:このレッスンで学んだことを、実際のプロジェクトにどのように活かしますか?
  5. このレッスンで学んだことと、これまでのレッスンの内容を組み合わせて、小さなプロジェクトを作成してみてください。
Web-Tutorial.com

Web-Tutorial 技術チーム

複数の開発者によって共同維持されているプログラミングチュートリアルプラットフォーム。各チュートリアルは専門分野の開発者が執筆・レビューしています。正確で信頼性の高いコンテンツを目指しています — 問題を見つけた場合はお知らせください。

100%