.
├─ flask_api
│ ├─ app
│ ├─ logs
│ ├─ venv
│ ├─ .env
│ ├─ .gitignore
│ ├─ config.py
│ ├─ README.md
│ ├─ requirements.txt
│ └─ run.py
└─ react_ui
├─ build
├─ node_modules
├─ public
├─ src
├─ .gitignore
├─ package-lock.json
├─ package.json
└─ README.md
Cross-Origin Resource Sharing (CORS) is an HTTP-header based mechanism that allows a server to indicate any origins (domain, scheme, or port) other than its own from which a browser should permit loading resources.
When developing, the Flask backend will likely be running on one port (e.g. http://localhost:5000/) and the React backend will be running on another (e.g. http://localhost:3000/). Out-of-the-box, if you want to make an API call to the backend, you would have to use the full URL:
axios.get('http://localhost:5000/api/color', headers);
Anyone can make a get request without worrying about CORS. But, if the server is configured to send back some json data, we then need to consider the following:
- if the requester is from the same origin, we have no CORS issues
- if the requester is from a different origin (e.g. a different port), then the server needs to explicitly allow code from other origins to access the resource. This can be done with the
'Access-Control-Allow-Origin'
header using the*
wildcard.
# ...
response = jsonify(data)
response.headers['Access-Control-Allow-Origin'] = '*'
return response
As soon as we try to do a POST though, we will run into the same problem again. The Flask backend needs to identify if origins other than its own are allowed to Post. This is easiest done using flask-cors and is described in the next section.
But for this example, let's assume that in production we intend to serve the React frontend from the same domain and port as the Flask backend (e.g. using nginx).
In the meantime, we can configure a proxy in the package.json
file of the React project. This allows the app to "pretend" it's making requests from the same port as the backend, thereby avoiding all CORS issues.
"proxy": "http://127.0.0.1:5000",
This tells the React development server to proxy any unknown requests to your API server. Side note: for some reason you cannot use http://localhost:5000
. Once we add this proxy pointing to our backend, we can change all our API calls to just the endpoint path:
axios.get('/api/color', headers);
We also no longer need the headers for any responses on the server:
# ...
response = jsonify(data)
# We only need this header for public APIs.
# response.headers['Access-Control-Allow-Origin'] = '*'
return response
This proxy is only applicable to the React development server. When we deploy the frontend and backend together, we would configure nginx to serve the frontend, then act as a reverse-proxy for the backend API which will be running as a service. In other words, we don't need to change anything in our front or backend code when we deploy this.
See: https://flask-cors.readthedocs.io/en/latest/
pip install flask-cors
In the simplest case, you can initialize the Flask-Cors extension with default arguments. This will allow CORS for all domains on all routes.
# ...
from flask_cors import CORS
app = Flask(__name__)
# Allow all origins access to all resources:
CORS(app)
That't it. Our front end can now access resources and POST to our backend APIs using the full URL:
axios.post('http://localhost:5000/api/color', data, headers);
Rather than allowing all origins access to everything, you might want to specify which origins have access to what resources:
# Allow some origins access to some resources:
cors = CORS(app, resources={r'/api/*': {'origins': ['http://localhost:3000']}})
In lieu of initializing CORS()
, you can use decorators to allow access directly on the endpoints:
# ...
from flask_cors import cross_origin
@app.route('/api/demo', methods=['GET'])
@cross_origin(origins=['http://localhost:3000'])
def api_demo_one():
return 'example'
It is a very good idea to prefix API endpoints with /api so that:
- they do not get mixed with any possible routes used by the React side.
- they can be easily targeted with CORS rules as shown above
Flask-cors allows for total granular control beyond what is shown here, for example you can pass parameters saying which methods are allowed and more. See the API docs.