In this is the second part of the series, we will be looking at building the API using FastAPI and Supabase python client.
Prerequisites
- Have a database set up on Supabase(can match the database set up in the last tutorial)
- Comfortable with Python (including Virtual Environments)
- An Idea of what Environment Variables are
- JSON format
Setting up FastAPI
FastAPI is a modern, fast (high-performance), web framework for building APIs with Python 3.6+ based on standard Python type hints.
Installing python 3.7.5
To get started , make sure you have python > 3.7 as the latest
supabase
client uses that.
Download python 3.7.5 from here.
Install it with the Add to path flag checkbox enabled.
To list all the installed python on your system use the below command
> py -0
Installed Pythons found by C:\Windows\py.exe Launcher for Windows
-3.7-32 *
-3.6-32
Creating test FastAPI
Navigate to the folder where you want to create your project and run the following commands
mkdir supafast-api
cd .\supafast-api\
Create and activate a Virtual Environment with the latest installed python using the given commands
virtualenv venv-supaFastApi --python=python3.7
.\venv-supaFastApi\Scripts\activate
Create an api
folder in which the main login for the API will reside.. Name the file as main.py
Note: Do not name the file other than main.py as it is required for deployment.
mkdir api
cd .\api
cd. >main.py
pip install fastapi
pip install "uvicorn[standard]"
In the main.py
file, add the code below and run the API using uvicorn to test if everything works properly.
#main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def root():
return {"message": "Hello World Test"}
uvicorn main:app --reload
http://127.0.0.1:8000/
A response similar to this should appear on the screen at http://127.0.0.1:8000/
// 20211127160738
// http://127.0.0.1:8000/
{
"message": "Hello World Test"
}
TIP : use JSON Viewer Chrome Extension to view prettified JSON in the browser.
Now that the test FastAPI is working, let's get started and add the Supabase Python client to get data in the API.
Using Data from Supabase
pip install supabase
We need to use the SUPABASE_URL
as well as SUPABASE_KEY
which will be present in the settings of the Supabase project that we created in the first part of this series.
#main.py
from fastapi import FastAPI
from supabase import create_client, Client
app = FastAPI()
url = YOUR_SUPABASE_URL
key = YOUR_SUPABASE_KEY
supabase: Client = create_client(url, key)
Getting Data from Database
This was the structure of the database in the Supabase and for the API ,we are going to add two endpoints in FastAPI...
- To list down all the themes - /themes
- To list down the monsters of a particular theme - /monsters/?theme={theme_name}
#main.py
from fastapi import FastAPI
from supabase import create_client, Client
app = FastAPI()
url = YOUR_SUPABASE_URL
key = YOUR_SUPABASE_KEY
@app.get("/themes")
def themes():
themes = supabase.table('themes').select('*').execute()
return themes
@app.get("/monsters/")
def monsters(theme : str = "demo-theme-1"):
monsters = supabase.table('monsters').select('*').eq('monsterTheme',theme).execute()
return monsters
For the URL http://127.0.0.1:8000/themes
, the result would be
// 20211127163945
// http://127.0.0.1:8000/themes
{
"data": [
{
"id": 1,
"created_at": "2021-11-26T15:28:21+00:00",
"monsterTheme": "demo-theme-1"
},
{
"id": 2,
"created_at": "2021-11-26T15:29:45+00:00",
"monsterTheme": "demo-theme-2"
},
{
"id": 3,
"created_at": "2021-11-26T15:30:04+00:00",
"monsterTheme": "demo-theme-3"
},
{
"id": 4,
"created_at": "2021-11-26T15:30:12+00:00",
"monsterTheme": "demo-theme-4"
},
{
"id": 6,
"created_at": "2021-11-26T15:30:21+00:00",
"monsterTheme": "demo-theme-5"
}
],
"status_code": 200
}
For the "monsters" endpoint, we need a query parameter, but in case we don't send a query parameter, we will have a default value set so that a response is available at the endpoint.
In the main.py
above , we have a default set to demo-theme-1.. So in case the API call is to the endpoint http://127.0.0.1:8000/monsters/
we will get the result as follows
// 20211129131036
// http://127.0.0.1:8000/monsters/
{
"data": [
{
"id": 1,
"created_at": "2021-11-27T02:47:11+00:00",
"monsterName": "demo-monster-1",
"monsterQuote": "demo-quote-1",
"monsterTheme": "demo-theme-1",
"monsterLevel": 5
},
{
"id": 2,
"created_at": "2021-11-27T02:49:03+00:00",
"monsterName": "demo-monster-2",
"monsterQuote": "demo-quote-2",
"monsterTheme": "demo-theme-1",
"monsterLevel": 10
},
{
"id": 4,
"created_at": "2021-11-27T02:50:21+00:00",
"monsterName": "demo-monster-3",
"monsterQuote": "demo-quote-3",
"monsterTheme": "demo-theme-1",
"monsterLevel": 20
}
],
"status_code": 200
}
Getting Images from Storage
The Supabase Python Client also provides various methods to interact with the Supabase Storage and we are going to get the public URL for the stored images.
For that we need to have the exact location of the file in the Bucket as well as the correct name for the images.
From the last blog of the series, we had the above structure of files in the Bucket supafast-api
#main.py
from fastapi import FastAPI
from supabase import create_client, Client
app = FastAPI()
url = YOUR_SUPABASE_URL
key = YOUR_SUPABASE_KEY
supabase: Client = create_client(url, key)
@app.get("/themes")
def themes():
themes = supabase.table('themes').select('*').execute()
for theme in themes['data']:
#theme['monsterThemeBg'] = supabase.storage().StorageFileAPI(BUCKET_NAME).get_public_url(FILE_LOCATION)
theme['monsterThemeBg'] = supabase.storage().StorageFileAPI('supafast-api').get_public_url('themes/'+ theme['monsterTheme'].replace(' ','-') + '.png')
return themes
Here, the FILE_LOCATION is easier to construct because the way these files are stored. The response we receive after adding the monsterThemeBg
is like
// 20211129184109
// http://127.0.0.1:8000/themes
{
"data": [
{
"id": 1,
"created_at": "2021-11-26T15:28:21+00:00",
"monsterTheme": "demo-theme-1",
"monsterThemeBg": "https://jtpcokcjjqbizziufohl.supabase.co/storage/v1/object/public/supafast-api/themes/demo-theme-1.png"
},
{
"id": 2,
"created_at": "2021-11-26T15:29:45+00:00",
"monsterTheme": "demo-theme-2",
"monsterThemeBg": "https://jtpcokcjjqbizziufohl.supabase.co/storage/v1/object/public/supafast-api/themes/demo-theme-2.png"
},
...
],
"status_code": 200
}
The final main.py
file will look something like this..
#main.py
from fastapi import FastAPI
from supabase import create_client, Client
app = FastAPI()
url = YOUR_SUPABASE_URL
key = YOUR_SUPABASE_KEY
supabase: Client = create_client(url, key)
@app.get("/themes")
def themes():
themes = supabase.table('themes').select('*').execute()
print(themes['data'])
for theme in themes['data']:
theme['monsterThemeBg'] = supabase.storage().StorageFileAPI('supafast-api').get_public_url('themes/'+ theme['monsterTheme'] + '.png')
return themes
@app.get("/monsters/")
def monsters(theme : str = "demo-theme-1"):
monsters = supabase.table('monsters').select('*').eq('monsterTheme',theme).execute()
for monster in monsters['data']:
monster['monsterBg'] = supabase.storage().StorageFileAPI('supafast-api').get_public_url(monster['monsterTheme']+'/'+ monster['monsterName'].replace(' ','-') + '.png')
return monsters
Setting Up Environment Variables
As you can see in the main.py
, SENSITIVE properties like the url and key are directly defined in the file. Such sensitive data should never be in a file and to overcome it, we use environment variables.
I found this really good article on dev.to to use environment variables from a .env
file.
To do that,
//Stop the uvicorn server
cd. >.env
Now in the newly created .env
file, add your variables in the format
#.env
SUPABASE_SUPAFAST_URL=your-supabase-url
SUPABASE_SUPAFAST_KEY=you-supabase-key
#make sure you don't have spaces before/after the equal sign
In main.py
file,
import os
from fastapi import FastAPI
from supabase import create_client, Client
from dotenv import load_dotenv
load_dotenv()
app = FastAPI()
url = os.getenv('SUPABASE_SUPAFAST_URL')
key = os.getenv('SUPABASE_SUPAFAST_KEY')
supabase: Client = create_client(url, key)
'''
Rest of the code for the routes of the API
'''
Here, we additionally import the os
and the load_dotenv
packages, run the load_dotenv()
function which loads the KEY=VALUE pairs into the environment so that os.getenv('KEY')
can retrieve the value from the environment.
We didn't need to install a package for load_dotenv because the
python-dotenv
gets automatically installed when running pip install "uvicorn[standard] "
In case you use git(or some other VCS), make sure to put the .env
in the .gitignore
file, so that it is not visible to the general public.
Next Steps
Now that we have our API fully working on our local environment, in the next tutorial will deploy it on DETA and make it available publicly.
In case you have any doubts or suggestions regarding the article, feel free to contact me on my email aunicorndeveloper@gmail.com or on Twitter at @aUnicornDev.
The API built in this series is used in the tabsMonster project. Everyone is more than welcome to checkout and contribute in this Open Source Project.
Important Links
Below are the links for docs and products if you are more interesed.