【Node.js】Expressで最小のAPIサーバーを作る|ルーティング・JSON・GET/POST

【Node.js】Expressで最小のAPIサーバーを作る|ルーティング・JSON・GET/POST Node.js

Node.jsでWeb APIサーバーを作るとき、もっとも広く使われているのがExpressです。標準のhttpモジュールでもサーバーは作れますが、ルーティングやJSONの処理を自分で書く必要があり、コードが煩雑になります。Expressを使えば、数行でルーティング付きのAPIサーバーが作れ、JSONの返却やリクエストボディの受け取りも簡単です。

この記事では、expressのインストールから、GETPOSTのルーティング、URLパラメータやクエリの取得、JSONボディの受け取り、ステータスコードの設定まで、最小限のAPIサーバーを実機で動かしながら作っていきます。実際にサーバーを起動し、各エンドポイントにリクエストを送って動作を確認しています。

先に結論

  • npm install expressでインストールし、const app = express()で始めます。
  • ルーティングはapp.get("/path", (req, res) => {...})のように書きます。
  • JSONを返すのはres.json(データ)。配列もオブジェクトもそのまま渡せます。
  • URLの:idreq.params.id?sort=...req.query.sortで取れます。
  • POSTのJSONボディを受け取るにはapp.use(express.json())が必須です。
  • ステータスコードはres.status(201).json(...)のように指定します。

素のサーバーとの違いはhttpモジュールでWebサーバーを作る、インストールまわりはnpmとpackage.jsonの基礎、ポート番号などの環境変数はprocess完全ガイドもあわせて参考になります。

スポンサーリンク

Expressとは(httpモジュールとの違い)

Expressは、Node.jsのWebアプリケーション用フレームワークです。標準のhttpモジュールだけでルーティングを実装すると、if (req.url === "/users" && req.method === "GET")のような分岐を延々と書くことになります。Expressは、このルーティングとリクエスト・レスポンス処理をすっきり書けるようにしてくれます。APIサーバーやWebサービスのほとんどがExpress(や類似のフレームワーク)の上に作られています。

インストールと最小サーバー

まずnpm install expressでExpressをインストールします。そして数行で、リクエストに応答する最小のサーバーが作れます。

express をインストール
# プロジェクトフォルダで実行
npm init -y
npm install express
server.js(最小のサーバー)
const express = require("express");
const app = express();

// "/" にアクセスが来たら文字列を返す
app.get("/", (req, res) => {
  res.send("Hello Express");
});

// 3000番ポートで待ち受ける
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`listening on ${PORT}`);
});

node server.jsで起動し、ブラウザやcurlhttp://localhost:3000/にアクセスするとHello Expressが返ります。実機でも、この最小サーバーが正しく起動し、応答することを確認しました。express()でアプリを作り、app.getでルートを定義し、app.listenでポートを指定して待ち受ける——この3ステップが基本です。process.env.PORTを使うと、環境変数でポートを変えられます。

ルーティング(GET)

URLごとに異なる処理をするのがルーティングです。app.get("/パス", ハンドラ)で、そのパスへのGETリクエストに応答します。JSONを返すにはres.json()を使います。

ルーティングとJSON返却
// 文字列を返す
app.get("/hello", (req, res) => {
  res.send("こんにちは");
});

// JSON(配列)を返す
app.get("/api/users", (req, res) => {
  res.json([
    { id: 1, name: "田中" },
    { id: 2, name: "鈴木" },
  ]);
});

実機でも、/api/usersにアクセスすると[{"id":1,"name":"田中"},{"id":2,"name":"鈴木"}]というJSONが返りました。res.json()に配列やオブジェクトを渡すだけで、自動的にJSON文字列に変換され、Content-Type: application/jsonも付きます。素のhttpモジュールではJSON.stringifyやヘッダー設定を自分で書く必要がありますが、Expressでは不要です。

URLパラメータとクエリ

URLの一部を変数として受け取るのがURLパラメータ:id)、?key=valueの部分がクエリです。それぞれreq.paramsreq.queryで取得します。

パラメータとクエリの取得
// :id の部分を req.params.id で受け取る
app.get("/api/users/:id", (req, res) => {
  const id = Number(req.params.id);
  const sort = req.query.sort || "none";   // ?sort=name の部分
  res.json({ id: id, sort: sort });
});

// 例: GET /api/users/5?sort=name
//  → { "id": 5, "sort": "name" }

実機でも、/api/users/5?sort=nameにアクセスすると{"id":5,"sort":"name"}が返りました。:idのようにコロンを付けた部分がreq.paramsに入り?sort=nameのようなクエリはreq.queryに入ります。注意点として、req.paramsreq.queryの値はすべて文字列です。数値として使いたいときはNumber()で変換します。

JSONを受け取る(POST + express.json)

クライアントから送られたJSONデータを受け取るには、POSTのルートを作り、app.use(express.json())を必ず設定します。これが無いとreq.bodyが空になります。

POSTでJSONボディを受け取る
const express = require("express");
const app = express();

// 【必須】これが無いと req.body が undefined になる
app.use(express.json());

app.post("/api/users", (req, res) => {
  const newUser = req.body;        // 送られたJSONがオブジェクトで入る
  console.log(newUser);            // { name: "佐藤" }
  res.status(201).json({ created: newUser });
});
express.json()を忘れるとreq.bodyが受け取れない

実機で確認したところ、app.use(express.json())を設定したうえで{"name":"佐藤"}をPOSTすると、req.body{ name: "佐藤" }が正しく入り、{"created":{"name":"佐藤"}}がステータス201で返りました。この一行を忘れると、req.bodyundefinedになり、送ったデータを受け取れません。「POSTしたのにデータが取れない」というトラブルの定番原因です。JSONを受け取るAPIでは、ルート定義より前にapp.use(express.json())を書いておきましょう。フォーム送信(application/x-www-form-urlencoded)を受け取る場合はexpress.urlencoded()を使います。

