JavaScriptで作るプロキシサーバー


## はじめに

:::note info
この記事は、[リンクアンドモチベーションアドベントカレンダー2024](https://qiita.com/advent-calendar/2024/lmi)の記事です。
:::

Web開発をしていると、サードパーティAPIにアクセスする際、CORSエラーに悩まされることがありませんか？この記事では、Node.jsを使って簡単なプロキシサーバーを構築し、この問題を解決する方法を解説します。

## プロキシサーバーとは？

プロキシサーバーは、クライアント（ブラウザなど）とリソース（APIサーバーなど）の間に立ち、リクエストやレスポンスを仲介するサーバーです。

### 利用例

- CORSの回避: 異なるドメイン間でのリクエスト制限を解消
- リクエストカスタマイズ: 認証ヘッダーの追加やURLの変更
- セキュリティの向上: APIキーやトークンをクライアントから隠す

## プロジェクトの準備

まず、プロジェクトのフォルダを作成してNode.jsの環境を整えます。

```bash
mkdir instant-proxy && cd instant-proxy
npm init -y
npm install express http-proxy-middleware dotenv
```

## プロキシサーバーの実装

以下のコードを index.js に記述してください。

```javascript
const express = require('express');
const app = express();
const { createProxyMiddleware } = require('http-proxy-middleware');

app.get('/', (req, res) => {
  res.send("This is a test");
});

// プロキシ設定
app.use('/proxy', (req, res, next) => {
  createProxyMiddleware({
    target: 'https://official-joke-api.appspot.com/random_ten', // 転送先のURL
    changeOrigin: true, // リクエストのオリジンをターゲットに変更
    pathRewrite: {
        [`^/proxy`]: '', // URLの書き換え
    }
  })(req, res, next);
});

// 3000の部分は任意のポートに書き換えてください
const port = process.env.PORT || 3000;

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});

module.exports = app;
```

今回はプロキシ先として[official-joke-api](https://official-joke-api.appspot.com/random_ten)というでランダムにジョークを返すAPIを利用します。
試しにpostmanなどでリクエストすると以下のようなレスポンスが得られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/5bb0a240-f4fc-eabc-43f5-680b42a4953b.png)


プロキシを利用することで以下のようなイメージでリクエストされます。

```mermaid
sequenceDiagram
    participant Client as クライアント
    participant ProxyServer as プロキシサーバー(node)
    participant API as サードパーティAPI <br/>（今回はofficial-joke-api.appspot.com）

    Client->>ProxyServer: /proxy リクエスト送信
    ProxyServer->>API: リクエストを転送
    API-->>ProxyServer: レスポンス（JSONデータ）
    ProxyServer-->>Client: レスポンスを返す
```

ローカルのサーバーを起動し、`http://localhost:3000/proxy`に対してリクエストしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/31410134-86b8-787d-5bf0-35369d234020.png)

無事に転送先のレスポンスが取得できていることがわかります。


## パプリックな環境にデプロイ

今回はvercelにデプロイしてvercel上でプロキシさせてみます。


### 設定ファイルの作成

プロジェクトのルートディレクトリに`vercel.json`を作成します。

```json
{
    "headers": [
        {
            "source": "/(.*)",
            "headers": [
                {
                    "key": "Access-Control-Allow-Origin",
                    "value": "*"
                },
                {
                    "key": "Access-Control-Allow-Credentials",
                    "value": "false"
                },
                {
                    "key": "Access-Control-Allow-Methods",
                    "value": "GET, POST, PUT, DELETE, OPTIONS"
                },
                {
                    "key": "Access-Control-Allow-Headers",
                    "value": "Content-Type, Authorization"
                }
            ]
        }
    ],
    "rewrites": [
        {
            "source": "/(.*)",
            "destination": "/"
        }
    ],
    "builds": [
        {
            "src": "index.js",
            "use": "@vercel/node"
        }
    ]
}

```

### リポジトリの作成

githubに作成したinstant-proxyのリポジトリを作成してファイルをpushしてください。

### vercelの設定

[vercel](https://vercel.com/)にアクセスしてログインします。
（アカウントがない場合はvercelのヘルプに従ってアカウントを作成してください。）

Overviewの`Search Repository and Projects`に作成したリポジトリ名`instant-proxy`を検索して`Import`をクリックし、`Configure Project`で`Deploy`をクリックしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/43fa9f41-4184-1f90-7d46-0324fb9a862f.png)

作成されたプロジェクトの`Visit`をクリックするとデプロイしたページにリダイレクトされます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/02409c1b-5c81-7525-4c08-94b4b35ca6ad.png)

無事にデプロイされていることが確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/aead1eb6-669a-0852-1265-c482a48011a6.png)

## 動作確認

デプロイしたプロキシサーバーをローカルのpostmanから使用してみます。
デプロイされたURLにパス`/proxy`を加えてGetリクエストします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2527942/119a495c-79ea-9710-685e-a5bfc78d9423.png)

ローカルで検証したように転送先のAPIの結果が取得できています🎉


## おわりに

今回は本当に簡素ななんちゃってプロキシでしたが、カスタマイズしていけばテストやCORSの回避など色々と便利な場面がありそうです。
数行で実装できるので、興味ある方は色々と遊んでみてください👍


## 参考

https://github.com/chimurai/http-proxy-middleware