Wednesday, 13 October, 2021 UTC


Summary

この記事はSuper SIMチームのソフトウェアエンジニアインターンのNikolay Nikolovがこちらで公開した記事(英語)を日本語化したものです。
皆さんは「プロキシ」という言葉を一度は聞いたことがあるのではないでしょうか。実はプロキシは非常に身近で便利な技術です。
プロキシとは、複数のサービスの間に位置し、リクエストとレスポンスを双方向に処理・修正する中間的なアプリケーションのことです。
現実世界に例えると、スペインから来た人を目の前にして、あなたがスペイン語を話せないとします。困っていたところ、あなたは友人の田中さんがスペイン語と日本語を話せ、通訳ができることを思い出します。

通訳が以下のとおり行われます。
  1. あなたが田中さんに日本語で話しかけます。
  2. 田中さんは頭の中でそれをスペイン語に翻訳して、新しい友人にスペイン語で話します。
  3. 新しい友人は、スペイン語で田中さんに返事をします。
  4. 田中さんは頭の中でそれを日本語に翻訳して、あなたに日本語で返事をします。
このシナリオでは、田中さんはあなたと新しい友人との間の代理人(プロキシ)としての役割を持っています。お互いに直接話すことはできませんが、田中さんのおかげで会話のキャッチボール(リクエストとレスポンスの送受信)ができます。
プロキシとは何かを理解できたので、次にプロキシの使用例についてご紹介します。おすすめの使用例を列挙します。
  • 認可: サービスへのアクセスを認可されたリクエストのみを転送する。
  • ロードバランシング: 複数のインスタンスにリクエストを均等に分配する。
  • ロギング: バックエンドAPIサービスへのリクエストをログ出力する。
プロキシとは何か、なぜプロキシが有用なのかを理解できたところで、Node.jsを使って簡単なプロキシを作ってみましょう。
必要なもの
このチュートリアルを進めるには、以下の項目が必要です。
  • Node.js
  • Yarn
シンプルなNode.jsプロキシを構築する
簡単な手順で、Node.jsで複数の異なるサーバーおよびエンドポイントにリクエストを転送できるシンプルなプロキシを作成しましょう。

プログラムの初期化

まず、新しいNode.jsプログラムを作成します。ターミナルを開き、以下のコマンドを実行してください。
yarn init 
これにより、プログラムの基本設定を含むpackage.jsonファイルが生成されます。コマンドを実行すると設定に関する質問(名前、バージョンなど)が表示されます。質問に対してエンターキーをクリックすると、デフォルトの値が設定されます。たとえば、アプリケーションのデフォルトエントリーポイントはindex.jsです。

依存パッケージをインストールする

プロキシを動作させるには以下のパッケージが必要です。
  • express: Node.jsで使うウェブサーバーフレームワーク。
  • http-proxy-middleware: シンプルなプロキシフレームワーク。
  • (任意) morgan: HTTPリクエストログ出力用のミドルウェア。
これらのパッケージをインストールします。以下のコマンドを実行してください。
yarn add express http-proxy-middleware morgan 

スタートコマンドを追加する

生成されたpackage.jsonにスタートコマンドを追加します。scriptsパラメータを追加し、その値に、{"start": "node index.js"}を指定してください。
スタートコマンドを追加した後のpackage.jsonは以下のとおりです。
依存パッケージをインストールした時期によって、バージョン番号が異なる場合があります。パッケージの基本的な機能は同じです。
{ "name": "simple-nodejs-proxy", "version": "1.0.0", "main": "index.js", "license": "MIT", "dependencies": { "express": "^4.17.1", "http-proxy-middleware": "^1.0.5", "morgan": "^1.10.0" }, "scripts": { "start": "node index.js" } } 
プログラムのルートディレクトリに、空のindex.jsファイルを作成してください。
これで、プログラムを実行する準備が整いました。以下のコマンドを実行してください。
yarn start 
プログラムが実行されます。現時点ではindex.jsは空なので、ターミナルには何も出力されません。
設定が完了したので、次にプロキシを作成しましょう。

プロキシを作成する

