Node.jsのhttpモジュールでGET/POSTリクエストを処理する方法


Node.jsのhttpモジュール完全ガイド:GET/POSTリクエストをゼロから処理する方法

はじめに

Node.jsは、そのイベント駆動型のノンブロッキングI/Oモデルにより、スケーラブルで高性能なネットワークアプリケーションを構築するための強力なプラットフォームとして、現代のWeb開発において不動の地位を築いています。サーバーサイドJavaScriptの代名詞ともいえるNode.jsですが、その心臓部には、Webサーバーの根幹をなすHTTPプロトコルを扱うためのコアモジュールが存在します。それが「httpモジュール」です。

多くの開発者は、Express.js、Koa.js、Fastifyといった高機能なWebフレームワークを通じてNode.jsに触れることがほとんどでしょう。これらのフレームワークは、ルーティング、ミドルウェア、テンプレートエンジンとの連携など、複雑なWebアプリケーション開発を劇的に簡素化してくれます。しかし、その便利な抽象化の裏側で、一体何が起こっているのでしょうか?その答えを握っているのが、httpモジュールです。

この記事では、あえてフレームワークの力を借りず、Node.jsに標準で組み込まれているhttpモジュールのみを使って、基本的なWebサーバーを構築する方法をゼロから徹底的に解説します。この記事を読み終える頃には、あなたは以下の知識を習得しているはずです。

  • httpモジュールを使って基本的なHTTPサーバーを起動する方法
  • リクエストオブジェクト(req)とレスポンスオブジェクト(res)の役割
  • URLに基づいたルーティングを実装し、GETリクエストを処理する方法
  • クエリ文字列をパースして、GETリクエストからデータを取得する方法
  • Node.jsにおけるストリームの概念を理解し、POSTリクエストのボディデータを正しく受信する方法
  • application/x-www-form-urlencodedapplication/jsonといった一般的なデータ形式をパースする方法
  • これまでの知識を統合し、簡単なWebアプリケーションを構築する実践的なスキル

なぜ今、httpモジュールを学ぶ必要があるのでしょうか?それは、Node.jsのコアな動作原理を深く理解するためです。フレームワークが提供する魔法のような機能の裏側を知ることで、あなたはより優れた問題解決能力を身につけ、パフォーマンスのボトルネックを特定し、フレームワークをより効果的に使いこなすことができるようになります。いわば、自動車の運転方法だけでなく、エンジンの仕組みを学ぶようなものです。

本記事は、Node.jsの基礎を固めたい初心者から、フレームワークの内部構造に興味を持つ中級者まで、幅広い読者を対象としています。さあ、Node.jsの原点ともいえるhttpモジュールの世界へ、一緒に深く潜っていきましょう。

Node.jsとHTTPの基礎

httpモジュールの詳細に入る前に、その土台となるHTTPプロトコルとNode.jsの基本的な動作原理について簡単におさらいしておきましょう。

HTTPプロトコル:Webの通信言語

HTTP(HyperText Transfer Protocol)は、Webブラウザ(クライアント)とWebサーバーが情報をやり取りするための基本的なルール(プロトコル)です。その通信は、常にクライアントからの「リクエスト」と、それに対するサーバーからの「レスポンス」という一対のメッセージで構成されます。

  • HTTPリクエスト: クライアントがサーバーに送るメッセージです。主に以下の要素で構成されます。

    • メソッド (Method): リクエストの目的を示す動詞です。
      • GET: 特定のリソース(Webページ、画像、データなど)を取得するために使います。
      • POST: サーバーにデータを送信し、新しいリソースを作成するために使います(例:フォームの送信、ユーザー登録)。
      • 他にもPUT (更新), DELETE (削除), PATCH (部分的更新) などがあります。
    • URL (Uniform Resource Locator): リクエスト対象のリソースの場所を示します。例:/users/123
    • ヘッダー (Headers): リクエストに関する追加情報(メタデータ)です。例:Content-Type: application/json(送信するデータ形式)、Authorization: Bearer ...(認証情報)。
    • ボディ (Body): POSTPUTリクエストで、サーバーに送信する実際のデータを含みます。GETリクエストには通常ボディはありません。
  • HTTPレスポンス: サーバーがクライアントに返すメッセージです。

    • ステータスコード (Status Code): リクエストが成功したか、失敗したかを示す3桁の数字です。
      • 200 OK: リクエスト成功。
      • 201 Created: リソースの作成に成功(主にPOSTリクエストへの応答)。
      • 404 Not Found: リクエストされたリソースが見つからない。
      • 500 Internal Server Error: サーバー内部でエラーが発生した。
    • ヘッダー (Headers): レスポンスに関する追加情報です。例:Content-Type: text/html(返すデータの形式)、Set-Cookie: ...(クッキーの設定)。
    • ボディ (Body): クライアントに返す実際のコンテンツ(HTML、JSONデータ、画像など)です。

