Skip to main content

supabase-py-lite

In-memory Supabase client for testing. Zero infrastructure, drop-in replacement for supabase-py.

Like Qdrant's client(":memory:"), but for Supabase.

from supabase_py_lite import create_client

supabase_client = create_client(":memory:")

supabase_client.table("users").insert({"id": 1, "name": "Alice"}).execute()

result = supabase_client.table("users").select("*").eq("name", "Alice").single().execute()
print(result.data)  # {"id": 1, "name": "Alice"}

Why?

  • No Docker — no supabase start, no containers, no waiting
  • No network — everything runs in-process with SQLite
  • No config — one line to get a working client
  • Same API — mirrors supabase-py's query builder interface
  • Auto-creates tables — just insert data, tables and columns appear automatically

Install

pip install supabase-py-lite

Usage

from supabase_py_lite import create_client

# In-memory (for testing)
supabase_client = create_client(":memory:")

# File-based (for persistence)
supabase_client = create_client("./my_app.db")

Insert

supabase_client.table("users").insert({"id": 1, "name": "Alice", "age": 30}).execute()

# Batch insert
supabase_client.table("users").insert([
    {"id": 2, "name": "Bob", "age": 25},
    {"id": 3, "name": "Charlie", "age": 35},
]).execute()

Select

# All rows
res = supabase_client.table("users").select("*").execute()

# Specific columns
res = supabase_client.table("users").select("id, name").execute()

# With count
res = supabase_client.table("users").select("*", count="exact").execute()
print(res.count)  # 3

# Single row
res = supabase_client.table("users").select("*").eq("id", 1).single().execute()
print(res.data)  # {"id": 1, "name": "Alice", "age": 30}

# Maybe single (returns None instead of raising)
res = supabase_client.table("users").select("*").eq("id", 999).maybe_single().execute()
print(res.data)  # None

Filters

.eq("col", value)        # col = value
.neq("col", value)       # col != value
.gt("col", value)        # col > value
.gte("col", value)       # col >= value
.lt("col", value)        # col < value
.lte("col", value)       # col <= value
.in_("col", [a, b])      # col IN (a, b)
.like("col", "A%")       # col LIKE 'A%'
.ilike("col", "a%")      # case-insensitive LIKE
.is_("col", None)        # col IS NULL
.contains("col", val)    # JSON array contains val

# Chain them
res = (
    supabase_client.table("users")
    .select("*")
    .gte("age", 25)
    .lte("age", 35)
    .order("age", ascending=False)
    .limit(10)
    .execute()
)

Update

supabase_client.table("users").update({"name": "Alicia"}).eq("id", 1).execute()

Delete

supabase_client.table("users").delete().eq("id", 1).execute()

Upsert

supabase_client.table("users").upsert({"id": 1, "name": "Alice v2"}).execute()

# Custom conflict column
supabase_client.table("users").upsert(
    {"email": "a@b.com", "name": "A"}, on_conflict="email"
).execute()

JSON columns

supabase_client.table("posts").insert({
    "id": 1,
    "meta": {"tags": ["python", "supabase"], "draft": False}
}).execute()

res = supabase_client.table("posts").select("*").eq("id", 1).execute()
print(res.data[0]["meta"]["tags"])  # ["python", "supabase"]

Foreign Keys

Register FK relationships with define_foreign_key, then use embedded resource syntax in .select() to join related data — exactly like Supabase.

# posts.user_id -> users.id
supabase_client.define_foreign_key("posts", "user_id", "users")

# comments.post_id -> posts.id
supabase_client.define_foreign_key("comments", "post_id", "posts")

Many-to-one (FK on current table → embeds as a single object):

res = supabase_client.table("posts").select("id, title, users(name, email)").execute()
# [{"id": 1, "title": "Hello", "users": {"name": "Alice", "email": "alice@example.com"}}, ...]

One-to-many (FK on related table → embeds as a list):

res = supabase_client.table("posts").select("id, title, comments(body)").execute()
# [{"id": 1, "title": "Hello", "comments": [{"body": "Great post!"}, ...]}, ...]

Wildcard on main table:

res = supabase_client.table("posts").select("*, users(name)").execute()

Alias — rename the embedded key in the result:

res = supabase_client.table("posts").select("id, author:users(name)").execute()
# [{"id": 1, "author": {"name": "Alice"}}, ...]

Hint — explicitly specify which FK column to use (useful when multiple FKs point to the same table):

res = supabase_client.table("posts").select("id, users!user_id(name)").execute()

Multiple embeds in one query:

res = supabase_client.table("posts").select("id, users(name), comments(body)").execute()

The FK join column is added to the query automatically if needed and stripped from the result unless you selected it explicitly.

Use in tests

import pytest
from supabase_py_lite import create_client

@pytest.fixture
def db():
    supabase_client = create_client(":memory:")
    yield supabase_client
    supabase_client.close()

def test_create_user(db):
    db.table("users").insert({"id": 1, "name": "Alice"}).execute()
    res = db.table("users").select("*").eq("id", 1).single().execute()
    assert res.data["name"] == "Alice"

Supported API

Method Status
.from_() / .table() ✅
.select() ✅
.insert() ✅
.update() ✅
.delete() ✅
.upsert() ✅
.eq/.neq/.gt/.gte/.lt/.lte ✅
.in_/.like/.ilike/.is_ ✅
.contains ✅
.order/.limit/.offset/.range ✅
.single/.maybe_single ✅
count="exact" ✅
JSON columns ✅
Auto-create tables ✅
Foreign key joins ✅
.or_() ✅
.not_() ✅

Development

git clone https://github.com/acelikyurek/supabase-py-lite
cd supabase-py-lite
uv sync --group dev
uv run pytest -v

License

MIT

Metadata

Release files for supabase-py-lite 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for supabase-py-lite 1.0.0
File Size Uploaded
supabase_py_lite-1.0.0.tar.gz 73.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for supabase-py-lite 1.0.0
File Interpreter ABI Platform
supabase_py_lite-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 95.8 kB

Release files / supabase_py_lite-1.0.0.tar.gz

Download URL supabase_py_lite-1.0.0.tar.gz
Size 73.5 kB
Tags Source
SHA-256 checksum
How to use checksums
983f67277b8cb09de4391571cb45bfe530e25ea927cc594db68802f66f7066a4
BLAKE2b-256 checksum
How to use checksums
f33dc6309052859c7706147995f04dd01baf4f647494f516bf3ada4507b6e281
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release files / supabase_py_lite-1.0.0-py3-none-any.whl

Download URL supabase_py_lite-1.0.0-py3-none-any.whl
Size 22.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e2335b73e09337def7769c342bac453ec7967cd31f20b0d23c4b55297cf6b680
BLAKE2b-256 checksum
How to use checksums
af5bb7889dbb021f95a8c40faa445bfe73e4aa477e637bd69f805d5bb5ca387e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page