ReactアプリにNode.jsサーバープロキシを設定する方法

October 09, 2018
執筆者
Phil Nash
Twilion
レビュー担当者
Aya Shiomi
Twilion

Node.js + Express

この記事はTwilio Developer EvangelistのPhil Nashがこちらで執筆した記事の日本語版です。執筆から時間が経過しているため、2021年5月時点で動作確認を行い、一部オリジナルの記事よりコードを修正しています。

 

Create React Appは、Reactアプリケーションを稼働させるための優れたツールです。Twilio Videoチャット用のアクセストークン生成など、サーバーサイドコンポーネントを必要とするアプリケーションを構築、試作する場合ではどうでしょうか。1つのコマンドですべてを動かせるため、同じプロジェクト内で1台のサーバーを操作するのが最も簡単です。

この記事を最後まで読むと、Reactアプリと連動するExpressサーバーの設定方法を理解できるようになります。お急ぎの場合は、GitHubのスタータープロジェクトをご覧ください。

動作の仕組み

Create React Apppackage.jsonで設定できるオプションには、text/html以外のリクエストを別のバックエンドにプロキシするものがあります。この機能を利用すると、あらゆる場所で稼働しているアプリケーションにプロキシすることができますが、今回はReactプロジェクト内でサーバーを実行できるようにしたいと思います。

ここでは、1つのコマンドでReactアプリとExpressサーバーを同時に実行できるようにするために、いくつかのnpmモジュールをまとめて、プロキシできるようにします。

はじめに

以降の手順では、Node.jsとnpmをインストールしておく必要があります。

まず、Create React Appを使用し、新しいReactアプリを作成します。ただし、create-react-appパッケージを全体にインストールする必要はありません。代わりに、以下のコードを実行します。

npm init react-app my-new-app
cd my-new-app

このコードでは、npm initにイニシャライザーの名前を指定し、create-をその前に付加し、npxを使用してコマンドをインストールおよび実行します

新しいReactアプリケーションを実行し、正常に生成されたことを確認します。

npm start

回転するReactロゴが表示されていればここのまでの作業は順調に進んでいます。

サーバーの追加

サーバーの依存関係を、ReactアプリのdevDependenciesに追加します。依存関係はフロントエンドの構築に含まれていません。

Cmd/Ctrl + Cでサーバーを停止し、npmを使用してExpressをインストールします。

npm install express --save-dev

Express v4.16.0以降でbody-parserが組み込まれているためオリジナル投稿から記述を変更しています。

以下の依存関係を追加し、フロントエンドとサーバーを同時に実行できるようにします。

npm install node-env-run nodemon npm-run-all express-pino-logger pino-colada --save-dev

.envというファイルをプロジェクトディレクトリに作成し、環境変数を保存します。この段階でディレクトリに何かを追加する必要はありませんが、今後の開発においてサーバーで必要となるAPIキーなどの資格情報を格納するのに役立ちます。

次に、プロジェクトディレクトリに、serverという新しいディレクトリとserver/index.jsファイルを作成します。ここでは、テスト用に小規模なアプリケーションを作成します。そのため以下のコードをserver/index.jsに追加します。

const express = require('express'); 
const pino = require('express-pino-logger')(); 

const app = express(); app.use(express.urlencoded({ extended: false })); 
app.use(pino); 

app.get('/api/greeting', (req, res) => { 
    const name = req.query.name || 'World'; 
    res.setHeader('Content-Type', 'application/json'); 
    res.send(JSON.stringify({ greeting: `Hello ${name}!` })); 
}); 

app.listen(3001, () => console.log('Express server is running on localhost:3001') );

Package.jsonを開き、"scripts"オブジェクトに新しいスクリプトを追加し、node-env-runおよびnodemonを使用してサーバーを実行します。

"scripts": { 
    // other scripts 
    "server": "node-env-run server --exec nodemon | pino-colada" 
},