GETリクエスト vs POSTリクエスト

この記事の主役であるGETPOSTの違いを明確にしておきましょう。

  • GETリクエスト:

    • 目的: データの取得。
    • データ送信: URLの末尾に「クエリ文字列」として付与します(例:/search?q=nodejs&page=1)。
    • 特性:
      • ブラウザのブックマークや履歴に残る。
      • べき等(Idempotent):何度同じリクエストを送っても、サーバー上のリソース状態は変わらない。
      • 送信できるデータ量に制限がある(URLの長さに依存)。
      • 機密情報(パスワードなど)の送信には不向き。
  • POSTリクエスト:

    • 目的: データの送信、リソースの作成・更新。
    • データ送信: HTTPリクエストの「ボディ」に含めて送信します。
    • 特性:
      • ブックマークや履歴には残らない。
      • べき等ではない:同じリクエストを複数回送ると、新しいリソースが複数作成される可能性がある。
      • 送信できるデータ量に実質的な制限はない。
      • フォームデータやJSONなど、複雑で大きなデータ、機密情報の送信に適している。

Node.jsのイベントループとコールバック

Node.jsの最大の特徴は「イベント駆動型ノンブロッキングI/O」です。これは、時間のかかる処理(ファイル読み書き、データベースクエリ、ネットワーク通信など)をバックグラウンドで実行させ、その処理が終わるのを待たずに次のタスクに進む仕組みです。処理が完了すると、「イベント」が発生し、あらかじめ登録しておいた「コールバック関数」が実行されます。

httpモジュールもこのモデルに完全に基づいています。サーバーはリクエストが来るのを待ち構え、リクエストが到着するという「イベント」が発生すると、指定されたコールバック関数(リクエストハンドラ)を呼び出して処理を開始します。この仕組みのおかげで、Node.jsサーバーは多くの同時接続を効率的にさばくことができるのです。

基本的なHTTPサーバーの作成

それでは、いよいよhttpモジュールを使って、世界で最もシンプルなWebサーバーを構築してみましょう。以下のコードをserver.jsという名前で保存してください。

“`javascript
// 1. httpモジュールをインポートする
const http = require(‘http’);

// 2. サーバーのホスト名とポートを定義する
const hostname = ‘127.0.0.1’; // ローカルホスト
const port = 3000;

// 3. HTTPサーバーを作成する
// http.createServer()は、リクエストが来るたびに実行される関数(リクエストリスナー)を引数に取る
const server = http.createServer((req, res) => {
// 4. レスポンスのステータスコードとヘッダーを設定する
// 200は「OK」を意味するステータスコード
// Content-Typeヘッダーは、レスポンスボディのデータ形式をクライアントに伝える
res.writeHead(200, { ‘Content-Type’: ‘text/plain; charset=utf-8’ });

// 5. レスポンスボディを書き込み、レスポンスを終了する
res.end(‘こんにちは、世界!\n’);
});

// 6. 指定したポートとホスト名でリクエストの待受を開始する
server.listen(port, hostname, () => {
// サーバーが待受状態になったときに実行されるコールバック関数
console.log(サーバーが http://${hostname}:${port}/ で起動しました。);
});
“`

