Skip to main content

In-memory Supabase client for testing. Drop-in replacement for supabase-py backed by SQLite.

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

supabase_py_lite-1.0.0.tar.gz (73.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

supabase_py_lite-1.0.0-py3-none-any.whl (22.2 kB view details)

Uploaded Python 3

File details

Details for the file supabase_py_lite-1.0.0.tar.gz.

File metadata

  • Download URL: supabase_py_lite-1.0.0.tar.gz
  • Upload date:
  • Size: 73.5 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for supabase_py_lite-1.0.0.tar.gz
Algorithm Hash digest
SHA256 983f67277b8cb09de4391571cb45bfe530e25ea927cc594db68802f66f7066a4
MD5 eb8bc76c1a11109d2eded457ed3b3637
BLAKE2b-256 f33dc6309052859c7706147995f04dd01baf4f647494f516bf3ada4507b6e281

See more details on using hashes here.

File details

Details for the file supabase_py_lite-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: supabase_py_lite-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 22.2 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for supabase_py_lite-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e2335b73e09337def7769c342bac453ec7967cd31f20b0d23c4b55297cf6b680
MD5 bf94bd55dafe7540fdd44f3faaff0396
BLAKE2b-256 af5bb7889dbb021f95a8c40faa445bfe73e4aa477e637bd69f805d5bb5ca387e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page