フロントエンドAPIモック導入したことでビルド時間が爆速になった
おはようございます、モチベーションクラウドの開発に参画している@sinpaoutです。
TL; DR
Docker + Rails + Mysql + webpackerで起動するのが時間かかりすぎるので
全てを捨ててnodeのみ(webpack-dev-server)で生きていくことに。。。
環境
Rails + Mysql + webpacker(vue.jsビルド)がDockerイメージとして管理され、
コマンド一発で開発環境を起動できる。
問題
起動にdb:setup
や db:migrate
などDBの初期化が走り大体3〜5分前後かかる。
更に画面が起動後にWebpackerが走り、フロントのビルドは1分ちょい。
DBの処理化なしでマイグレーションのみでもRailsが立上がるまで2,3分かかってしまう。
普段はJSのビルド時間も入れると5分はかかってしまう。
立上がったあとは毎回ログインして目的の画面に進むが途中で何かしらエラーに直面して進めなくなることはよくある。
また、 Seeds
データが全てのパターンきちんと用意さていない事が多く
目的の画面まで到達するのにかなりの時間と労力がかかってしまう。
ときにはDockerが壊れて丸一日をクジラのお世話に費やされてしまうエンジニアもいた。
Seedsデータを用意できても更新系やデータのありなしなどの
パターンはDBを直接触る必要が出てき来たりするので手間がかかるので
APIモックシステムの構築を検討することになった。
APIモックとは
APIをJSONファイルとしてwebpack-dev-serverなど簡易Webサーバで提供する仕組み
Railsなどのバックエンドを起動しないため高速に開発環境を起動可能
バックエンドの関係者たちを退場させる:
Nodeは今どきnvmなど入れとけばバージョン管理も楽なのでDockerも退場。
(みんな今まで頑張ってくれてありがとう。。。)
アプリのAPIパスとJSONファイルのマッピングはyamlファイルで定義し
axiosのintercepeterでアドレスを変換する
使ってみる
マッピングの設定:
# js/mocks/apiMapper.yml
default:
desc: デフォルトのモック
api:
/users: mocks/users.json
/users/1/: mocks/users/detail.json
users.json
の中身
{
"users": [{
"id": "",
"name": "",
}]
}
上記の例は
/users
のAPIをmocks/users.json
に
/users/1/
のAPIを mocks/users/detail.json
に置き変える。
※ パスのidの部分は全て1として解釈するようにする。
APIをJSONファイルと関連付けてくれる人
API Mockerの詳細
// js/mocks/apiMocker.js
import urlParse from 'url-parse'
import apiMapper from './apiMapper.yml'
const defaultApi = apiMappers.default.api
global.apiMockIntercepter = (config) => {
const originalUrl = config.url
const parsedUrl = urlParse(config.url)
let apiPath = parsedUrl.pathname.replace(new RegExp(`^${config.baseURL}`), '')
// idをすべて1に置き換える
apiPath = apiPath.replace(/\/([0-9]+)\//ig, '/1/')
const mockApiPath = defaultApi[apiPath]
if (mockApiPath) {
// 強制的にGETに
config.method = 'get'
config.url = config.baseURL + mockApiPath
// 元情報を書き出す
console.info('api mocked', originalUrl, mockApiPath)
}
return config
}
※ 環境に合わせてパスを調整する必要がある。
Webpackの設定
普段は index
のみバンドルするが、実行環境がlocal
の時のみ apiMocker
を挿入する。
apiMocker
が index
より前に挿入する必要がある。
CopyWebpackPlugin:
モックのJSONファイルをoutputパスにコピーさせる
// webpack.local.js
if (process.env.DEV_ENV === 'local') {
...
// Inject api mocker
webpackConfig.entry.index = [
`${dir.mocks}/apiMocker.js`,
`${dir.js}/index.js`
]
webpackConfig.plugins.push(new CopyWebpackPlugin([{
from: `${dir.mocks}/api/`,
to: `${output.path}/api/`
}]))
...
}
axiosの設定
実行環境がlocal
の時かつ apiMockIntercepter
が存在したら使うようにする
axios.interceptors.request.use((config) => {
if (process.env.DEV_ENV === 'local' && global.apiMockIntercepter) {
return global.apiMockInterceptor(config)
}
return config
})
モックマッピングの拡張
パターンごとに切り替えられるようにする。
# js/mocks/apiMapper.yml
default: &default
desc:
api: &api
/users: /users.json
/users/1/: /users/detail.json
...
noData:
<<: *default
api:
<<: *api
/users: /users_no_data.json
noData
のパターンでは /users
をデータなしに置き換える
users_no_data.json
{
"users": []
}
一捻り
Devtoolのネットワークでデバッグ
元のAPIやPOST場合は中身がリクエストの中身がわからなくなるので
console
で元の情報を表示するように apiMocker
を更新
// js/mocks/apiMocker.js
global.apiMockIntercepter = (config) => {
const originalUrl = config.url
...
if (mockApiPath) {
...
// 元情報を書き出す
console.info('api mocked', originalUrl, mockApiPath)
}
return config
}
UIからの置き換え
開発時に直接モックを切替変えられると、より効率上がるので
画面の右上あたりにパターン一覧を置き換えるポップアップ的なものを実装。
ブラウザーのリロード後もモックの設定を有効にしたいのでsessionStorageに突っ込む。
cookieを使わない理由はhttpOnlyなどを考慮したため。
E2E用
Cypressなどからパターンを変えられるように apiMocker
を更新
// js/mocks/apiMocker.js
const apiMocker = {
currentPattern: sessionStorage.getItem('apiMockerPattern') || 'default'
}
// E2Eようの外部モジュールから参照できるようにグローバル変数にしておく
window.apiMocker = apiMocker
...
// モックパターンをセットする関数を用意
apiMocker.setCurrentMock = function (patternName) {
apiMocker.currentPattern = patternName
sessionStorage.setItem('apiMockerPattern', apiMocker.currentPattern)
}
global.apiMockIntercepter = (config) => {
...
const apiMap = apiMappers[apiMocker.currentPattern].api
const mockApiPath = apiMap[apiPath]
...
}
Cypressから使う
cy.window().then((win) => {
win.apiMocker.setCurrentMock('noData')
cy.visit('localhost:8081/company/1/users')
})
結果
- 開発環境の立ち上げ5分 → 1分ちょい
- パターンごとにのデータの用意がjsonファイルのみで完結(ストレス激減)
- Dockerの死亡やSeed不足の悩みから開放
- E2Eからのパターンが切替えられるようになるためUIテストが書きやすい
- DirやPOなどのエンジニア以外への画面共有が楽
今後の追加機能
- Webpackerのビルド廃止(Railsと完全に縁を切る)
- Railsは嫌いではない(むしろ好き)がやりすぎると制御しづらくなるの要注意。
- POSTなどの更新系API対応
- 4xx、5xx系のエラー対応
- webpackのバージョンアップやチューニング
- プルリクエスト単位でのレビュー環境の用意
- 静的なファイルで再現できるためS3にビルド結果を展開が可能