まず、依存パッケージをインポートします。index.jsファイルをテキストエディタで開き、以下のコードをペーストしてください。
const express = require('express'); const morgan = require("morgan"); const { createProxyMiddleware } = require('http-proxy-middleware'); 
次に、Expressサーバーを作成し、後ほど使用する変数を定義します。依存パッケージのブロックの下に、以下のコードをペーストしてください。
// Create Express Server const app = express(); // Configuration const PORT = 3000; const HOST = "localhost"; const API_SERVICE_URL = "https://jsonplaceholder.typicode.com"; 
受信したリクエストをログに記録するmorganミドルウェアを追加します。変数のブロックの下に、以下のコードをペーストしてください。
// Logging app.use(morgan('dev')); 
テスト用の/infoエンドポイントを追加します。このエンドポイントはリクエストを転送せず、テキストレスポンスのみを返します。
morganミドルウェアのブロックの下に、以下のコードをペーストしてください。
// Info GET endpoint app.get('/info', (req, res, next) => { res.send('Billing APIとAccount APIへのプロキシサービスです。'); }); 
Authorization Headerがない場合に、403(Forbidden)エラーを送信する認可用のミドルウェアも追加します。
/infoエンドポイントのブロックの下に、以下のコードをペーストしてください。
// Authorization app.use('', (req, res, next) => { if (req.headers.authorization) { next(); } else { res.sendStatus(403); } }); 
プロキシのエンドポイントを定義します。/json_placeholderで始まるリクエストを、ダミーデータを提供するAPI、JSON Placeholder APIにプロキシします。また、APIに転送される際に/json_placeholderが省略されるように、pathRewriteを定義します。
app.useのブロックの下に、以下のコードをペーストしてください。
// Proxy endpoints app.use('/json_placeholder', createProxyMiddleware({ target: API_SERVICE_URL, changeOrigin: true, pathRewrite: { [`^/json_placeholder`]: '', }, })); 
これにより、/json_placeholderを含むパスにリクエストが送られると、リクエスト先のURLから/json_placeholderが省略されます。例えば、localhost:3000/json_placeholder/posts/1にリクエストを送ると、リクエスト先のURLはhttps://jsonplaceholder.typicode.com/posts/1に上書きされます。
最後に、設定したサーバーを呼び出します。index.jsファイルの最終行に、以下のコードをペーストしてください。
// Start the Proxy app.listen(PORT, HOST, () => { console.log(`Starting Proxy at ${HOST}:${PORT}`); }); 

プロキシを実行する

プロキシを実行します。ターミナルでプログラムのルートディレクトリから以下のコマンドを実行してください。
yarn start 
以下のような内容がターミナルで出力されます。
yarn run v1.22.4 $ node index.js [HPM] Proxy created: / -> https://jsonplaceholder.typicode.com [HPM] Proxy rewrite rule created: "^/json_placeholder" ~> "" Starting Proxy at localhost:3000 
これでプロキシが実行されました。
/infoにGETリクエストを送信します。
2つ目のターミナルを開いて、以下のコマンドを実行してください。
curl localhost:3000/info 
問題なくサーバーが動作すると、プロキシからのレスポンスが出力されます。
Billing APIとAccount APIへのプロキシサービスです。 
これまでの手順で、エンドポイントに対するGETリクエストからのレスポンスを検証しました。次に、実際にプロキシの動作検証を行います。
2つ目のターミナルで、以下のコマンドを実行してください。
curl localhost:3000/json_placeholder/posts/1 
すると、以下のForbiddenエラーが出力されます。
Forbidden 
これは、認可ミドルウェアがすべてのリクエストにAuthorization Headerが含まれていることを必要とするためです。
そこで、リクエストに変更を加えます。
2つ目のターミナルで、以下のコマンドを実行してください。
curl -H "Authorization: nikolay" localhost:3000/json_placeholder/posts/1 
すると、以下のオブジェクトが出力されます。
{ "userId": 1, "id": 1, "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" } 
プロキシを通したリクエストが成功しました。
さらに、POSTリクエストでも試してみましょう。
2つ目のターミナルで、以下のコマンドを実行してください。
curl -X POST -H "Authorization: real_user" --data '{"title": "Build a Node.js Proxy Server in Under 10 minutes!","body": "We have all heard the term "proxy"...",userId="1"}' localhost:3000/json_placeholder/posts 
すると、以下のオブジェクトが出力されます。
{ "{\"title\": \"Build a Node.js Proxy Server in Under 10 minutes!\",\"body\": \"We have all heard the term "proxy"...\",userId": "\"1\"}", "id": 101 } 
これでプロキシサーバーの開発、動作検証ができました!
このプログラムの全コードは、Githubリポジトリを参照してください。
次のステップ
本稿でご紹介したプロキシは、非常にシンプルです。ユースケースに応じて、以下のように拡張できます。
  • より洗練された認可ミドルウェアを使用して、サービスへのアクセスを承認されたリクエストのみを転送する。
  • APIサービスの多数のデプロイメントにリクエストを均等に分散させる。
  • バックエンドAPIサービスへのすべてのリクエストを記録する。
  • プロキシサーバー、Twilio、C#でアイデンティティを保護する(英語記事)。
  • ReactアプリにNode.jsサーバープロキシを設定する。
まとめ
プロキシサーバーは複雑なイメージを持たれがちですが、実は非常に簡単に構築できます。このチュートリアルでプロキシの基礎が学べたので、次はさらに複雑なプロキシサーバーを作ってみてはいかがでしょうか。ぜひプロジェクトをシェアしてください!
Nikolay NikolovはTwilioのSuper SIMチームソフトウェアエンジニアインターンをしています。ウェブや機械学習の概念を探求し、技術愛好家と共有することが好きです。Nikolayへのご連絡は、LinkedInまたはウェブサイトからお願いします。