この短いコードを一行ずつ分解して、何が行われているのかを詳しく見ていきましょう。

  1. const http = require('http');
    Node.jsのコアモジュールであるhttpを読み込んで、httpという定数に代入しています。これで、httpモジュールが提供する機能(createServerなど)を使えるようになります。

  2. const hostname = '127.0.0.1'; const port = 3000;
    サーバーを起動するネットワークインターフェースとポート番号を定義しています。127.0.0.1は「localhost」とも呼ばれ、自分自身のコンピュータを指します。3000は、開発用によく使われるポート番号です。

  3. const server = http.createServer((req, res) => { ... });
    これがサーバー構築の核となる部分です。http.createServer()メソッドは、新しいHTTPサーバーオブジェクトを作成して返します。このメソッドは引数としてリクエストリスナー関数を受け取ります。この関数は、サーバーにHTTPリクエストが届くたびに自動的に呼び出されます。

    この関数は2つの重要な引数を受け取ります。
    * req (Request): http.IncomingMessageクラスのインスタンスで、クライアントからのリクエストに関する情報(URL、HTTPメソッド、ヘッダーなど)が詰まっています。
    * res (Response): http.ServerResponseクラスのインスタンスで、クライアントに返すレスポンスを構築するためのメソッド(ヘッダーの設定、データの書き込みなど)を提供します。

  4. res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
    resオブジェクトのwriteHeadメソッドを使って、レスポンスヘッダーを書き込んでいます。

    • 第一引数の200は、HTTPステータスコードで、「リクエストは成功しました (OK)」という意味です。
    • 第二引数は、ヘッダー情報を格納したオブジェクトです。ここではContent-Typeヘッダーを設定しています。text/plainは、レスポンスのボディがプレーンテキストであることを示します。charset=utf-8を指定することで、日本語などのマルチバイト文字が文字化けするのを防ぎます。
  5. res.end('こんにちは、世界!\n');
    resオブジェクトのendメソッドは、レスポンスの送信を完了させるための非常に重要なメソッドです。このメソッドは、以下の2つの役割を同時に果たします。

    • 引数に渡されたデータ(ここでは文字列'こんにちは、世界!\n')をレスポンスボディの最後の部分として送信します。
    • サーバーに「このレスポンスは完了しました」と伝え、ソケットを閉じます。このメソッドを呼び出さないと、クライアントはレスポンスを永遠に待ち続けることになります。
  6. server.listen(port, hostname, () => { ... });
    作成したサーバーオブジェクトのlistenメソッドを呼び出して、指定したポートとホスト名でクライアントからの接続を待ち受けるように指示します。

    • 第三引数はオプションのコールバック関数で、サーバーが正常に起動し、待受状態になったときに一度だけ実行されます。ここでは、コンソールに確認メッセージを表示しています。

サーバーの実行と確認

このserver.jsファイルを実行するには、ターミナル(コマンドプロンプト)を開き、ファイルがあるディレクトリで以下のコマンドを入力します。

bash
node server.js

ターミナルに「サーバーが http://127.0.0.1:3000/ で起動しました。」と表示されれば成功です。

次に、Webブラウザを開き、アドレスバーに http://127.0.0.1:3000 と入力してエンターキーを押してください。ブラウザの画面に「こんにちは、世界!」と表示されるはずです。

また、curlコマンドを使える環境であれば、ターミナルから以下のようにリクエストを送ることもできます。

bash
curl -i http://127.0.0.1:3000/

-iオプションは、レスポンスヘッダーも一緒に表示するためのものです。以下のような出力が得られるでしょう。

“`
HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Date: [現在の日時]
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 22

こんにちは、世界!
“`

writeHeadで設定したステータスコードとContent-Typeが正しく返されていることが確認できます。

GETリクエストの処理

現在のサーバーは、どのURLにアクセスしても同じ「こんにちは、世界!」というメッセージを返します。http://127.0.0.1:3000/abouthttp://127.0.0.1:3000/users/1 にアクセスしても結果は同じです。

実用的なWebサーバーを構築するには、リクエストされたURLに応じて異なるレスポンスを返す「ルーティング」機能が必要です。これは、reqオブジェクトのurlプロパティを調べることで実現できます。

