SiddhantSadangi/st_supabase_connection

Repository files navigation

A Streamlit connection component to connect Streamlit to Supabase Storage, Database, and Auth.

import streamlit as st
from st_supabase_connection import SupabaseConnection

st_supabase_client = st.connection(
    name="supabase_connection",
    type=SupabaseConnection,
    url=<your-supabase-url>,
    key=<your-supabase-key>
)

# Example: List buckets
buckets = st_supabase_client.list_buckets()
st.write(buckets)
Open in Streamlit

Web capture_2-12-2023_124639_st-supabase-connection streamlit app

  • Cache functionality to cache returned results. Save time and money on your API requests
  • Same method names as the Supabase Python API. Minimum relearning required
  • Exposes more storage methods than currently supported by the Supabase Python API. For example, update(), create_signed_upload_url(), and upload_to_signed_url()
  • Less keystrokes required when integrating with your Streamlit app.
Examples with and without the connector
Without connectorWith connector
Download file to local system from Supabase storage
import mimetypes
import streamlit as st
from supabase import create_client

supabase_client = create_client(
    supabase_url="...", supabase_key="..."
)

bucket_id = st.text_input("Enter the bucket_id")
source_path = st.text_input("Enter source path")

file_name = source_path.split("/")[-1]

if st.button("Request download"):
    with open(file_name, "wb+") as f:
        response = supabase_client.storage.from_(
        bucket_id
        ).download(source_path)
        f.write(response)

    mime = mimetypes.guess_type(file_name)[0]
    data = open(file_name, "rb")

    st.download_button(
        "Download file", data=data, 
        file_name=file_name, mime=mime,
    )
import streamlit as st
from st_supabase_connection import SupabaseConnection

st_supabase_client = st.connection(
    name="supabase_connection", type=SupabaseConnection
)

bucket_id = st.text_input("Enter the bucket_id")
source_path = st.text_input("Enter source path")

if st.button("Request download"):
    file_name, mime, data = st_supabase_client.download(
        bucket_id, source_path,
    )

    st.download_button(
        "Download file", data=data, 
        file_name=file_name, mime=mime,
    )
Upload file from local system to Supabase storage
import streamlit as st
from supabase import create_client

supabase_client = create_client(
supabase_key="...", supabase_url="..."
)

bucket_id = st.text_input("Enter the bucket_id")
uploaded_file = st.file_uploader("Choose a file")
destination_path = st.text_input("Enter destination path")
overwrite = "true" if st.checkbox("Overwrite?") else "false"  

with open(uploaded_file.name, "wb") as f:
    f.write(uploaded_file.getbuffer())

if st.button("Upload"):
    with open(uploaded_file.name, "rb") as f:
        supabase_client.storage.from_(bucket_id).upload(
            path=destination_path,
            file=f,
            file_options={
            "content-type": uploaded_file.type,
            "x-upsert": overwrite,
            },
        )
import streamlit as st
from st_supabase_connection import SupabaseConnection

st_supabase_client = st.connection(
    name="supabase_connection", type=SupabaseConnection
)

bucket_id = st.text_input("Enter the bucket_id")
uploaded_file = st.file_uploader("Choose a file"):
destination_path = st.text_input("Enter destination path")
overwrite = "true" if st.checkbox("Overwrite?") else "false" 

if st.button("Upload"):
    st_supabase_client.upload(
        bucket_id, "local", uploaded_file, 
        destination_path, overwrite,
    )
  1. Install st-supabase-connection
pip install st-supabase-connection
  1. Set the SUPABASE_URL and SUPABASE_KEY Streamlit secrets as described here.

Note

For local development outside Streamlit, you can also set these as your environment variables (recommended), or pass these to the url and key args of st.connection().

  1. Import
from st_supabase_connection import SupabaseConnection, execute_query
  1. Initialize
st_supabase_client = st.connection(
    name="YOUR_CONNECTION_NAME",
    type=SupabaseConnection,
    ttl=None,
)
  1. Use in your app to query tables and files, and add authentication. Happy Streamlit-ing! 🎈
Storage
  • delete_bucket()
  • empty_bucket()
  • get_bucket()
  • list_buckets()
  • create_bucket()
  • upload()
  • download()
  • update_bucket()
  • move()
  • list_objects()
  • create_signed_urls()
  • get_public_url()
  • create_signed_upload_url()
  • upload_to_signed_url()
Database
  • execute_query() - Executes the passed query with caching enabled.
  • All methods supported by postgrest-py.
