Quart + React Tutorial — Part 1

The async/await full-stack

This tutorial is a guide in building a single page app, SPA, in React with a Quart backend assuming you have a basic understanding of Python and JavaScript. The app itself will be a simple TODO list, however it will utilize the latest async/await syntax in both JavaScript and Python.

You can see the code directly in this repository, with each section in this article corresponding to a commit.

1. Setting up

As with all projects we need the tooling in place first to proceed, and for this project that means Python and NodeJS. You’ll need to ensure that Python 3.7 (or greater) and NodeJS 10 (or greater) are installed. The installation of these will depend on your operating system, so please search for instructions.

In addition to the language tooling, we also need a package managers for Python and JavaScript. I recommend Poerty and Yarn respectively, with installation instructions available here and here.

2. Frontend — Tool chain

The create-react-app tool is the easiest way to create and install all the tooling for a SPA. It should be installed via yarn, yarn global add create-react-app (the global is optional, but I tend to use it in other projects). It can then be used in the project directory via create-react-app frontend . The command yarn run start will create a local development server and launch a browser for the app.

Your app at this stage.

Also note that the code can be tested with yarn run test .

3. Backend — Tool chain

The poetry tool is the easiest way to create and install tooling for the backend. It should be used in the project directory via poetry new --src backend . After you run this you should have

/backend

/frontend

in the project directory. We can now install the initial dependencies by running (in the backend directory) poetry install . You can initiate the tests via poetry run pytest tests/ .

4. Frontend — Display a simple TODO list

Now we have the basic tool chain we can build the actual functionality. Lets keep it very simple and just display a list of these TODO items ["a", "b", "c"] . This can be a simple functional component, /frontend/src/Items.js ,

const Items = function(props) {

const listItems = props.items.map(

(item, index) => (<li key={index}>{item}</li>)

);

return (

<ul>

{listItems}

</ul>

);

}

which can be displayed by a simple App component, /frontend/src/App.js ,

const App = function() {

return (

<div className="App">

<Items items={["a", "b", "c"]} />

</div>

);

}

5. Backend — Route to return the TODO items

Now we can display the items, lets fetch them from a server. To do this we need to create a simple Quart app with a route that returns the items. Firstly lets add the quart dependency poetry add quart then create a simple web-app in the file /backend/src/backend.py with the contents,

from quart import jsonify, Quart



app = Quart(__name__)



@app.route("/items/")

async def get_items():

return jsonify(["a", "b", "c"]) if __name__ == "__main__":

app.run()

which simply returns the same fixed list as earlier. The __name__ == "__main__" guard allows poetry run python src/backend.py to start the backend web-app.

6. Frontend — Fetching the list

First we need to proxy requests to the backend web-app when they are made in the browser, this is simply done by adding the backend server’s root URL as ”proxy”: “http://localhost:5000" to the /frontend/package.json file. This means that requests in the browser are proxied to to the backend server. (This avoids any CORS issues, if you’ve experienced them in the past).

Now we can instruct the App component to fetch the items when it is mounted using the async/await syntax throughout,

class App extends Component {

constructor(props) {

super(props);

this.state = {items: []};

}



async componentDidMount() {

const response = await fetch("/items/");

const items = await response.json();

this.setState({items: items});

}



render() {

return (

<div className="App">

<Items items={this.state.items} />

</div>

);

}

}

7. Frontend — Creating new items

To create new items we need to create a component that allows the item’s description to be typed in with a button to click to create it. This should a controlled component, in that it should control the input DOM and store the value typed in the state of the component (see the input onChange ),

class CreateItem extends Component {

constructor(props) {

super(props);

this.state = {item: ""};

}



async onSubmit(event) {

event.preventDefault();

await this.props.onCreate(this.state.item);

this.setState({item: ""});

}



render() {

return (

<form onSubmit={this.onSubmit.bind(this)}>

<label>Item:

<input value={this.state.item}

onChange={(event) => {

this.setState({item: event.target.value})

}}

/>

</label>

<button>Create!</button>

</form>

);

}

}

In addition this component takes a onCreate prop which allows the new item to be propagated back to the parent component, as so

class App extends Component {

... // Previous code async onCreate(item) {

this.setState(prevState => (

{items: [...prevState.items, item]}

));

await fetch(

"/items/", {

method: "POST",

headers: {

"Accept": "application/json",

"Content-Type": "application/json",

},

body: JSON.stringify(item),

}

);

}



render() {

return (

<div className="App">

<Items items={this.state.items} />

<CreateItem onCreate={this.onCreate.bind(this)} />

</div>

);

}

here the CreateItem component is given a callback onCreate which updates the items state as expected. In addition the onCreate method POSTs the item to the server.

8. Backend — Updating the items

To update the items the server needs a route that accepts the POST request and retrieves the sent data,

@app.route("/items/", methods=["POST"])

async def add_item():

item = await request.get_json()

items.append(item)

return jsonify({}) # Returns success

note that the retrieved item is appended to an items variable, this will serve as our storage. This is a global in memory variable, which is a poor choice outside of this article as it will be lost whenever the server restarts (Part 2 will address this), however it serves to show how to do this. The GET therefore also needs updating to use this variable, to this,

items = [] @app.route("/items/", methods=["GET"])

async def get_items():

return jsonify(items)

9. Frontend — Styling the list

The create-react-app tool adds a css loader to the build process, which means CSS can be directly imported into the JavaScript source code. This means we can write a css file, such as Items.css as below,

.Items {

padding: 0;

}



.Items > li {

list-style: none;

border: 1px solid grey;

border-radius: 4px;

padding: 8px;

}

and then directly import and use it in the Items.js code and component. After doing so the Items.js source looks is,

import React from 'react';

import './Items.css';



const Items = function(props) {

const listItems = props.items.map(

(item, index) => (<li key={index}>{item}</li>)

);

return (

<ul className="Items">

{listItems}

</ul>

);

}



export default Items;

note the import and the className usage.

10. Conclusion

This includes this introductory article to the async/await stack, once completed your app should look like this,

The app at the end, with one item on the TODO list.

as a reminder all the code discussed here is available in this repository.