In this example, we will learn how to setup a React-based web app that is served from a local development server, and, at the same time, to have this application to load JSON data from another local server using Express. The logical issue that arises relates to cross-domain access; because your app is first served by the Webpack development server — let's say it comes from the localhost:8080. With that, XMLHTTPRequest access to other server-port is not allowed.
In this proposed solution, we will simply setup a proxy server using Webpack's configuration, which is intended to allow both the development environment and the API server working at the same time.
The source in this repository is just kept for the sake of referencing the history of the project changes. Throughout this page you will be able to see links to positions in the history of changes.
First, make sure your .gitignore does not include the ./myProject/node_modules and ./myProject/dist. Dist is where files will be automatically generated by Webpack (and Babel).
myProject/node_modules/
myProject/dist/
mkdir myProject
cd myProject
The setup here assumes NodeJS 8.6.0.
npm init
And other preset definitions so that Babel can transpile ES2015, and React-based code.
npm install react react-dom --save
npm install webpack webpack-dev-server babel-core babel-loader babel-preset-es2015 babel-preset-react babel-preset-stage-2 --save-dev
Check so far tree at this stage
"scripts": {
"start": "node app.js",
"babel-start": "npm run build",
"build": "webpack --mode development && cp src/index.html dist/index.html && webpack-dev-server --mode development --content-base src/ --inline --hot --history-api-fallback",
"build:prod": "webpack --mode production && cp src/index.html dist/index.html"
},
Create your webpack.config.js
var path = require("path");
var DIST_DIR = path.resolve(__dirname, "dist");
var SRC_DIR = path.resolve(__dirname, "src");
var config = {
entry: SRC_DIR + "/app/index.js",
output: {
path: DIST_DIR + "/app",
filename: "bundle.js",
publicPath: "/app/"
},
module: {
rules: [
{
test: /\.js?/,
include: SRC_DIR,
loader: "babel-loader",
query: {
presets: ["react", "es2015", "stage-2"]
}
}
]
}
};
module.exports = config;
As you try to execute with "npm run build", in the first time, you should be prompted to install a cli tool.
Do an initial test to check if the Webpack "npm run babel-start" script it properly generating your React code:
npm run babel-start
And test in the browser
http://localhost:8080
'use strict';
const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/api/test', (req, res, next) => {
var outputJSON = {
'status' : null
}
console.log("Data received from client: ")
console.log(req.body);
res
.status(200)
.set('Content-Type', 'application/json')
.send(JSON.stringify(outputJSON))
.end();
});
// This cab be used in case you are serving production
//app.use('/', express.static(path.join(__dirname, 'dist')));
// [START listen]
const PORT = process.env.PORT || 8090;
app.listen(process.env.PORT || 8090, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
// [END listen]
// [END app]
module.exports = app;
devServer: {
proxy: {
"/a/*": {
target: "http://localhost:8090",
secure: false,
rewrite: function(req, options) {
//you can handle rewrite here if you need to
}
},
}
},
node app.js
npm install axios --save
Add the component to the ./src/app directory
import React, { Component } from 'react';
import axios from 'axios';
class APITester extends Component {
constructor(props) {
super(props);
this.state = {
loading : false,
status : 'no data yet'
};
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(event) {
event.preventDefault();
this.setState({loading:true})
var data = {
"argumentToJSON": "foo"
}
// axios.post('/app_send', {data}) // this is like data:data ..
axios.post('/api/test', data)
.then(res => {
if(res.data.status=='ok') {
this.setState({loading: false, status: 'ok'});
};
});
}
render() {
if(!this.state.loading) {
return (
<div className="">
<form onSubmit={this.handleSubmit}>
<p>
Result: {this.state.status} <br />
<input type="submit" value="Call server" />
</p>
</form>
</div>
);
} else {
return (
<div className="">
<p>
loading...
</p>
</div>
);
}
}
}
export default APITester;
Update the App.js to include it :
import React, { Component } from 'react';
import APITester from './APITester';
class App extends Component {
render() {
return (
<div className="App">
<h1 className="App-title">
Webpack development and API JSON via Proxy
</h1>
<APITester />
</div>
);
}
}
export default App;
Run the Dev server
npm run babel-start
Test
http://localhost:8080
In a nutshell:
- You are calling your app from localhost:8080
- Webpack generates all the output files (index.html, index.js etc in the dist directory)
- Webpack will proxy to localhost:8090
- Your app.js will answer /api/test