Auth
  • cached_sign_in_with_password() - Cached version of sign_in_with_password() for faster sign-in.
  • All methods supported by Supabase's Python API .
>>> st_supabase_client.list_buckets(ttl=None)
[
    SyncBucket(
        id="bucket1",
        name="bucket1",
        owner="",
        public=False,
        created_at=datetime.datetime(2023, 7, 31, 19, 56, 21, 518438, tzinfo=tzutc()),
        updated_at=datetime.datetime(2023, 7, 31, 19, 56, 21, 518438, tzinfo=tzutc()),
        file_size_limit=None,
        allowed_mime_types=None,
    ),
    SyncBucket(
        id="bucket2",
        name="bucket2",
        owner="",
        public=True,
        created_at=datetime.datetime(2023, 7, 31, 19, 56, 28, 203536, tzinfo=tzutc()),
        updated_at=datetime.datetime(2023, 7, 31, 19, 56, 28, 203536, tzinfo=tzutc()),
        file_size_limit=100,
        allowed_mime_types=["image/jpg", "image/png"],
    ),
]
>>> st_supabase_client.create_bucket("new_bucket")
{'name': 'new_bucket'}
>>> st_supabase_client.get_bucket("new_bucket")
SyncBucket(id='new_bucket', name='new_bucket', owner='', public=True, created_at=datetime.datetime(2023, 8, 2, 19, 41, 44, 810000, tzinfo=tzutc()), updated_at=datetime.datetime(2023, 8, 2, 19, 41, 44, 810000, tzinfo=tzutc()), file_size_limit=None, allowed_mime_types=None)
>>> st_supabase_client.update_bucket(
      "new_bucket",
      file_size_limit=100,
      allowed_mime_types=["image/jpg", "image/png"],
      public=True,
    )
{'message': 'Successfully updated'}
>>> st_supabase_client.move("new_bucket", "test.png", "folder1/new_test.png")
{'message': 'Successfully moved'}
>>> st_supabase_client.list_objects("new_bucket", path="folder1", ttl=0)
[
    {
        "name": "new_test.png",
        "id": "e506920e-2834-440e-85f1-1d5476927582",
        "updated_at": "2023-08-02T19:53:22.53986+00:00",
        "created_at": "2023-08-02T19:52:20.404391+00:00",
        "last_accessed_at": "2023-08-02T19:53:21.833+00:00",
        "metadata": {
            "eTag": '"814a0034f5549e957ee61360d87457e5"',
            "size": 473831,
            "mimetype": "image/png",
            "cacheControl": "max-age=3600",
            "lastModified": "2023-08-02T19:53:23.000Z",
            "contentLength": 473831,
            "httpStatusCode": 200,
        },
    }
]
>>> st_supabase_client.empty_bucket("new_bucket")
{'message': 'Successfully emptied'}
>>> st_supabase_client.delete_bucket("new_bucket")
{'message': 'Successfully deleted'}
>>> execute_query(st_supabase_client.table("countries").select("*"), ttl=0)
APIResponse(
    data=[
        {"id": 1, "name": "Afghanistan"},
        {"id": 2, "name": "Albania"},
        {"id": 3, "name": "Algeria"},
    ],
    count=None,
)
>>> execute_query(
        st_supabase_client.table("users").select("name, teams(name)", count="exact"), 
        ttl="1h",
    )
    
APIResponse(
    data=[
        {"name": "Kiran", "teams": [{"name": "Green"}, {"name": "Blue"}]},
        {"name": "Evan", "teams": [{"name": "Blue"}]},
    ],
    count=2,
)
>>> execute_query(
        st_supabase_client.table("cities").select("name, countries(*)", count="exact").eq("countries.name", "Curaçao"),
        ttl=None,
    )

APIResponse(
    data=[
        {
            "name": "Kralendijk",
            "countries": {
                "id": 2,
                "name": "Curaçao",
                "iso2": "CW",
                "iso3": "CUW",
                "local_name": None,
                "continent": None,
            },
        },
        {"name": "Willemstad", "countries": None},
    ],
    count=2,
)
>>> execute_query(
        st_supabase_client.table("countries").insert(
            [{"name": "Wakanda", "iso2": "WK"}, {"name": "Wadiya", "iso2": "WD"}], count="None"
        ),
        ttl=0,
    )
    
