Compare commits
6 Commits
v2.1.2
...
no-react-d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62a9caad1a | ||
|
|
c800477a1b | ||
|
|
b65b1b2a2a | ||
|
|
d0478f2723 | ||
|
|
23cddeae9c | ||
|
|
09e155d625 |
44
README.md
44
README.md
@@ -2,11 +2,16 @@
|
||||
|
||||
A minimal example of using a Node backend (server for API, proxy, & routing) with a [React frontend](https://github.com/facebookincubator/create-react-app).
|
||||
|
||||
* 📐 [Design Points](#user-content-design-points)
|
||||
* 🕺 [Demo](#user-content-demo)
|
||||
* 🚀 [Deploy to Heroku](#user-content-deploy-to-heroku)
|
||||
* ⤵️ [Switching from create-react-app-buildpack](#user-content-switching-from-create-react-app-buildpack)
|
||||
* 🎛 [Runtime Config](#user-content-runtime-config)
|
||||
* 💻 [Local Development](#user-content-local-development)
|
||||
|
||||
To deploy a frontend-only React app, use the static-site optimized
|
||||
▶️ [create-react-app-buildpack](https://github.com/mars/create-react-app-buildpack)
|
||||
|
||||
⤵️ [Switching from create-react-app-buildpack](#switching-from-create-react-app-buildpack)?
|
||||
|
||||
|
||||
## Design Points
|
||||
|
||||
@@ -16,9 +21,8 @@ A combo of two npm projects, the backend server and the frontend UI. So there ar
|
||||
* [deployed automatically](https://devcenter.heroku.com/categories/deployment) via heroku/nodejs buildpack
|
||||
2. [**React UI**](react-ui/): [`react-ui/package.json`](react-ui/package.json)
|
||||
* generated by [create-react-app](https://github.com/facebookincubator/create-react-app)
|
||||
* deployed via build hooks in the Node server's [`./package.json`](package.json)
|
||||
* module cache configured by `cacheDirectories`
|
||||
* bundling via `heroku-postbuild` hook
|
||||
* deployed via `build` script in the Node server's [`./package.json`](package.json)
|
||||
* module cache configured by `cacheDirectories`
|
||||
|
||||
Includes a minimal [Node Cluster](https://nodejs.org/docs/latest-v8.x/api/cluster.html) [implementation](server/index.js) to parallelize the single-threaded Node process across the available CPU cores.
|
||||
|
||||
@@ -41,7 +45,7 @@ This deployment will automatically:
|
||||
* detect [Node buildpack](https://elements.heroku.com/buildpacks/heroku/heroku-buildpack-nodejs)
|
||||
* build the app with
|
||||
* `npm install` for the Node server
|
||||
* `heroku-postbuild` for create-react-app
|
||||
* `npm run build` for create-react-app
|
||||
* launch the web process with `npm start`
|
||||
* serves `../react-ui/build/` as static files
|
||||
* customize by adding API, proxy, or route handlers/redirectors
|
||||
@@ -79,7 +83,33 @@ If an app was previously deployed with [create-react-app-buildpack](https://gith
|
||||
git commit -m 'Migrate from create-react-app-buildpack to Node server'
|
||||
git push heroku master
|
||||
```
|
||||
|
||||
1. If the app uses [Runtime configuration](https://github.com/mars/create-react-app-buildpack/blob/master/README.md#user-content-runtime-configuration), then follow [Runtime config](#user-content-runtime-config) below to keep it working.
|
||||
|
||||
## Runtime Config
|
||||
|
||||
create-react-app itself supports [configuration with environment variables](https://facebook.github.io/create-react-app/docs/adding-custom-environment-variables). These compile-time variables are embedded in the bundle during the build process, and may go stale when the app slug is promoted through a pipeline or otherwise changed without a rebuild. See create-react-app-buildpack's docs for further elaboration of [compile-time vs runtime variables](https://github.com/mars/create-react-app-buildpack/blob/master/README.md#user-content-compile-time-vs-runtime).
|
||||
|
||||
[create-react-app-buildpack's runtime config](https://github.com/mars/create-react-app-buildpack/blob/master/README.md#user-content-runtime-configuration) makes it possible to dynamically change variables, no rebuild required. That runtime config technique may be applied to Node.js based apps such as this one.
|
||||
|
||||
1. Add the inner buildpack to your app, so that the `heroku/nodejs` buildpack is last:
|
||||
|
||||
```bash
|
||||
heroku buildpacks:add -i 1 https://github.com/mars/create-react-app-inner-buildpack
|
||||
|
||||
# Verify that create-react-app-inner-buildpack comes before nodejs
|
||||
heroku buildpacks
|
||||
```
|
||||
2. Set the bundle location for runtime config injection:
|
||||
|
||||
```bash
|
||||
heroku config:set JS_RUNTIME_TARGET_BUNDLE=/app/react-ui/build/static/js/*.js
|
||||
```
|
||||
3. Now, build the app with this new setup:
|
||||
|
||||
```bash
|
||||
git commit --allow-empty -m 'Enable runtime config with create-react-app-inner-buildpack'
|
||||
git push heroku master
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "heroku-cra-node",
|
||||
"version": "1.0.0",
|
||||
"version": "3.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -15,7 +15,7 @@
|
||||
},
|
||||
"array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "http://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
|
||||
},
|
||||
"body-parser": {
|
||||
@@ -190,7 +190,7 @@
|
||||
},
|
||||
"media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g="
|
||||
},
|
||||
"merge-descriptors": {
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "heroku-cra-node",
|
||||
"version": "1.0.0",
|
||||
"version": "3.0.0",
|
||||
"description": "How to use create-react-app with a custom Node API on Heroku",
|
||||
"engines": {
|
||||
"node": "10.x"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server",
|
||||
"heroku-postbuild": "cd react-ui/ && npm install && npm install --only=dev --no-shrinkwrap && npm run build"
|
||||
"build": "cd react-ui/ && npm install && npm run build"
|
||||
},
|
||||
"cacheDirectories": [
|
||||
"node_modules",
|
||||
|
||||
5473
react-ui/package-lock.json
generated
5473
react-ui/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,9 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"react": "^16.6.3",
|
||||
"react-dom": "^16.7.0",
|
||||
"react-scripts": "^2.1.2"
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "^2.1.8"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
||||
108
react-ui/src/App.js
vendored
108
react-ui/src/App.js
vendored
@@ -1,18 +1,14 @@
|
||||
import React, { Component } from 'react';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
message: null,
|
||||
fetching: true
|
||||
};
|
||||
}
|
||||
function App() {
|
||||
const [message, setMessage] = useState(null);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [url, setUrl] = useState('/api');
|
||||
|
||||
componentDidMount() {
|
||||
fetch('/api')
|
||||
const fetchData = useCallback(() => {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error(`status ${response.status}`);
|
||||
@@ -20,54 +16,54 @@ class App extends Component {
|
||||
return response.json();
|
||||
})
|
||||
.then(json => {
|
||||
this.setState({
|
||||
message: json.message,
|
||||
fetching: false
|
||||
});
|
||||
setMessage(json.message);
|
||||
setIsFetching(false);
|
||||
}).catch(e => {
|
||||
this.setState({
|
||||
message: `API call failed: ${e}`,
|
||||
fetching: false
|
||||
});
|
||||
setMessage(`API call failed: ${e}`);
|
||||
setIsFetching(false);
|
||||
})
|
||||
}
|
||||
}, [url]);
|
||||
|
||||
useEffect(() => {
|
||||
setIsFetching(true);
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
{ process.env.NODE_ENV === 'production' ?
|
||||
<p>
|
||||
This is a production build from create-react-app.
|
||||
</p>
|
||||
: <p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
}
|
||||
<p>{'« '}<strong>
|
||||
{isFetching
|
||||
? 'Fetching message from API'
|
||||
: message}
|
||||
</strong>{' »'}</p>
|
||||
<p><a
|
||||
className="App-link"
|
||||
href="https://github.com/mars/heroku-cra-node"
|
||||
>
|
||||
React + Node deployment on Heroku
|
||||
</a></p>
|
||||
<p><a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a></p>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
{ process.env.NODE_ENV === 'production' ?
|
||||
<p>
|
||||
This is a production build from create-react-app.
|
||||
</p>
|
||||
: <p>
|
||||
Edit <code>src/App.js</code> and save to reload.
|
||||
</p>
|
||||
}
|
||||
<p>{'« '}<strong>
|
||||
{this.state.fetching
|
||||
? 'Fetching message from API'
|
||||
: this.state.message}
|
||||
</strong>{' »'}</p>
|
||||
<p><a
|
||||
className="App-link"
|
||||
href="https://github.com/mars/heroku-cra-node"
|
||||
>
|
||||
React + Node deployment on Heroku
|
||||
</a></p>
|
||||
<p><a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a></p>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
Reference in New Issue
Block a user