以下のスクリプトを実行し、サーバーが正常に稼働していることを確認します。

npm run server

http://localhost:3001/api/greetingを開いて動作を確認します。JSON応答と「Hello World!」という挨拶文が表示されるはずです。nameというクエリーパラメーターをURLに追加し、結果を確認します。

サーバーとReactアプリの実行

サーバーとReactアプリケーションを同時に実行するには、package.jsonにコードをいくつか追加する必要があります。

まず、サーバーに対するプロキシを設定します。"proxy"キーをpackage.jsonに追加します。サーバーはすでに、ポート3001で稼働するように設定済みとなるため、プロキシの場所をlocalhost:3001に指定します。

"proxy": "http://localhost:3001"

サーバーとフロントエンドを同時に実行するスクリプトが必要です。ここではnpm-run-allを使用します。2つのスクリプトを同時に並列モードで実行する場合は、npm-run-allが提供するショートカットrun-pコマンドを使用すると便利です。

以下のコードをpackage.jsonの"scripts"セクションに追加します。

"scripts": { 
    // other scripts 
    "server": "node-env-run server --exec nodemon", 
    "dev": "run-p server start" 
},

npm run devを実行すると、Reactアプリケーションとサーバーが両方とも起動します。ただし、この段階ではlocalhost:3000/api/greetingにリクエストしてもCreate React AppプロキシはベースHTMLのみを応答し、実際のページは読み込めません。

代わりに、Reactアプリのコンポーネント内で試してみましょう。

Reactからプロキシ化されたサーバーを使用

ここからの説明はReactの関数コンポーネントに対応するため変更を加えています。変更後のコード全体はこちらでご覧いただけます。

関数コンポーネントでステートフックを利用するため、App.jsuseStateを追加します。

import logo from './logo.svg';
import './App.css';
import React, {useState} from 'react';

Appコンポーネントにフォームを追加します。このフォームは、/api/greetingコンポーネントを使用して挨拶文を作成し、ページに表示します。そのため、以下のステートフックと関数をsrc/App.jsのApp関数コンポーネントに追加します。

function App() {
  const [name, setName] = useState('');
  const [greeting, setGreeting] = useState('');

  const handleChange = (event) => {
    setName(event.target.value);
  }
  
  const handleSubmit = (event) => {
    event.preventDefault();
    fetch(`/api/greeting?name=${encodeURIComponent(name)}`)
    .then(response => response.json())
    .then(state => setGreeting(state.greeting)); 
  }

  // return function continues...

続けて以下のフォームをreturn関数に追加します。

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <form onSubmit={handleSubmit}>
            <label htmlFor="name">Enter your name: </label>
            <input
              id="name"
              type="text"
              value={name}
              onChange={handleChange}
            />
            <button type="submit">Submit</button>
          </form>
          <p>{greeting}</p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
      </header>
    </div>
  );

Reactアプリをブラウザで開き、名前を入力して送信(Submit)します。挨拶文が表示され、Reactアプリがプロキシ化されたサーバーと通信していることが分かります。

ここがスタート地点です。

Create React Appにより、Reactアプリケーションは問題なく起動しましたが、サーバーサイドのコンポーネントも必要な場合は手順が複雑になります。この記事では、proxyオプションとnpm-run-allなどのツールを併用してExpressサーバーを実行する方法を解説しました。

この記事で示したコードはすべて、こちらのGitHubリポジトリ(オリジナル)、変更版で確認できます。Express APIを使用してReactアプリを構築する場合は、このコードを出発点として使用できます。さらに、Reactを使用してTwilio VideoTwilioチャットのアプリケーションを作成する場合は、Twilioブランチからどちらのアプリ用のアクセストークンでも返すよう設定できます。READMEの指示通りに進めてください。

このテンプレートを使用すると、Expressサーバーを利用してReactアプリケーションを素早く簡単に構築できます。この記事が皆さまのアイデア実現の基礎となればと思います。

I can't wait to see what you build!