Node.js+ExpressでAPIサーバを立てる(ES6, Webpack, Flow, Jest, ESLint)
まずJavaScriptの歴史をざっと見てみる
1995年にJavaが登場したが、その勢いに便乗して、ブレンダン・アイクが開発したLiveScriptがJavaScriptへと改名したことにより、 JavaScript爆誕。
その翌年、マイクロソフトがJavaScriptのライセンスがなかったために独自のJScriptを開発。ところが、JavaScriptを開発していたNetscape Navigator社は、JavaScriptとJScriptの互換性があまりにひどかったことから1997年にECMA InternationalにJavaScriptとJScriptの統合を依頼。このJavaScriptのメイン機能を乗せた言語をECMAScript(ES)と言う。統合を依頼とはいったものの、この統合がなかなかうまく進まず、2000年代前後は、JavaScriptが重い・セキュリティホールになりがち、などと言った理由から人気が落ち、Flashの方が人気を博した。
しかし、2005年にGoogle mapがAjaxを駆使して非同期通信をWebブラウザからできるようにしたことで、JavaScriptが再度盛り上がることとなった。2008年にはV8というJavaScriptエンジンが登場したりと、さらに熱が高まっていった。
JavaScriptの最大の欠点は モジュール機能の欠如している ことだった。それを解決するべく生まれたのが CommonJS である。これはあくまで規格であって、2009年に登場したNode.jsはCommonJSの1つである。Node.jsの登場によって、JavaScriptはブラウザ以外からも利用できることが証明された。そして、Node.jsのようにCommonJSで作られたツール群をブラウザでも利用できるようにしたい、というニーズが生まれ、その解決方法がbrowserifyやWebpackである。gulpやgruntはタスクランナー(JavaScriptのコードを書いてから、実際にブラウザで使えるまでの諸々の作業を自動化してくれるツール)だがWebpackはその機能も含んでいる。
一方で、ECMAScriptの策定も進んでおり、2016年に策定されたECMAScriptはES7と呼ばれ、2015年に作成されたものはES6と呼ばれる。ES6やES7のように新しい構文が登場する一方で、ついていけないブラウザには babel と呼ばれるものを使って対応することになる。
以下を参照
Node.jsでAPIサーバを実装していく
では、APIサーバをNode.js(Express)で構築して行くことにする。フロントエンドをVue.jsやReact.jsで実装するとなると、同じリポジトリでAPIサーバ・フロントエンドの実装を行うことが想定されるが、その場合は、Node.jsもWebpackでES6の記法に従うことになるだろう(const hoge = require('hoge') ではなく、 import hoge from 'hoge' という記法になるイメージ)。
まずExpressをインストールして簡単なサーバを構築する
$ yarn add express
次にルートディレクトリに、 app.js を作成して以下のように編集する。
// app.js
const express = require('express');
const app = express();
app.get('/', function(req, res){
res.send({ message: 'hello world' });
});
app.listen(3000);
ディレクトリ構成は以下のようになる。
. ├── app.js ├── package.json └── yarn.lock
node app.js とすると http://localhost:3000/ が起動するはず。
{ "message": "hello world" }
が返ってくれば成功。
Node.jsをWebpack+ES6に対応させる
今の状態だとモジュールのインポートがReact.jsなどと異なった形である( import hoge from 'hoge' ではなく、 const hoge = require('hoge') )。React.jsでお馴染みの flow や ESLint、Jestにも対応させるべく、WebpackでES6対応する。
Webpackのセットアップ
$ yarn add -D babel-core babel-polyfill babel-preset-env babel-loader@7 webpack webpack-cli webpack-merge flow-babel-webpack-plugin webpack-node-externals $ yarn add -D babel-regenerator-runtime babel-preset-es2015 babel-preset-stage-2
Webpackの設定ファイルを書く。
configというディレクトリを作成し、 webpack.config.base.js という名前で以下のように実装する。
const path = require('path');
const nodeExternals = require('webpack-node-externals');
const FlowBabelWebpackPlugin = require('flow-babel-webpack-plugin');
module.exports = {
target: 'node',
externals: [nodeExternals()],
mode: 'development',
entry: ['babel-regenerator-runtime', './app.js'],
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'server.js',
},
resolve: {
modules: ['node_modules'],
},
module: {
rules: [
{
test: /\.js$/,
use: [
{
loader: 'babel-loader',
options: {
plugins: ['transform-flow-strip-types'],
presets: ['flow', 'stage-2'],
},
},
],
// node_modules は除外する
exclude: /node_modules/,
},
],
},
plugins: [new FlowBabelWebpackPlugin()],
node: {
net: 'empty',
tls: 'empty',
dns: 'empty',
fs: 'empty',
__dirname: true,
},
watchOptions: {
ignored: ['node_modules'],
},
};
続いて、同じディレクトリに、 webpack.config.dev.js と言う名前で以下のように編集する。
const merge = require('webpack-merge');
const baseConfig = require('./webpack.config.base');
const config = merge(baseConfig, {
mode: 'development',
});
module.exports = config;
.babelrc というファイルを作成し、中身を以下のように編集しておく。
{ "presets": ["es2015", "flow"] }
flowのセットアップ
続いて、flowのセットアップをしていく。
$ yarn add -D babel-cli babel-preset-flow flow-bin $ yarn flow init
すると、 .flowconfig が生成されるので、以下のように編集しておく。
[ignore] .*/node_modules/.* .*/dist/.* .*/test/.* [include] [libs] flow-typed [lints] deprecated-call-syntax=off [options] esproposal.decorators=ignore module.name_mapper='^src\/\(.*\)$' -> '<PROJECT_ROOT>/src/\1' module.system=haste all=false
ESLintのセットアップ
コードの静的解析ツールESLintを入れる。
$ yarn add -D eslint $ ./node_modules/.bin/eslint --init

