Node.jsでAPI連携する方法

Node.jsにおけるAPI連携の例を解説します。

概要

Node.jsは、JavaScriptランタイムで、APIとの連携によく使用されます。APIとの連携を実現するためには、リクエストの送信やレスポンスの処理が必要となります。

ここでは、以下バージョンを使用した、Node.jsでAPIとの連携を行う方法を説明します。

nodejs v19.7.0

また、今回作成するコードは全て、GitHubに掲載しています。


フロントエンドからAPIを実行する方法

フロントエンドでAPIを使用することは一般的なシナリオで、JavaScriptのFetch APIを使うと簡単に実装できます。Fetch APIはブラウザがネイティブで提供するAPIで、非同期HTTP(Ajax)リクエストを行うことができます。

以下の例では、Node.jsで作成した/usersエンドポイントにPOSTリクエストを送信し、新しいユーザーを作成します。

index.html

<!DOCTYPE html>
<html>
<body>
  <!-- ボタンを追加 -->
  <button onclick="createUser()">Create User</button>

  <script>
    async function createUser() {
      const response = await fetch('http://localhost:3000/users', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ name: 'John', age: 25 })
      });

      if (!response.ok) {
        const message = await response.text();
        throw new Error(`An error has occurred: ${message}`);
      }

      const user = await response.json();

      console.log(user);
    }
  </script>
</body>
</html>

解説

このソースコードは、ユーザーを作成するためのHTMLページを生成します。

  • HTMLドキュメントの定義とボタンの追加:
<!DOCTYPE html>
<html>
<body>
    <!-- ボタンを追加 -->
    <button onclick="createUser()">Create User</button>

この部分では、HTML文書の基本的な構造を定義し、ユーザーを作成するためのボタンを追加しています。ボタンがクリックされるとcreateUser関数が実行されます。

  • createUser関数の定義:
<script>
async function createUser() {
    const response = await fetch('http://localhost:3000/users', {
        method: 'POST',
        headers: {
        'Content-Type': 'application/json'
        },
        body: JSON.stringify({ name: 'John', age: 25 })
    });

この部分で非同期関数createUserを定義しています。この関数は、fetchAPIを使用して、指定したURL(この場合はhttp://localhost:3000/users)に対してPOSTリクエストを送信します。リクエストボディには、作成するユーザーの情報をJSON形式で指定します。

  • エラーハンドリング:
    if (!response.ok) {
        const message = await response.text();
        throw new Error(`An error has occurred: ${message}`);
    }

response.okプロパティは、レスポンスが成功したかどうか(HTTPステータスコードが200~299の範囲かどうか)を判断します。成功していない場合、エラーメッセージを生成してエラーをスローします。

  • レスポンスの処理とログ出力:
const user = await response.json();

response.json()メソッドを使用して、レスポンスボディをJSONとして解析します。

server.js

// モジュールをインポートします
const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");

// ユーザーを保存するための仮のデータストア
let users = [];

// HTMLページのパスを設定します
const indexFilePath = path.join(__dirname, "index.html");

// リクエストハンドラを作成します
const requestHandler = (req, res) => {
  const { pathname } = url.parse(req.url);

  switch (pathname) {
    case "/":
      fs.readFile(indexFilePath, (err, data) => {
        if (err) {
          res.writeHead(500);
          res.end(`Error loading ${indexFilePath}`);
        } else {
          res.setHeader("Content-Type", "text/html");
          res.end(data);
        }
      });
      break;

    case "/users":
      if (req.method === "GET") {
        res.setHeader("Content-Type", "application/json");
        res.end(JSON.stringify(users));
      } else if (req.method === "POST") {
        let body = "";

        req.on("data", (chunk) => {
          body += chunk.toString();
        });

        req.on("end", () => {
          const user = JSON.parse(body);
          users.push(user);
          res.setHeader("Content-Type", "application/json");
          res.end(JSON.stringify(user));
        });
      }
      break;

    default:
      res.writeHead(404);
      res.end(`404 - Page not found!`);
  }
};

// サーバーを作成して起動します
const server = http.createServer(requestHandler);

server.listen(8000, () => {
  console.log("Server is running on port 8000");
});


解説

  • モジュールをインポートする
const http = require("http");
const url = require("url");
const fs = require("fs");
const path = require("path");

これらの行は、HTTPサーバーを作成するための http モジュール、URLを解析するための url モジュール、ファイルシステム操作を行うための fs モジュール、パス操作を行うための path モジュールをインポートしています。

  • ユーザーを保存するための仮のデータストアを作成する
let users = [];

この行は、作成されたユーザーを保存するための配列を作成しています。

  • HTMLページのパスを設定する
const indexFilePath = path.join(__dirname, "index.html");

この行は、HTMLファイルのパスを設定しています。__dirname は現在のモジュールファイルのディレクトリへのパスを指します。

  • リクエストハンドラを作成する
const requestHandler = (req, res) => {
  // ...
};

この関数は、サーバーに送信された全てのHTTPリクエストを処理します。リクエストオブジェクト (req) とレスポンスオブジェクト (res) の二つの引数を取ります。

  • ルートURL ("/") へのリクエストを処理する
case "/":
  fs.readFile(indexFilePath, (err, data) => {
    // ...
  });
  break;

ルートURLへのリクエストがあった場合、HTMLファイルを読み込み、その内容をレスポンスとして返します。

  • “/users” へのGETおよびPOSTリクエストを処理する
case "/users":
  if (req.method === "GET") {
    // ...
  } else if (req.method === "POST") {
    // ...
  }
  break;

“/users” へのGETリクエストがあった場合、保存されている全てのユーザーを返します。POSTリクエストがあった場合、新たにユーザーを作成します。

  • 他のURLへのリクエストを処理する
default:
  res.writeHead(404);
  res.end(`404 - Page not found!`);

ルートURLや “/users” 以外のURLへのリクエストがあった場合、HTTPステータスコード404とエラーメッセージを返します。

  • HTTPサーバーを作成して起動する
const server = http.createServer(requestHandler);
server.listen(8000, () => {
  console.log("Server is running on port 8000");
});

最後に、作成したリクエストハンドラを引数にして http.createServer() メソッドを呼び出し、サーバーを作成します。次に listen() メソッドを呼び出し、サーバーがポート8000でリッスンを開始するようにします。サーバーが起動したら、コンソールにメッセージを出力します。

最終的に以下のようなディレクトリ構成になります。

integration
├── index.html
├── server.js

ここまで完成したら、以下のコマンドを実行してサーバーを起動します。

node server.js

実行が完了したら、ブラウザでhttp://localhost:3000にアクセスしてみましょう。 ボタンをクリックすると、ユーザーが作成されます。 ユーザーが作成された、http://localhost:3000/usersにアクセスすると、作成されたユーザーの一覧が表示されます。

テスト

## ユーザーの取得
curl -X GET http://localhost:3000/users
# ユーザーの作成
curl -X POST -H "Content-Type: application/json" -d '{"id":"1","name":"John Doe"}' http://localhost:3000/users

外部APIへのリクエストを送信する

外部APIへのリクエストを送信するためには、通常、HTTPクライアントライブラリが使用されます。ここでは、その一つであるaxiosを使用します。まず、npmを使ってaxiosをインストールします。

npm install axios

external.js

const http = require('http');
const axios = require('axios');

const requestHandler = async (req, res) => {
  if (req.url === '/data' && req.method === 'GET') {
    try {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      res.setHeader('Content-Type', 'application/json');
      res.end(JSON.stringify(response.data));
    } catch (error) {
      res.writeHead(500);
      res.end(JSON.stringify({ message: 'Error occurred' }));
    }
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
};

const server = http.createServer(requestHandler);

server.listen(3000, () => {
  console.log('Server is running on port 3000');
});

解説

– モジュールをインポートします:

const http = require('http');
const axios = require('axios');

httpはNode.js内蔵のHTTPサーバーモジュールで、axiosはPromiseベースのHTTPクライアントライブラリです。

  • リクエストハンドラー関数を定義します:
const requestHandler = async (req, res) => {
  if (req.url === '/data' && req.method === 'GET') {
    try {
      const response = await axios.get('https://jsonplaceholder.typicode.com/posts');
      res.setHeader('Content-Type', 'application/json');
      res.end(JSON.stringify(response.data));
    } catch (error) {
      res.writeHead(500);
      res.end(JSON.stringify({ message: 'Error occurred' }));
    }
  } else {
    res.writeHead(404);
    res.end('Not found');
  }
};

この関数は、リクエストURLとメソッドをチェックし、それに応じたレスポンスを返します。具体的には、’/data’へのGETリクエストが来た場合に、axiosを使って外部APIからデータを取得し、そのデータをJSON形式でレスポンスとして返します。

  • HTTPサーバーを作成します:
const server = http.createServer(requestHandler);

http.createServerメソッドを使用して、上で定義したリクエストハンドラー関数requestHandlerを使用するHTTPサーバーを作成します。

  • サーバーを起動します:
server.listen(3000, () => {
  console.log('Server is running on port 3000');
});

server.listenメソッドを使用して、サーバーを起動し、ポート3000でリクエストを待ち受けます。サーバーが起動したら、“Server is running on port 3000"というメッセージがコンソールに表示されます。

テスト

curl http://localhost:3000/data

まとめ

Expressフレームワークを使用せず、プレーンなNode.jsのみでHTTPサーバーを作成しました。リクエストをハンドリングするためのロジックはrequestHandler関数内に定義されています。

  • httpaxiosモジュールをインポートします。httpはNode.js内蔵のHTTPサーバーモジュール、axiosはPromiseベースのHTTPクライアントライブラリです。

  • リクエストハンドラー関数requestHandlerを定義します。この関数では、リクエストURLとメソッドをチェックし、それに応じたレスポンスを返します。具体的には、’/data’へのGETリクエストが来た場合に、axiosを使って外部APIからデータを取得し、そのデータをJSON形式でレスポンスとして返します。

  • http.createServerメソッドを使用して、このリクエストハンドラーを用いるHTTPサーバーを作成します。

  • server.listenメソッドを使用して、サーバーを起動し、ポート3000でリクエストを待ち受けます。サーバーが起動したら、“Server is running on port 3000"というメッセージがコンソールに表示されます。

このように、プレーンなNode.jsでもHTTPサーバーの基本的な動作を実装することができます。ただし、ルーティングやエラーハンドリングなどの複雑な動作を実装する場合は、Expressのようなフレームワークの利用を検討するとよいでしょう。