New component: Streamlit Supabase Connector 🎉

:electric_plug: Streamlit Supabase Connector

Downloads

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

:star: Star the repo on GitHub:
GitHub

:eyes: Explore the connector in the demo app (no Supabase account needed):
Open in Streamlit

:thinking: Why use this?

  • 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.experimental_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 if exists?") 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.experimental_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 if exists?") else "false" 
    
    if st.button("Upload"):
        st_supabase_client.upload(
          bucket_id, "local", uploaded_file, destination_path, overwrite,
          )
    

:hammer_and_wrench: Setup

  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.experimental_connection() (not recommended).

:magic_wand: Usage

  1. Import
from st_supabase_connection import SupabaseConnection
  1. Initialize
st_supabase_client = st.experimental_connection(
    name="YOUR_CONNECTION_NAME",
    type=SupabaseConnection,
    ttl=None,
    url="YOUR_SUPABASE_URL", # not needed if provided as a streamlit secret
    key="YOUR_SUPABASE_KEY", # not needed if provided as a streamlit secret
)
  1. Use in your app to query tables and files. Happy Streamlit-ing! :balloon:

:ok_hand: Supported methods

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
  • query() - Runs a cached SELECT query
  • All methods supported by postgrest-py.

:books: Examples

:package: Storage operations

List existing buckets

>>> st_supabase.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"],
    ),
]

Create a bucket

>>> st_supabase_client.create_bucket("new_bucket")
{'name': 'new_bucket'}

Get bucket details

>>> st_supabase.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)

Update a bucket

>>> st_supabase_client.update_bucket(
      "new_bucket",
      file_size_limit=100,
      allowed_mime_types=["image/jpg", "image/png"],
      public=True,
    )
{'message': 'Successfully updated'}

Move files in a bucket

>>> st_supabase_client.move("new_bucket", "test.png", "folder1/new_test.png")
{'message': 'Successfully moved'}

List objects in a bucket

>>> 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,
        },
    }
]

Empty a bucket

>>> st_supabase.empty_bucket("new_bucket")
{'message': 'Successfully emptied'}

Delete a bucket

>>> st_supabase_client.delete_bucket("new_bucket")
{'message': 'Successfully deleted'}

:file_cabinet: Database operations

Simple query

>>> st_supabase.query("*", table="countries", ttl=0).execute()
APIResponse(
    data=[
        {"id": 1, "name": "Afghanistan"},
        {"id": 2, "name": "Albania"},
        {"id": 3, "name": "Algeria"},
    ],
    count=None,
)

Query with join

>>> st_supabase.query("name, teams(name)", table="users",  count="exact", ttl="1h").execute()
APIResponse(
    data=[
        {"name": "Kiran", "teams": [{"name": "Green"}, {"name": "Blue"}]},
        {"name": "Evan", "teams": [{"name": "Blue"}]},
    ],
    count=None,
)

Filter through foreign tables

>>> st_supabase.query("name, countries(*)", count="exact", table="cities", ttl=None).eq(
        "countries.name", "Curaçao"
    ).execute()

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,
)

Insert rows

>>> st_supabase_client.table("countries").insert(
        [{"name": "Wakanda", "iso2": "WK"}, {"name": "Wadiya", "iso2": "WD"}], count="None"
    ).execute()
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]
Check the Supabase Python API reference for more examples.

:star: Explore all options in a demo app

Open in Streamlit

:bow: Acknowledgements

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.

:hugs: Want to support my work?

Buy Me A Coffee

5 Likes

Woah! This is an amazing component, and exactly what I needed for a personal project.

Thanks for sharing with the community! :hugs:

Charly

1 Like

Glad you find it useful :slight_smile:

Lemme know your thoughts after using it. I plan on adding more supabase products to this connector

That’s fantastic! The app to see how to use the API is so handy!

2 Likes

Glad you liked it :slight_smile:

1 Like

Love this! Exactly what I need! :partying_face:

1 Like

Glad to be of use :slight_smile:

Would also love if you could share the apps you build using this :smiley:

I’ve successfully used your components to link the database to my app and it works, but I’ve run into a problem. I have no idea how to upload a wav file that has been recorded in the app to Supabase bucket. It seems that this is not feasible. May I have some pointers?

Ah, I hadn’t though of that use-case. Currently all transfers happen between your local file system and Supabase, not Streamlit’s file system.
However, this would be easy to add. Could you please create a feature request in the connection repo here?
Issues · SiddhantSadangi/st_supabase_connection (github.com)

1 Like

NVM, I created a feature request: FEATURE REQUEST: Upload from and download to Streamlit hosted filesystem. · Issue #1 · SiddhantSadangi/st_supabase_connection (github.com)

You can follow it on GH to be notified when it is released.

1 Like

Thank you! That’s very kind of you :smiling_face_with_three_hearts:

Hey,
I just released v1.0.0 of the connector. :tada:

This now supports file transfer between Supabase storage and both your local filesystem and Streamlit’s hosted filesystem. :white_check_mark:

Could you update your version of st_supabase_connector and let me know if this works for you?

:rocket: New release: st_supabase_connection :rocket:

I just released v1.1.0 of st_supabase_connection. This version adds support for overwriting existing files when using the upload() method, along with readability updates to the code and README.

:star: Star the repo on GitHub: https://github.com/SiddhantSadangi/st_supabase_connection

:eyes: Explore the latest version in the demo app: https://st-supabase-connection.streamlit.app/ (no Supabase account needed)

:soon: Coming soon, support for Supabase Auth :rocket:

:rocket: New release: st_supabase_connection v1.2.0 :rocket:

This version allows you to easily connect your Streamlit apps to Supabase Auth, a powerful and secure authentication service for your web projects. :closed_lock_with_key:

You can also check out the demo app, which has a new and improved look and feel. :dizzy:

GitHub: GitHub - SiddhantSadangi/st_supabase_connection: An easy-to-use Supabase connector for Streamlit that caches your API calls to make querying fast and cheap.
PyPI: st-supabase-connection · PyPI
Tutorial: https://st-supabase-connection.streamlit.app/

1 Like

File “/home/saurabh/.local/lib/python3.8/site-packages/st_supabase_connection/init.py”, line 141, in SupabaseConnection
allowed_mime_types: Optional[list[str]] = None,
TypeError: ‘type’ object is not subscriptable

Raised issue on github Issues · SiddhantSadangi/st_supabase_connection · GitHub

Thanks for creating this issue @Saurabh_M :hugs:

This should hopefully be fixed in st_supabase_connection v1.2.2 :rocket:

Please let me know if it isn’t.

Thanks for building this Siddhant.
I’m trying to do access a custom schema using this lib.

I think it is possible with supabase-py as described here, but haven’t tried myself.

Is it possible to access a custom schema using st-supabase-conenction, if so where do I declare the schema name? Any starter code or help is much appreciated.

It would also be helpful to add this bit to the database section in the docs.

Linked GitHub issue

Hey @phreakyphoenix :wave:

You can use a wrapper to access custom schemas. However, as of now, you cannot pass options to create_client() using this wrapper. In order to update the schema on the postgrest client, you can use the code below:

st_supabase = st.connection(name="supabase_connection", type=SupabaseConnection) 
st_supabase.client.postgrest.schema("YOUR_SCHEMA_NAME")

Let me know if you have any other questions.