Node.js 再入門メモ(その 5)

本日のテーマ

今日は「Express で適当な WebAPI を作って React から呼ぶでござる」の巻。

前回の最後にやった「Express + React」を少しだけ発展させたようなものである。

tercel-s.hatenablog.jp

さらに SuperAgent を用いた非同期通信にも挑戦してみる。 例によって、書いて動かすだけで精一杯なので、説明らしい説明はしない。

前準備

まずはプロジェクトの初期設定から。

ターミナルを開いて、プロジェクトディレクトリに cd して npm init。 この操作は何度もやってきているのでもはや慣れたものだ。

$ npm init -y

続いて必要なライブラリを入れていこう。

今回はバックエンドに Express、フロントエンドに React、非同期通信に SuperAgent を使用する。 また JSX のビルドには Babel を使い、さらにモジュールのバンドルに webpack を使う。

というわけで、結局ターミナルには以下のコマンドをひととおり打ち込むことになる。

$ npm i --save express
$ npm i --save react react-dom
$ npm i --save superagent
$ npm i --save-dev webpack
$ npm i --save-dev babel-loader babel-core
$ npm i --save-dev babel-preset-es2015 babel-preset-react

この時点での package.json (の抜粋)を以下に載せておこう。 なお今後のために、最低限 scripts の設定も行っておく。

package.json
{
  "name": "expressapp",
  (略)
  "scripts": {  // 設定しよう
    "build": "webpack",
    "start": "node server.js"
  },
  (略)
  "dependencies": {
    "express": "^4.15.4",
    "react": "^15.6.1",
    "react-dom": "^15.6.1",
    "superagent": "^3.6.0"
  },
  "devDependencies": {
    "babel-core": "^6.26.0",
    "babel-loader": "^7.1.2",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-react": "^6.24.1",
    "webpack": "^3.5.5"
  }
}

さらにプロジェクトのディレクトリ構成を決める。

簡単のため、ディレクトリはシンプルにしておく。

$ mkdir public src

サーバサイドの実装

ここでは「/api/sayHello にアクセスすると、Hello, WebAPI というメッセージを返すだけの超静的な API」を作ろう。 こんなものを作って何が楽しいのか分からないかも知れないが、とりあえず夢を広げるためには極力シンプルなところから始めないと大抵挫折する。

プロジェクトディレクトリ直下(package.json と同階層)に server.js を作ってみる。 これが今回のエントリポイントである。

server.js
const express = require('express')
const app = express()

// URLのルートにアクセスされたときは、
// public 以下を返す
app.use(express.static(__dirname + '/public'))

// /api/sayHello にアクセスされたときは
// 'Hello, WebAPI' というメッセージを JSON 形式で返す
app.get('/api/sayHello', (req, res) => {
  res.json({
    message: 'Hello, WebAPI'
  })
})

app.listen(3000)

とりあえず動作原理を全く意識せずに写経しただけなのだが、それでもソースコードを眺めれば何が起こるのかだいたい分かる。

クライアントサイドの実装

HTML を作る

ファイルの場所に注意。 HTML は public ディレクトリ配下に作る。

public/index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta http-equiv="content-type"
    content="text/html; charset=UTF-8">
    <title>Hello!</title>
  </head>
  <body>
    <div id="Content"></div>
    <script src="./app.js"></script>
  </body>
</html>

この空っぽの HTML に、React が API の実行結果を流し込んでくれることを想定している。

最後の方の <script> タグで app.js を参照しているが、これは JSX をビルドした結果生成されるフロントエンド用のスクリプトである。 今はまだいない。

React から WebAPI を呼ぶ

先ほどの HTML に命を吹き込むべく、WebAPI と連携する React コンポーネントを JSX で書く。

今回、非同期通信で API を呼ぶために SuperAgent を使用した。 Qiita にわかりやすい解説があった。

App コンポーネント生成直後の componentWillMount() にて先ほど作ったサーバ側の API を呼び、取得したメッセージを setState() でステートに設定している。

そして state にセットされたメッセージは render() によってレンダリングされる。

src/index.js
import React, {Component} from 'react'
import ReactDOM from 'react-dom'
import request from 'superagent'

class App extends Component {
  constructor(props) {
    super(props)
    this.state = {
      message: ''
    }
  }

  componentWillMount() {
    // API を呼び出し、結果を State に格納
    request
      .get('/api/sayHello')
      .end((err, data) => {
        if(err) {
          console.error(err)
          return
        }
        this.setState({
          message: data.body.message
        })
      })
  }
  render() {
    return <div>{this.state.message}</div>;
  }
}

ReactDOM.render(<App />, 
  document.getElementById('Content'))

ここまでできたら、

  1. src/index.js に書いた JSX を、
  2. ちゃんとブラウザが理解できる形式に変換して、
  3. public/app.js として保存するように

webpack を設定しよう。

webpack.config.js
const path = require('path')
module.exports = {
  entry: path.join(__dirname, 'src/index.js'),  // ソースファイル
  output: {
    path: path.join(__dirname, 'public'),       // 出力ディレクトリ
    filename: 'app.js'                          // 出力ファイル名
  },
  devtool: 'inline-source-map',
  module: {
    rules:[
      {
        test: /.js$/,           // ファイルのパターン
        loader: 'babel-loader', // 使用するプラグイン
        options: {              // プラグインのオプション
          presets: ['es2015', 'react']
        }
      }
    ]
  }
}

いざ実行

先ほど package.json の scripts に build と start のスクリプトを設定していれば、以下のコマンドでビルドと実行ができるようになっているはずだ。

$ npm run build
$ npm start

http://localhost:3000 にアクセスして「Hello, WebAPI」と表示されたら成功。

f:id:tercel_s:20170826001524p:plain