ステータスコードと404

レスポンスのHTTPステータスコードはres.status()で指定します。作成成功なら201、未検出なら404、というように使い分けます。どのルートにも一致しなかったリクエストの処理も書けます。

ステータスコードと404処理
// 見つからなければ 404 を返す
app.get("/api/users/:id", (req, res) => {
  const user = findUser(req.params.id);   // 仮の検索関数
  if (!user) {
    return res.status(404).json({ error: "not found" });
  }
  res.json(user);
});

// どのルートにも一致しなかったとき(最後に置く)
app.use((req, res) => {
  res.status(404).json({ error: "ページが見つかりません" });
});

実機でも、定義していないパス(/nope)にアクセスするとステータス404が返ることを確認しました。res.status(コード)に続けて.json().send()をつなげて書きます。すべてのルート定義の最後にapp.use(...)を置くと、どれにも一致しなかったリクエストをまとめて処理でき、独自の404レスポンスを返せます。なお、ifの中でreturn res.status(...)のようにreturnを付けるのは、その後の処理を実行させないためのよくある書き方です。

ミドルウェアの基礎

Expressの中心的な仕組みがミドルウェアです。すべてのリクエストの前に共通処理(ログ出力・認証チェックなど)を挟めます。app.use()で登録し、next()を呼ぶと次の処理に進みます。

ミドルウェアでログを出す
// すべてのリクエストの前に実行される
app.use((req, res, next) => {
  console.log(`${req.method} ${req.url}`);   // 例: GET /api/users
  next();   // これを呼ばないと先に進まない(リクエストが止まる)
});

// express.json() も実はミドルウェアの一種
next()を呼ばないとリクエストが止まる

ミドルウェアは(req, res, next)の3つの引数を取り、処理が終わったらnext()を呼んで次へ進めますnext()を呼び忘れると、リクエストがそこで止まり、レスポンスが返らずタイムアウトになります。先ほどのexpress.json()も、実は「リクエストボディを読んでJSONに変換し、req.bodyにセットしてnext()する」ミドルウェアです。認証・ログ・CORS対応など、共通処理はミドルウェアとして書くのがExpressの基本スタイルです。登録した順番に実行されるため、express.json()や共通ミドルウェアはルート定義より前に置くことを意識してください。

主なメソッドまとめ

Expressでよく使うメソッドをまとめます。

書き方 働き
app.get(path, fn) GETリクエストのルート
app.post(path, fn) POSTリクエストのルート
res.json(data) JSONを返す
res.status(code) ステータスコードを指定
req.params URLパラメータ(:id
req.query クエリ(?key=value
req.body POSTのボディ(要express.json()
app.use(fn) ミドルウェアの登録

よくある失敗

express.json()を忘れてreq.bodyが空

JSONボディを受け取るにはapp.use(express.json())が必須です。ルートより前に書きます。

req.paramsを数値だと思って使う

パラメータやクエリは文字列です。数値として使うならNumber()で変換します。

ミドルウェアでnext()を呼び忘れる

リクエストが止まってタイムアウトします。処理後にnext()を呼びます。

404処理を先に書いてしまう

app.useの総括的な処理は、すべてのルート定義のに置きます。

res.jsonとres.sendを二重に呼ぶ

レスポンスは1回だけです。2回送ろうとするとエラーになります。

よくある質問

QExpressとhttpモジュールはどちらでAPIを作るべき?
A学習目的で仕組みを理解するなら標準のhttpモジュール、実際にAPIを作るならExpressが向いています。Expressはルーティング・JSON処理・ミドルウェアが簡単に書け、コードが大幅に短くなります。多くの実務プロジェクトでExpress(や類似フレームワーク)が使われています。
QPOSTしたのにreq.bodyが取れません。
Aapp.use(express.json())を設定していない可能性が高いです。JSONボディを受け取るには、ルート定義より前にこの一行が必要です。フォーム送信を受け取る場合はexpress.urlencoded({ extended: true })を使います。
QURLの:idやクエリを取得するには?
A:idのようなURLパラメータはreq.params.id?sort=nameのようなクエリはreq.query.sortで取得します。どちらも値は文字列なので、数値として使うときはNumber()で変換してください。
Qミドルウェアとは何ですか?
Aリクエストとレスポンスの間に挟まる共通処理のことです。(req, res, next)を引数に取り、ログ出力・認証・ボディ解析などを行ってnext()で次へ進めます。app.use()で登録し、登録順に実行されます。express.json()もミドルウェアの一種です。
Qポート番号を変えるには?
Aapp.listen(ポート番号)で指定します。const PORT = process.env.PORT || 3000のように書くと、環境変数PORTがあればそれを、無ければ3000を使う、という柔軟な指定ができます。本番環境ではポートを環境変数で渡すのが一般的です。

まとめ

  • npm install expressでインストールし、express()app.getapp.listenで最小サーバーが作れます。
  • JSONはres.json()で返し、ステータスはres.status()で指定します。
  • :idreq.params、クエリはreq.query(どちらも文字列)。
  • POSTのJSONはapp.use(express.json())が必須です。
  • 共通処理はミドルウェアで書き、next()で次へ進めます。

Expressを使えば、わずか数行でルーティング付きのAPIサーバーが作れます。「res.jsonでJSONを返す」「express.json()でJSONを受け取る」「共通処理はミドルウェア」という基本を押さえれば、本格的なWeb APIへ自然に発展させていけます。まずは最小のサーバーを動かして、ルートを少しずつ増やしてみてください。