URLに基づいた基本的なルーティング

req.urlプロパティには、ホスト名とポート番号を除いたURLのパス部分(クエリ文字列も含む)が格納されています。これを使って、サーバーのロジックを分岐させてみましょう。

server.jsを以下のように書き換えます。

“`javascript
const http = require(‘http’);

const server = http.createServer((req, res) => {
// リクエストされたURLパスで処理を分岐
if (req.url === ‘/’) {
res.writeHead(200, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(‘

トップページへようこそ!

‘);
} else if (req.url === ‘/about’) {
res.writeHead(200, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(‘

これはAboutページです。

‘);
} else {
// どのURLにも一致しない場合
res.writeHead(404, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(‘

404 Not Found

ページが見つかりません。

‘);
}
});

const port = 3000;
server.listen(port, () => {
console.log(サーバーが http://localhost:${port}/ で起動しました。);
});
“`

このコードでは、if/else if/else構文を使ってreq.urlの値をチェックしています。

  • / にアクセスすると、トップページのHTMLが返されます。
  • /about にアクセスすると、AboutページのHTMLが返されます。
  • それ以外のURL(例:/contact)にアクセスすると、ステータスコード404 Not Foundと共にエラーページが返されます。

Content-Typetext/htmlに変更したことで、ブラウザはレスポンスをHTMLとして解釈し、<h1>タグなどを正しくレンダリングします。

クエリ文字列の解析

GETリクエストは、URLにクエリ文字列を含めることで、サーバーに少量のデータを渡すことができます。例えば、検索機能では https://example.com/search?keyword=nodejs のように、?以降にキー=値のペアでデータを付加します。

httpモジュールだけでは、このクエリ文字列を簡単にパースする機能はreqオブジェクトに直接備わっていません。req.urlには /search?keyword=nodejs のような文字列がそのまま入ってきます。これをパースするには、Node.jsの別のコアモジュールである url を使うのが伝統的な方法です。

server.jsをさらに改良して、クエリ文字列を処理するコードを追加してみましょう。

“`javascript
const http = require(‘http’);
// urlモジュールをインポート
const url = require(‘url’);

const server = http.createServer((req, res) => {
// URLをパースする
// 第二引数に true を指定すると、queryプロパティがオブジェクトにパースされる
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname; // パス部分 (/search)
const query = parsedUrl.query; // クエリ部分 ({ keyword: ‘nodejs’ })

res.writeHead(200, { ‘Content-Type’: ‘text/html; charset=utf-8’ });

if (pathname === ‘/’) {
res.end(‘

トップページ

‘);
} else if (pathname === ‘/search’) {
const keyword = query.keyword || ‘指定なし’;
res.end(<h1>検索結果</h1><p>キーワード「${keyword}」で検索しました。</p>);
} else {
res.writeHead(404, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(‘

404 Not Found

‘);
}
});

const port = 3000;
server.listen(port, () => {
console.log(サーバーが http://localhost:${port}/ で起動しました。);
});
“`

このコードのポイントは url.parse(req.url, true) です。
* url.parse()関数は、URL文字列をパースして、各構成要素(プロトコル、ホスト、パス名、クエリなど)を含むオブジェクトを返します。
* 第二引数に true を渡すのが非常に重要です。これにより、クエリ文字列(keyword=nodejs&lang=jaなど)が自動的に解析され、queryプロパティに { keyword: 'nodejs', lang: 'ja' } のような便利なオブジェクト形式で格納されます。
* parsedUrl.pathname を使ってルーティングを行い、/search のパスでは parsedUrl.query オブジェクトから keyword プロパティの値を取り出してレスポンスに含めています。

このサーバーを起動して、ブラウザで http://localhost:3000/search?keyword=Node.js入門 にアクセスしてみてください。「キーワード「Node.js入門」で検索しました。」と表示されるはずです。

補足: URLクラスを使った現代的な方法
Node.js v7以降では、ブラウザ環境でも使われているWHATWGの URL APIが利用可能です。こちらの方がよりモダンで標準的な方法と言えます。

