In my previous post, Creating an API with python: Part 6: HTTPS and Proxying, I added an nginx proxy to the FastAPI API in order to serve the API over HTTPS, forward requests from HTTP to HTTPS and serve the API over a more ‘standard’ URL. In this post, I will add CORS (Cross-Origin Resource Scripting) support to the API. This enables the API to be called from a browser where the website host is not the same as the API. This is useful for when you want to build a front-end for the API that is not hosted in the same location as the API.
Prerequisites
These prerequisites are assumed for this post:
- Creating an API with python: Part 1: GET Endpoints
- Creating an API with python: Part 2: MariaDB Database
- Creating an API with python: Part 3: POST Endpoints
- Creating an API with python: Part 4: DELETE Endpoints
- Creating an API with python: Part 5: Authentication
- Creating an API with python: Part 6: HTTPS and Proxying
Step 1: Add the Allowed Origins to the Config
Determine which origin hosts you want to allow requests from. The list should at least include localhost, so we’ll add that in this example.
-
Change to the code directory (
~/vboxshare/fastapi
should be replaced with the path to your FastAPI python code):$ cd ~/vboxshare/fastapi
- Open the
config.yaml
file and append your origins to it:origins: - "https://localhost" - "https://someotherdomain.com"
Step 2: Update main.py with CORS
Update main.py
with the CORS middleware.
- Open the
main.py
file and replace the code at the top, before the first endpoint (the line that starts# Get link by link_id
), with the following:from typing import Optional from datetime import timedelta, datetime from sqlalchemy.orm import Session from fastapi import FastAPI, HTTPException, Depends, status from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from fastapi.middleware.cors import CORSMiddleware from manager import manager, schemas, authentication, CONFIG from manager.database import get_db app = FastAPI() origins = [origin for origin in CONFIG['origins']] app.add_middleware( CORSMiddleware, allow_origins=origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
Note the addition of the import ofCORSMiddleware
, theCONFIG
object and theapp.add_middleware
line. If you want to restrict which methods/headers are allowed, change the entries forallow_methods
andallow_headers
from["*"]
to a list of allowed methods/headers, e.g.["GET","POST"]
or["Accept","Content-Language"]
. Setallow_credentials
toFalse
if you don’t want to support cookies in cross-origin requests. For more information on CORS, see: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS.
Step 3: Start FastAPI
- On your server, change to the code directory (
~/vboxshare/fastapi
should be replaced with the path to your FastAPI python code):$ cd ~/vboxshare/fastapi
-
Run the FastAPI server:
$ . ~/.venv-fastapi/bin/activate (.venv-fastapi) $ uvicorn --host 0.0.0.0 main:app --root-path /api --reload
Step 4: Test CORS
Test that CORS is working with curl requests to the OPTIONS endpoint, which will be used by browsers to make pre-flight requests to check if cross-origin requests are accepted before sending the main request.
-
On the server running the API, run the following curl command, replacing
<YOUR_IP>
with the server IP:$ curl --cacert "/etc/ssl/certs/rootCA.crt" -X OPTIONS -H "Origin: INVALID" -H "Access-Control-Request-Method: INVALID" https://<YOUR_IP>/api/
You should get the response:Disallowed CORS origin, method
This means that CORS is working and responding with the correct error, as the OriginINVALID
and theAccess-Control-Request-Method
INVALID
are not in your accepted origins or methods. -
Now run:
$ curl --cacert "/etc/ssl/certs/rootCA.crt" -X OPTIONS -H "Origin: https://localhost" -H "Access-Control-Request-Method: INVALID" https://<YOUR_IP>/api/
This time you should get the response:Disallowed CORS method
This time the Origin (https://localhost
) is in your allowed list (inconfig.yaml
) but theAccess-Control-Request-Method
INVALID
is not in your allowed methods, so this is the expected response. -
Finally, run:
$ curl --cacert "/etc/ssl/certs/rootCA.crt" -X OPTIONS -H "Origin: https://localhost" -H "Access-Control-Request-Method: GET" https://<YOUR_IP>/api/
This time you should get the response:OK
This time the Origin (https://localhost
) is in your allowed list (inconfig.yaml
) and theAccess-Control-Request-Method
GET
is in your allowed methods (as we entered"*"
, which means all valid methods are accepted). A browser making this pre-flight request to the API would now know it can proceed to make the main request.
Conclusion
You should now have a FastAPI API running with CORS, which handles which requests from a different origin (host) can be accepted.
If you want to find out how to add Multi-Account Support to the API, see my follow-on post, Creating an API with python: Part 8: Multiple Account Support.
Thanks for reading!
1 Response
[…] Creating an API with python: Part 7: CORS July 15, 2022 […]