Single Page Application の Mock Server を作る 2016
対象読者
以下のワードに興味が無い方はたぶん読まなくても良い記事です。
json-server
とjwt
browsersync
とhttp-proxy-middleware
npm-run-all --parallel
準備
% mkdir practice-mock-api && cd $_ % mkdir -p mock src/components www % npm init -y
json-server と jwt
json-server は express がベースとなっているので少し手を加えて jwt
が関わる処理を追加します。
db.json は jsonplaceholder のデータを流用します。
% npm install --save-dev json-server body-parser jsonwebtoken express-jwt % curl http://jsonplaceholder.typicode.com/db -o ~/practice-mock-api/mockServer/db.json
create mockServer.js:
const jsonServer = require('json-server') const server = jsonServer.create() const router = jsonServer.router('./mock/db.json') const jwt = require('jsonwebtoken') const expressJwt = require('express-jwt') const bodyParser = require('body-parser') server.use(bodyParser.urlencoded({ extended: false })) server.use(bodyParser.json()) const SERCRET = "shhhhhhared-secret" function createToken() { return jwt.sign({ exp: Math.floor(Date.now() / 1000) + (60 * 60), // 1 hour data: { username: "okamuuu", roles: ["operator", "admin"], } }, SERCRET) } const token = createToken() console.log("=== create new token ===") console.log(token) jwt.verify(token, SERCRET, (err, decoded) => { console.log("=== decoded ===") console.log(decoded) }) server.post('/auth/login', (req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({token: createToken()})) }) server.post('/auth/refresh', expressJwt({secret: SERCRET}), (req, res) => { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify({token: createToken()})) }) server.use('/api', expressJwt({secret: SERCRET}), router) server.listen(3000, function () { console.log("=== Mock API Server is running ===") console.log("=== USAGE ===") console.log(`curl -H \"Authorization: Bearer ${token}\" http://localhost:3000/api/posts`) })
起動するとこんな感じです。
% node mockServer.js === create new token === eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODAzOTAxNTksImRhdGEiOnsidXNlcm5hbWUiOiJva2FtdXV1Iiwicm9sZXMiOlsib3BlcmF0b3IiLCJhZG1pbiJdfSwiaWF0IjoxNDgwMzg2NTU5fQ.s5f1tRvFCwIZeJant3LLyyqWcVFlny4oQRv6KJ3PCRM === decoded === { exp: 1480390159, data: { username: 'okamuuu', roles: [ 'operator', 'admin' ] }, iat: 1480386559 } === Mock API Server is running === === USAGE === curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0ODAzOTAxNTksImRhdGEiOnsidXNlcm5hbWUiOiJva2FtdXV1Iiwicm9sZXMiOlsib3BlcmF0b3IiLCJhZG1pbiJdfSwiaWF0IjoxNDgwMzg2NTU5fQ.s5f1tRvFCwIZeJant3LLyyqWcVFlny4oQRv6KJ3PCRM" http://localhost:3000/api/posts
動作確認をします。無事 JWT で認証する必要がある WEB API の mock ができました。
% TOKEN=`curl -s -X "POST" http://localhost:3000/auth/login | jq .token -r` % curl -s -H "Authorization: Bearer $TOKEN" http://localhost:3000/api/posts | jq length 100
browsersync と http-proxy-middleware
前回の記事で紹介したように BroserSync と webpack を使って Hot Module Replacement
するためのサーバーを作成します。
前回の記事との違いは port の指定と起動する度にブラウザが open しないようにオプションを追加していて、http-proxy-middleware
を使って /api
と /auth
へのリクエストを mockServer へ渡すようにしています。
% npm install --save react react-dom % npm install --save-dev babel-core babel-loader babel-preset-es2015 babel-preset-stage-0 babel-preset-react % npm install --save-dev browser-sync webpack webpack-dev-middleware webpack-hot-middleware http-proxy-middleware
create webpack.config.js
const path = require('path') const webpack = require('webpack') module.exports = { debug: true, devtool: 'inline-source-map', entry: [ 'webpack/hot/dev-server', 'webpack-hot-middleware/client', './src/index.js' ], output: { publicPath: "/", path: path.resolve('www'), filename: "bundle.js", }, plugins: [ new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin() ], resolve: { extensions: ['', '.js'], }, module: { loaders:[ { test: /\.jsx?$/, exclude: /node_modules/, loaders: ['babel'] } ] } }
create devServer.js
var webpack = require('webpack') var proxy = require('http-proxy-middleware'); var browserSync = require('browser-sync') var webpackDevMiddleware = require('webpack-dev-middleware') var webpackHotMiddleware = require('webpack-hot-middleware') var config = require('./webpack.config') var bundler = webpack(config) var apiProxy = proxy('/api', { target: 'http://localhost:3000', logLevel: 'debug' }); var authProxy = proxy('/auth', { target: 'http://localhost:3000', logLevel: 'debug' }); browserSync({ port: 8080, open: false, server: { baseDir: config.output.path, middleware: [ apiProxy, authProxy, webpackDevMiddleware(bundler, { publicPath: config.output.publicPath, stats: { colors: true } }), webpackHotMiddleware(bundler) ] }, files: [ 'www/*.html' ] })
create .babelrc
{ "presets": ["es2015", "stage-0", "react"] }
create www/index.html
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <title>title</title> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> </head> <body> <div id="root"></div> <script src="/bundle.js"></script> </body> </html>
create src/index.js
import React from 'react' import { render } from 'react-dom' import App from './components/App' render( <App />, document.getElementById('root') ) if (module.hot) { module.hot.accept('./components/App.js', function() { const NextApp = require('./components/App').default; render(<NextApp/>, document.getElementById('root')); }); }
create scr/components/App.js
import React from 'react' export default (props) => ( <h1>HMR test</h1> )
起動
% node devServer.js
npm-run-all
最後に2つのフォアグランドプロセスをまとめて実行、停止できるようにします。
% npm install --save-dev npm-run-all
edit package.json
"scripts": { "mock-server": "node mockServer.js", "dev-server": "node devServer.js", "serve": "npm-run-all --parallel mock-server dev-server" },
rpm run serve
と Ctrl-C
を使って起動、停止ができます。
SEE ALSO
https://github.com/chimurai/http-proxy-middleware/blob/master/examples/browser-sync/index.js