Connect a Custom React App to Flask
Learn how to build a React app from scratch and connect it to a Python Flask API server. We will use Webpack to build the React bundle and proxy requests to the Flask server.
Table of Contents 📖
- Project Architecture
- Environment Variables
- React Project Initialization & Installing React
- Installing Webpack
- Installing Babel Loader and Presets
- Handling HTML
- Configuring Webpack
- Coding the React App
- npm Start Script
- Python Flask Project
- Running the App
Project Architecture
Environment Variables
ENV=development
WEBPACK_DEV_SERVER_PORT=1234
WEBPACK_DEV_SERVER_HOST=127.0.0.1
WEBPACK_DEV_SERVER_URL=http://127.0.0.1:1234
FLASK_PORT=1235
FLASK_HOST=127.0.0.1
FLASK_URL=http://127.0.0.1:1235
React Project Initialization & Installing React
To begin, lets initialize our project as an ES6 npm project.
npm init es6 -y
Now lets install React. React is just a library, so we can install it with npm.
npm i react
We will now need to install ReactDOM. ReactDOM is a package that acts as glue between React and the DOM.
npm i react-dom
Installing Webpack
Lets now install our module bundler Webpack as a development dependency. Webpack will transpile our React code to JavaScript that the browser understands.
npm i webpack -D
We also need to install the package webpack-cli (webpack command line interface).
npm i webpack-cli -D
This package provides a set of commands to increase speed/efficiency when setting up a custom webpack project. We also want to install the package webpack-dev-server.
npm i webpack-dev-server -D
This package gives us a web server to serve up our project and live reload it whenever changes are made.
Installing Babel Loader and Presets
To handle React code with Webpack, we need to install a loader called babel loader along with some other Babel dependencies.
npm i @babel/preset-env @babel/preset-react babel-loader -D
A loader is a function that Webpack passes code through to perform some sort of transformation. The Babel loader is a Webpack loader that transpiles JavaScript code. Babel presets are used to configure the Babel transipler.
- @babel/preset-env - Allows us to use the latest JavaScript.
- @babel/preset-react - Handles React code.
Handling HTML
Now lets create the HTML file to house our React app. We can do this using the html-webpack-plugin.
npm i html-webpack-plugin -D
A Webpack plugin interacts with the Webpack lifecycle. The html-webpack-plugin creates an HTML file to place our bundled JavaScript code into.
Configuring Webpack
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const dotenv = require('dotenv');
// Use dotenv to get the environment variables
dotenv.config({ path: path.resolve(__dirname, '../', '.env')});
const WEBPACK_DEV_SERVER_HOST = process.env.WEBPACK_DEV_SERVER_HOST;
const WEBPACK_DEV_SERVER_PORT = process.env.WEBPACK_DEV_SERVER_PORT;
const FLASK_URL = process.env.FLASK_URL;
module.exports = {
mode: 'development',
target: 'web',
entry: './src/index.jsx',
// Bundled code
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
})
],
// Configure the webpack development server
devServer: {
static: path.resolve(__dirname, 'dist'),
host: WEBPACK_DEV_SERVER_HOST,
port: WEBPACK_DEV_SERVER_PORT,
// Proxy api requests to the Flask server
proxy: [
{
context: ['/api'],
target: FLASK_URL
}
]
},
// Pass JavaScript files through the Babel transpiler
module: {
rules: [
{
test: /.(js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
['@babel/preset-react', {'runtime': 'automatic'}],
]
}
}
}
]
},
// Resolve .js and .jsx files
resolve: {
extensions: ['.js', '.jsx']
}
};
Coding the React App
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
import {createRoot} from 'react-dom/client';
import App from './components/App';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
import {useEffect, useState} from "react";
export default function App() {
const [users, setUsers] = useState([]);
const [loadedUsers, setLoadedUsers] = useState(false);
// Make an API call to the Flask server
useEffect(() => {
// Handle unmounting
const abortController = new AbortController();
const signal = abortController.signal;
fetch('/api/users', {signal})
.then(response => response.json())
.then(data => {
setUsers(data);
})
.catch(error => {
console.error('Error fetching users:', error);
})
.finally(() => {
setLoadedUsers(true);
});
}, []);
// Loop through the users and display them
return (
<div>
<h1>Registered Users:</h1>
{loadedUsers ? users.map(user => <h3 key={user.id}>{user.username}</h3>) : 'Loading...'}
</div>
);
}
npm Start Script
"start": "webpack-dev-server --open --config webpack.config.cjs"
Python Flask Project
python3 -m venv .venv
source .venv/bin/activate
pip install Flask python-dotenv py-mon
from flask import Flask, Response
import json
from dotenv import load_dotenv
import os
# Take the environment variables from .env file
load_dotenv()
FLASK_HOST = os.environ.get("FLASK_HOST")
FLASK_PORT = os.environ.get("FLASK_PORT")
# Create Flask app
app = Flask(__name__)
# API endpoint to serve up users
@app.route("/api/users")
def users():
users = [
{"username": "WittCode", "id": 1},
{"username": "Greg", "id": 2},
{"username": "Sabin", "id": 3},
{"username": "Mike", "id": 4},
{"username": "Spencer", "id": 5},
{"username": "Alex", "id": 6},
{"username": "Sam", "id": 7},
]
# Return a JSON response
return Response(json.dumps(users), mimetype="application/json")
# Run the application at the provided host and port
if __name__ == "__main__":
app.run(host=FLASK_HOST, port=FLASK_PORT)
Running the App
npm start
<i> [webpack-dev-server] [HPM] Proxy created: /api -> http://127.0.0.1:1235
<i> [webpack-dev-server] Project is running at:
<i> [webpack-dev-server] Loopback: http://127.0.0.1:1234/
python3 ./server.py
* Running on http://127.0.0.1:1235