これで生成された .eslintrc.json を以下のように編集する。
{ "extends": "airbnb-base", "parserOptions": { "ecmaVersion": 7, "sourceType": "module" }, "parser": "babel-eslint", "env": { "browser": true, "es6": true }, "plugins": ["flowtype"], "rules": { "flowtype/no-types-missing-file-annotation": 1, "max-len": [2, 100, 4, {"ignoreUrls": true}] } }
上の eslint の初期化時には関連するライブラリをインストールしていないので、以下でインストールする。
$ yarn add -D babel-eslint eslint-plugin-flowtype eslint-config-airbnb-base eslint-plugin-import
.eslintignore というファイルを生成して、ESLintの対象から外すものを明示しておく。
server.js flow-typed __tests__ __mocks__ node_modules .idea .vscode
これで yarn eslint . と実行すると、ESLintの結果が出力される。ここで出たエラーは、 yarn eslint . --fix とすると自動で修正される。
Jestのセットアップ
$ yarn add -D jest $ yarn add -D babel-jest babel-core
Node.jsをWebpackで起動する
この時点で、 Node.jsをWebpackで起動するのに必要なものは揃った。
package.json は以下のようにする。
{ "main": "./dist/server.js", "scripts": { "start": "node ./dist/server.js", "flow": "flow", "eslint": "eslint .", "test": "jest", "webpack-dev": "webpack --config config/webpack.config.dev.js" }, "dependencies": { "express": "^4.16.4" }, "devDependencies": { "babel-cli": "^6.26.0", "babel-core": "^6.26.3", "babel-eslint": "^10.0.1", "babel-jest": "^23.6.0", "babel-loader": "7", "babel-polyfill": "^6.26.0", "babel-preset-env": "^1.7.0", "babel-preset-es2015": "^6.24.1", "babel-preset-flow": "^6.23.0", "babel-preset-stage-2": "^6.24.1", "babel-regenerator-runtime": "^6.5.0", "eslint": "^5.8.0", "eslint-config-airbnb-base": "^13.1.0", "eslint-plugin-flowtype": "^3.2.0", "eslint-plugin-import": "^2.14.0", "flow-babel-webpack-plugin": "^1.1.1", "flow-bin": "^0.85.0", "jest": "^23.6.0", "webpack": "^4.25.1", "webpack-cli": "^3.1.2", "webpack-merge": "^4.1.4", "webpack-node-externals": "^1.7.2" } }
最初に作成した app.js の中身をES6に対応させるために1行目を以下のように書き換える。
import express from 'express';
package.json にあるように起動コマンドを実行してみる。
$ yarn webpack-dev $ yarn start
すると、 localhost:3000 が起動できるようになっているのが確認できる。
編集したファイルを自動で反映するようにする
$ yarn add -D nodemon concurrently
必要なライブラリのインストールが完了したら次は、 package.json に以下の行を追加する。
{
...
"script": {
...
"dev": "concurrently \"NODE_ENV=development webpack -w --config config/webpack.config.dev.js\" \"nodemon --watch ./dist/server.js\""
},
...
}
以下でローカルホストが起動したら成功。
$ yarn dev
ソースコードはこちら。