How to Connect and Integrate Supabase with Python: FastAPI, Flask, and Django
Supabase is a popular PostgreSQL-based BaaS (Backend-as-a-Service). Because it runs on a standard PostgreSQL database, you can connect to it using traditional database drivers (like psycopg2 or SQLAlchemy) or leverage its built-in REST API using client SDKs.
This guide provides a comprehensive walkthrough for integrating Supabase into your Python web applications, detailing implementations for FastAPI, Flask, and Django.
Initial Supabase Project Setup​
Before writing Python code, configure your Supabase database:
- Create a new project in the Supabase Dashboard.
- Go to Project Settings > API and copy your Project URL and API Key (anon public).
- Open the SQL Editor and create a sample table for this tutorial:
CREATE TABLE products (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name text NOT NULL,
price numeric NOT NULL
);
FastAPI Integration (Official SDK)​
FastAPI is an asynchronous framework, making it a perfect fit for the HTTP-based Supabase client library.
1. Installation​
Install the official Python client along with FastAPI and Uvicorn:
pip install fastapi "uvicorn[standard]" supabase pydantic
2. Implementation​
Create a main.py file to initialize the Supabase client and expose REST endpoints:
import os
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from supabase import create_client, Client
app = FastAPI()
# Fetch credentials from environment variables
SUPABASE_URL = os.environ.get("SUPABASE_URL", "https://your-project-ref.supabase.co")
SUPABASE_KEY = os.environ.get("SUPABASE_KEY", "your-anon-public-key")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
# Data validation schemas
class Product(BaseModel):
name: str
price: float
class ProductInDB(Product):
id: int
# CRUD Route Endpoints
@app.post("/products", response_model=ProductInDB)
def create_product(product: Product):
data = product.dict()
response = supabase.table("products").insert(data).execute()
if not response.data:
raise HTTPException(status_code=500, detail="Failed to create product")
return response.data[0]
@app.get("/products", response_model=list[ProductInDB])
def get_all_products():
response = supabase.table("products").select("*").execute()
return response.data
@app.get("/products/{product_id}", response_model=ProductInDB)
def get_product(product_id: int):
response = supabase.table("products").select("*").eq("id", product_id).execute()
if not response.data:
raise HTTPException(status_code=404, detail="Product not found")
return response.data[0]
@app.put("/products/{product_id}", response_model=ProductInDB)
def update_product(product_id: int, product: Product):
data = product.dict()
response = supabase.table("products").update(data).eq("id", product_id).execute()
if not response.data:
raise HTTPException(status_code=404, detail="Product not found")
return response.data[0]
@app.delete("/products/{product_id}")
def delete_product(product_id: int):
response = supabase.table("products").delete().eq("id", product_id).execute()
if not response.data:
raise HTTPException(status_code=404, detail="Product not found")
return {"message": f"Product {product_id} deleted"}
Run Uvicorn to start the local server:
export SUPABASE_URL="your-url"
export SUPABASE_KEY="your-key"
uvicorn main:app --reload
Flask Integration (Official SDK)​
Flask is synchronous by default, which integrates cleanly with the standard execution mode of the supabase-py client.
1. Installation​
pip install Flask supabase
2. Implementation​
Create an app.py file:
import os
from flask import Flask, jsonify, request
from supabase import create_client, Client
app = Flask(__name__)
SUPABASE_URL = os.environ.get("SUPABASE_URL")
SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
supabase: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
@app.route("/products", methods=["POST"])
def create_product():
data = request.get_json()
response = supabase.table("products").insert(data).execute()
return jsonify(response.data[0]), 201
@app.route("/products", methods=["GET"])
def get_products():
response = supabase.table("products").select("*").execute()
return jsonify(response.data)
@app.route("/products/<int:product_id>", methods=["GET", "PUT", "DELETE"])
def product_operations(product_id):
if request.method == "GET":
response = supabase.table("products").select("*").eq("id", product_id).execute()
if response.data:
return jsonify(response.data[0])
return jsonify({"error": "Product not found"}), 404
elif request.method == "PUT":
data = request.get_json()
response = supabase.table("products").update(data).eq("id", product_id).execute()
if response.data:
return jsonify(response.data[0])
return jsonify({"error": "Product not found"}), 404
elif request.method == "DELETE":
response = supabase.table("products").delete().eq("id", product_id).execute()
if response.data:
return jsonify({"message": f"Deleted {product_id}"})
return jsonify({"error": "Product not found"}), 404
if __name__ == "__main__":
app.run(debug=True)
Django Integration (Custom REST API Client)​
In Django applications where you want to query Supabase directly via its REST endpoints instead of configuring database settings in settings.py, you can use the requests library to query PostgREST.
1. Installation​
pip install Django requests
2. API Client Wrapper​
Create supabase_client.py inside your Django app:
import os
import requests
SUPABASE_URL = os.environ.get("SUPABASE_URL")
SUPABASE_KEY = os.environ.get("SUPABASE_KEY")
class SupabaseClient:
def __init__(self, table_name: str):
self.url = f"{SUPABASE_URL}/rest/v1/{table_name}"
self.headers = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}",
"Content-Type": "application/json"
}
def get_all(self):
response = requests.get(self.url, headers=self.headers)
response.raise_for_status()
return response.json()
def get_by_id(self, item_id: int):
response = requests.get(f"{self.url}?id=eq.{item_id}", headers=self.headers)
response.raise_for_status()
return response.json()
def create(self, data: dict):
response = requests.post(self.url, json=data, headers=self.headers)
response.raise_for_status()
return response.json()
def update(self, item_id: int, data: dict):
response = requests.patch(f"{self.url}?id=eq.{item_id}", json=data, headers=self.headers)
response.raise_for_status()
return response.json()
def delete(self, item_id: int):
response = requests.delete(f"{self.url}?id=eq.{item_id}", headers=self.headers)
response.raise_for_status()
return response.json()
3. Django Views​
Instantiate the wrapper inside your views:
import json
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from .supabase_client import SupabaseClient
product_client = SupabaseClient("products")
@require_http_methods(["GET", "POST"])
def product_list(request):
if request.method == "GET":
data = product_client.get_all()
return JsonResponse(data, safe=False)
elif request.method == "POST":
payload = json.loads(request.body)
new_item = product_client.create(payload)
return JsonResponse(new_item, status=201, safe=False)
Configure your URLs (urls.py) to map these views to your routing system as standard Django endpoints.
Sources​
- [1] Supabase Documentation: Connecting to Postgres from Python
- [2] PostgREST API Docs: PostgREST and Supabase API Endpoints
- [3] GitHub Repository: supabase-community/supabase-py