``javascript
// ...
const server = http.createServer((req, res) => {
// http://localhost:3000 のようなベースURLを第二引数に与える必要がある
const requestUrl = new URL(req.url,
http://${req.headers.host}`);

const pathname = requestUrl.pathname;
const keyword = requestUrl.searchParams.get(‘keyword’); // クエリパラメータの取得

// …
});
``URLクラスのインスタンスはsearchParamsというプロパティを持ち、これはURLSearchParamsオブジェクトです。.get(‘キー’)メソッドで特定のパラメータを簡単に取得できます。どちらの方法を使うかは好みやプロジェクトの規約によりますが、URL`クラスの方がより直感的で強力な機能を持っています。

POSTリクエストの処理

さて、いよいよ本題の後半、POSTリクエストの処理です。GETリクエストが主にデータの「取得」を目的としていたのに対し、POSTリクエストはデータの「送信」に使われます。ユーザー登録フォームの送信、APIへのデータ登録などが典型的な例です。

POSTリクエストで送られるデータは、GETリクエストのクエリ文字列とは異なり、リクエストのボディ部分に含まれてサーバーに送られます。このボディデータの受信方法が、Node.jsのノンブロッキングな特性を理解する上で非常に重要なポイントとなります。

なぜPOSTデータはストリームで受信されるのか?

reqオブジェクトに req.body のようなプロパティがあって、そこにPOSTデータが全部入っていれば簡単なのに、と思ったかもしれません。しかし、httpモジュールはそうはなっていません。その理由は、Node.jsが巨大なデータを効率的に扱うための設計にあります。

もし1GBの動画ファイルがPOSTリクエストでアップロードされたと想像してください。サーバーがリクエストボディ全体を一度にメモリに読み込もうとしたらどうなるでしょうか?メモリを大量に消費し、その間サーバーは他のリクエストに応答できなくなり、最悪の場合はメモリ不足でクラッシュしてしまいます。

これを避けるため、Node.jsではリクエストボディをストリーム(Stream)として扱います。ストリームとは、大きなデータを小さな塊(チャンク(Chunk))に分割し、水の流れのように少しずつ連続的に処理するための仕組みです。

reqオブジェクトは ReadableStream(読み取り可能なストリーム)の一種です。私たちはこのストリームにイベントリスナーを登録することで、データが到着するたびに少しずつ受信し、処理することができます。

dataイベントとendイベント

リクエストボディのストリームを処理するために、主に2つのイベントを利用します。

  • dataイベント: 新しいデータチャンクがサーバーに到着するたびに発生します。このイベントのコールバック関数は、引数としてデータチャンク(通常はBufferオブジェクト)を受け取ります。
  • endイベント: リクエストボディの全てのデータチャンクを読み取り終えたときに一度だけ発生します。このイベントのコールバック関数には引数はありません。ここで、受信した全てのチャンクを結合して最終的なデータとして処理します。

この流れをコードで見てみましょう。

“`javascript
// POSTデータを受信する基本的なパターン
let body = []; // データチャンクを格納するための配列

req.on(‘error’, (err) => {
// エラー処理
console.error(err);
}).on(‘data’, (chunk) => {
// dataイベント:チャンクが届くたびに配列に追加
body.push(chunk);
}).on(‘end’, () => {
// endイベント:全てのデータ受信完了後
// 配列に格納されたBufferチャンクを一つのBufferに結合し、文字列に変換
const fullBody = Buffer.concat(body).toString();

// ここで fullBody を使って処理を行う
// (例: JSONとしてパースする、など)
});
“`
このパターンはPOSTリクエスト処理の基本形であり、非常に重要なのでしっかり理解しておきましょう。

application/x-www-form-urlencoded 形式の処理

Webフォームからmethod="POST"で送信されるデータの最も一般的な形式が application/x-www-form-urlencoded です。データは name=Taro&[email protected] のように、クエリ文字列と同じ形式でエンコードされてリクエストボディに格納されます。

この形式のデータをパースするには、Node.jsのコアモジュールである querystring を使います。

それでは、簡単なフォームを表示するページと、そのフォームから送信されたデータを処理するロジックを実装してみましょう。

server.jsを以下のように更新します。

“`javascript
const http = require(‘http’);
const querystring = require(‘querystring’); // querystringモジュールをインポート

const server = http.createServer((req, res) => {
if (req.url === ‘/’ && req.method === ‘GET’) {
// GETリクエストでトップページにアクセスされたらフォームを表示
res.writeHead(200, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(<h1>フォーム</h1>
<form action="/submit" method="POST">
<label for="name">名前:</label>
<input type="text" id="name" name="name"><br><br>
<label for="email">メールアドレス:</label>
<input type="email" id="email" name="email"><br><br>
<button type="submit">送信</button>
</form>
);
} else if (req.url === ‘/submit’ && req.method === ‘POST’) {
// POSTリクエストで /submit にアクセスされたときの処理
let body = [];
req.on(‘data’, (chunk) => {
body.push(chunk);
});

req.on('end', () => {
  const fullBody = Buffer.concat(body).toString();
  // querystring.parse()で 'name=Taro&email=...' 形式の文字列をオブジェクトに変換
  const postData = querystring.parse(fullBody);

  const name = postData.name || '名無し';
  const email = postData.email || 'メールアドレス未入力';

  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.end(`
    <h1>送信ありがとうございました!</h1>
    <p>名前: ${name}</p>
    <p>メールアドレス: ${email}</p>
    <a href="/">フォームに戻る</a>
  `);
});

} else {
res.writeHead(404, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
res.end(‘

404 Not Found

‘);
}
});

const port = 3000;
server.listen(port, () => {
console.log(サーバーが http://localhost:${port}/ で起動しました。);
});
“`

このコードの動作を確認してみましょう。
1. サーバーを起動し、http://localhost:3000/にアクセスします。名前とメールアドレスを入力するフォームが表示されます。
2. フォームに情報を入力して「送信」ボタンを押します。
3. ブラウザは入力されたデータをname=...&email=...という形式にエンコードし、/submitというURLに対してPOSTリクエストを送信します。
4. サーバー側では、req.methodPOSTreq.url/submitの条件に一致するブロックが実行されます。
5. dataendイベントを使ってリクエストボディを受信し、Buffer.concat(body).toString()で文字列に変換します。
6. querystring.parse(fullBody)が、'name=Taro&email=taro%40example.com'のような文字列を { name: 'Taro', email: '[email protected]' } というJavaScriptオブジェクトにパースします。
7. パースしたデータを使って、お礼のメッセージを生成し、クライアントに返します。

application/json 形式の処理

現代のWeb APIでは、クライアントとサーバー間のデータ交換にJSON(JavaScript Object Notation)形式が広く使われています。JSON形式のデータがPOSTリクエストのボディで送られてきた場合、サーバー側ではそれを受信し、JSON文字列をJavaScriptオブジェクトにパースする必要があります。

JSON文字列のパースには、JavaScriptの標準組込みオブジェクトであるJSONparse()メソッドを使います。

今度は、JSONデータを受け取って処理するAPIエンドポイントを実装してみましょう。

“`javascript
const http = require(‘http’);

const server = http.createServer((req, res) => {
// APIエンドポイント: /api/users
if (req.url === ‘/api/users’ && req.method === ‘POST’) {
// リクエストヘッダーでContent-Typeがapplication/jsonか確認(推奨)
if (req.headers[‘content-type’] !== ‘application/json’) {
res.writeHead(400, { ‘Content-Type’: ‘application/json; charset=utf-8’ });
res.end(JSON.stringify({ error: ‘Content-Type must be application/json’ }));
return;
}

let body = [];
req.on('data', (chunk) => {
  body.push(chunk);
});

req.on('end', () => {
  const fullBody = Buffer.concat(body).toString();
  let jsonData;

  try {
    // JSON文字列をJavaScriptオブジェクトにパース
    jsonData = JSON.parse(fullBody);
  } catch (error) {
    // JSONのパースに失敗した場合(不正なJSON)
    res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({ error: 'Invalid JSON' }));
    return;
  }

  // パース成功
  console.log('受信したデータ:', jsonData);
  const name = jsonData.name;
  const job = jsonData.job;

  // 成功レスポンスを返す
  res.writeHead(201, { 'Content-Type': 'application/json; charset=utf-8' });
  const responseData = {
    message: 'User created successfully',
    user: { name, job }
  };
  // JavaScriptオブジェクトをJSON文字列に変換してレスポンスボディに設定
  res.end(JSON.stringify(responseData));
});

} else {
res.writeHead(404, { ‘Content-Type’: ‘text/plain; charset=utf-8’ });
res.end(‘Not Found’);
}
});

const port = 3000;
server.listen(port, () => {
console.log(サーバーが http://localhost:${port}/ で起動しました。);
});
“`
このコードの重要なポイントは以下の通りです。

  1. エラーハンドリング:
    • JSON.parse()は、不正な形式のJSON文字列をパースしようとするとエラーをスローします。そのため、try...catchブロックで囲み、エラーが発生した場合にはクライアントに400 Bad Requestステータスとエラーメッセージを返すのが定石です。
    • また、Content-Typeヘッダーをチェックし、application/jsonでないリクエストを早期に弾くことも堅牢なAPIを設計する上で重要です。
  2. レスポンスの作成:
    • レスポンスとして返すJavaScriptオブジェクトは、JSON.stringify()を使ってJSON形式の文字列に変換してからres.end()に渡します。
    • リソースの作成が成功したことを示すため、ステータスコードには201 Createdを使用するのが一般的です。

このAPIエンドポイントをテストするには、curlコマンドが便利です。

bash
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name": "Hanako", "job": "Developer"}' \
-i

  • -X POST: HTTPメソッドをPOSTに指定します。
  • -H "Content-Type: application/json": Content-Typeヘッダーを設定します。
  • -d '{"name": ...}': リクエストボディに含めるデータを指定します。
  • -i: レスポンスヘッダーも表示します。

実行すると、以下のようなレスポンスが返ってくるはずです。

“`
HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8
Date: [現在の日時]
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 68

{“message”:”User created successfully”,”user”:{“name”:”Hanako”,”job”:”Developer”}}
“`

サーバー側のコンソールには「受信したデータ: { name: ‘Hanako’, job: ‘Developer’ }」と表示されます。

実践的なアプリケーションの構築:TodoリストAPI

これまでに学んだGETとPOSTの処理を組み合わせて、簡単なTodoリストを管理するWebアプリケーションを構築してみましょう。

仕様:

  1. GET /: Todoリストを表示する簡単なHTMLページを返す。
  2. GET /api/todos: 現在のTodoリストをJSON形式で返す。
  3. POST /api/todos: 新しいTodoをJSON形式で受け取り、リストに追加する。
  4. 上記以外のURLは 404 Not Found を返す。

サーバーのメモリ上にTodoリストを保持するシンプルな実装にします。

server.js

“`javascript
const http = require(‘http’);
const url = require(‘url’);

// サーバーのメモリ上にTodoリストを保持する(簡易的なデータベース)
let todos = [
{ id: 1, title: ‘Node.jsの勉強’, completed: false },
{ id: 2, title: ‘牛乳を買う’, completed: true },
];
let nextId = 3;

const server = http.createServer((req, res) => {
const parsedUrl = url.parse(req.url, true);
const pathname = parsedUrl.pathname;
const method = req.method;

// ルーティング
if (pathname === ‘/’ && method === ‘GET’) {
// 1. トップページのHTMLを返す
serveHtml(res);
} else if (pathname === ‘/api/todos’ && method === ‘GET’) {
// 2. TodoリストをJSONで返す
getTodos(res);
} else if (pathname === ‘/api/todos’ && method === ‘POST’) {
// 3. 新しいTodoを追加する
addTodo(req, res);
} else {
// 4. 404 Not Found
notFound(res);
}
});

function serveHtml(res) {
res.writeHead(200, { ‘Content-Type’: ‘text/html; charset=utf-8’ });
const html = `
<!DOCTYPE html>


Todo List

Todo List



上部へスクロール