APIResponse(
    data=[
        {
            "id": 250,
            "name": "Wakanda",
            "iso2": "WK",
            "iso3": None,
            "local_name": None,
            "continent": None,
        },
        {
            "id": 251,
            "name": "Wadiya",
            "iso2": "WD",
            "iso3": None,
            "local_name": None,
            "continent": None,
        },
    ],
    count=None,
)

Note

If the call is valid, all Supabase Auth methods return the same response structure:

{
  "user": {
    "id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
    "app_metadata": {
      "provider": "email",
      "providers": [
        "email"
      ]
    },
    "user_metadata": {
      "attribution": "I made it :)",
      "fname": "Siddhant"
    },
    "aud": "authenticated",
    "confirmation_sent_at": null,
    "recovery_sent_at": null,
    "email_change_sent_at": null,
    "new_email": null,
    "invited_at": null,
    "action_link": null,
    "email": "[email protected]",
    "phone": "",
    "created_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 365359, tzinfo=datetime.timezone.utc)",
    "confirmed_at": null,
    "email_confirmed_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 373966, tzinfo=datetime.timezone.utc)",
    "phone_confirmed_at": null,
    "last_sign_in_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 377070, tzinfo=datetime.timezone.utc)",
    "role": "authenticated",
    "updated_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 381584, tzinfo=datetime.timezone.utc)",
    "identities": [
      {
        "id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
        "user_id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
        "identity_data": {
          "email": "[email protected]",
          "sub": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544"
        },
        "provider": "email",
        "created_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370040, tzinfo=datetime.timezone.utc)",
        "last_sign_in_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370002, tzinfo=datetime.timezone.utc)",
        "updated_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370040, tzinfo=datetime.timezone.utc)"
      }
    ],
    "factors": null
  },
  "session": {
    "provider_token": null,
    "provider_refresh_token": null,
    "access_token": "***",
    "refresh_token": "***",
    "expires_in": 3600,
    "expires_at": 1696800390,
    "token_type": "bearer",
    "user": {
      "id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
      "app_metadata": {
        "provider": "email",
        "providers": [
          "email"
        ]
      },
      "user_metadata": {
        "attribution": "I made it :)",
        "fname": "Siddhant"
      },
      "aud": "authenticated",
      "confirmation_sent_at": null,
      "recovery_sent_at": null,
      "email_change_sent_at": null,
      "new_email": null,
      "invited_at": null,
      "action_link": null,
      "email": "[email protected]",
      "phone": "",
      "created_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 365359, tzinfo=datetime.timezone.utc)",
      "confirmed_at": null,
      "email_confirmed_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 373966, tzinfo=datetime.timezone.utc)",
      "phone_confirmed_at": null,
      "last_sign_in_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 377070, tzinfo=datetime.timezone.utc)",
      "role": "authenticated",
      "updated_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 381584, tzinfo=datetime.timezone.utc)",
      "identities": [
        {
          "id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
          "user_id": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544",
          "identity_data": {
            "email": "[email protected]",
            "sub": "e1f550fd-9cd1-44e4-bbe4-c04e91cf5544"
          },
          "provider": "email",
          "created_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370040, tzinfo=datetime.timezone.utc)",
          "last_sign_in_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370002, tzinfo=datetime.timezone.utc)",
          "updated_at": "datetime.datetime(2023, 10, 8, 20, 26, 30, 370040, tzinfo=datetime.timezone.utc)"
        }
      ],
      "factors": null
    }
  }
}
st_supabase_client.auth.sign_up(
    dict(
        email='[email protected]',
        password='***',
        options=dict(
            data=dict(
                fname='Siddhant',
                attribution='I made it :)',
            )
        )
    )
)

SupabaseConnection() offers a cached version of sign_in_with_password() for faster, request-free sign-ins.

st_supabase_client.cached_sign_in_with_password(dict(email='[email protected]', password='***'))
st_supabase_client.auth.get_session()
st_supabase_client.auth.get_user()
st_supabase_client.auth.sign_out()

Note

Check the Supabase Python API reference for more examples.

Open in Streamlit

This connector builds upon the awesome work done by the open-source community in general and the Supabase Community in particular. I cannot be more thankful to all the authors whose work I have used either directly or indirectly.

Thanks to all contributors to this project 🙇

Contributors

Buy Me A Coffee
Sponsor me on GitHub

About

An easy-to-use Supabase connector for Streamlit that caches your API calls to make querying fast and cheap.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Sponsor this project

 

Contributors 3

  •  
  •  
  •