Serverless FrameworkでReactを使ったWordPress記事一覧ページを作る

Serverless FrameworkでReact + WP APIなwebページを作ってみたので、ひとまず覚書


## 使用ライブラリ
`serverless`コマンドはグローバルインストールされている前提。
バージョン対応とかを考えると、npm側でやったほうがいいのだろうけど。

```package.json
{
  "name": "sls-wordpress-frontend",
  "version": "0.0.0",
  "description": "",
  "main": "src/index.js",
  "devDependencies": {
    "babel-core": "^6.4.5",
    "babel-loader": "^6.2.1",
    "json-loader": "^0.5.4",
    "babel-plugin-add-module-exports": "^0.1.2",
    "babel-preset-es2015": "^6.3.13",
    "babel-preset-react": "^6.3.13",
    "serverless-webpack": "^1.0.0-rc.3",
    "webpack": "^1.13.1",
    "webpack-node-externals": "^1.3.3"
  },
  "dependencies": {
    "react": "^0.14.6",
    "react-dom": "^0.14.6",
    "wpapi": "^1.0.0"
  }
}
```


## Webpackの設定
json-loaderはwpapiをrequireする際に必要となります。
`dependencies`内のライブラリをLambdaに同梱したいので、`webpack-node-externals`を採用。

```webpack.config.js
var path = require('path');
var nodeExternals = require('webpack-node-externals');

module.exports = {
  entry: {
      '/src/index': './src/index.js'
  },
  target: 'node',
  resolve: {
    extensions: ['', '.js', '.jsx'],
    modulesDirectories: [
      'node_modules'
    ]
  },
  externals: [nodeExternals()],
  output: {
    libraryTarget: 'commonjs',
    path: path.join(__dirname, '.webpack'),
    filename: '[name].js'
  },
  module: {
    loaders: [
      {
        test: /\.js$|\.jsx$/,
        loader: 'babel-loader',
        include: path.join(__dirname,  'src')
      },{
        test: /\.json$/,
        loader: 'json-loader'
      }
    ]
  }
};

```

## Lambda function
とりあえず動くの優先ということで、１ファイル内にReactのコンポーネントも詰め込んでます。
やっていることは以下の３つです。

- wpapiライブラリでのWordPress記事情報取得
- React SSRでのwebページレンダリング
- Lambdaのcallbackで作成したHTMLを返す

```src/index.js
'use strict';
const WPAPI = require( 'wpapi' );
import React, { Component } from 'react';
import { renderToString } from 'react-dom/server';

class WPPost extends  Component {
    constructor(props, context) {
        super(props)
    }
    render() {
        const post = this.props.post
        return (
            <archive key={post.id}>
                <h2>{post.title.rendered}</h2>
                <div dangerouslySetInnerHTML={{__html: post.excerpt.rendered}} />
            </archive>
        )
    }
}

class App extends Component {
    constructor(props, context) {
        super(props)
    }
    render() {
        const postList = this.props.posts.map( post => {
            return <WPPost post={post} />
        })
        return (
            <main>
                <h1>WP Postlist</h1>
                {postList}
            </main>
        )
    }
}

function renderFullPage(renderedContent) {
  return `<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
	</head>
	<body>
		${renderedContent}
	</body>
</html>`;
}

module.exports.index = (event, context, callback) => {
    const wp = new WPAPI({ endpoint: 'https://api.wp-app.org/wp-json' });
    wp.posts().then(function( data ) {
      const renderedContent = renderToString(<App posts={data} context={context}/>);
      const renderedPage = renderFullPage(renderedContent);
      callback(null, renderedPage);
    }).catch(function( err ) {
        callback(err);
    });
};

```

## serverless.yml
API GatewayでのContent-Type指定やpluginsの指定などをお忘れなく。
独自ドメインについては、SSL証明書の設定等が必要となりますのでまたおいおい。

```serverless.yml
service: sls-wordpress-frontend
plugins:
  - serverless-webpack

provider:
  name: aws
  runtime: nodejs4.3
  stage: dev

package:
  include:
    - node_modules/
    - src/

custom:
  webpackIncludeModules: true

functions:
  index:
    handler: src/index.index
    events:
      - http:
          path: v1/index
          method: get
          integration: lambda
          response:
            headers:
              Content-Type: "'text/html'"
            template: $input.path('$')

```

## デプロイ

```
$ sls deploy
```
`serverless-webpack`がよしなにしてくれるので、デプロイはいつも通りです。
デプロイ後、API Gatewayのエンドポイントにアクセスして表示確認しましょう。


## 今後について
検索とか記事ページ遷移とかもできるようにしたいので、そのへんまでまとまったら汎用化しようかなと。

README.mdもまだ無いですが、リポジトリは以下です。
https://github.com/hideokamoto/sls-wordpress-frontend