Metadata-Version: 2.4
Name: firebase-client-sdk
Version: 0.1.0
Summary: Client-side Firebase SDK for frontend-oriented Python apps.
Author: Baptiste
License-Expression: MIT
Project-URL: Homepage, https://github.com/B4PT0R/firebase-client-sdk
Project-URL: Documentation, https://github.com/B4PT0R/firebase-client-sdk/tree/main/docs
Project-URL: Repository, https://github.com/B4PT0R/firebase-client-sdk
Project-URL: Issues, https://github.com/B4PT0R/firebase-client-sdk/issues
Project-URL: Changelog, https://github.com/B4PT0R/firebase-client-sdk/blob/main/CHANGELOG.md
Keywords: firebase,firestore,streamlit,gradio,reflex,sdk
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests
Requires-Dist: sseclient-py
Provides-Extra: dev
Requires-Dist: build; extra == "dev"
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-subtests; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# firebase-client-sdk

Client-side Firebase SDK for frontend-oriented Python apps.

Python import package: `firebase_client`  
Distribution name: `firebase-client-sdk`

This project is built for pure-Python app frontends such as Streamlit, Gradio, Reflex, and similar frameworks. It follows the Firebase client model: public Firebase config, user-driven auth flows, and security rules as the real protection boundary. It is not an admin SDK and it does not try to behave like one.

## What this gives you

- A Pythonic Firebase SDK shaped around the JavaScript client SDK mental model
- Auth, Firestore, Storage, Realtime Database, and callable Functions in one package
- A shared app container with named apps and per-product services
- Modular per-product helpers where they improve ergonomics
- A client-only design that stays honest about what Python can and cannot reproduce

## Why this exists

Firebase is often a strong fit for Python frontend-style apps:

- Authentication is already there
- Firestore and Realtime Database cover most data access needs
- Storage handles user files and media
- Functions can host light backend logic
- Security rules remain the core protection layer

But there is no serious official Python client SDK for this exact use case. This repository exists to close that gap with a real SDK shape, not a thin convenience wrapper.

## What this SDK is

- A client SDK for Python app frontends
- A Firebase JS-inspired API adapted to Python conventions
- A package for apps using user auth flows and public Firebase client config

## What this SDK is not

- Not `firebase-admin`
- Not a privileged backend SDK
- Not a service-account tool
- Not a way around Firebase security rules

## Installation

From PyPI:

```bash
pip install firebase-client-sdk
```

From the repository:

```bash
pip install -e .
```

Build distribution artifacts:

```bash
python -m build
```

Runtime dependencies:

- `requests`
- `sseclient-py`

## Quick start

```python
from firebase_client import get_app, get_apps, initialize_app

app = initialize_app(
    {
        "apiKey": "...",
        "projectId": "...",
        "storageBucket": "...",
        "databaseURL": "https://your-project-default-rtdb.firebaseio.com",
    }
)

secondary = initialize_app(
    {
        "apiKey": "...",
        "projectId": "...",
        "storageBucket": "...",
        "databaseURL": "https://your-other-project-default-rtdb.firebaseio.com",
    },
    name="secondary",
)

auth = app.auth
firestore = app.firestore
storage = app.storage
database = app.database
functions = app.functions

default_app = get_app()
all_apps = get_apps()
```

The package root stays intentionally small. Detailed APIs live in product subpackages:

- `firebase_client.app`
- `firebase_client.auth`
- `firebase_client.firestore`
- `firebase_client.storage`
- `firebase_client.database`
- `firebase_client.functions`

## Modular API style

Use the root package for app initialization:

```python
from firebase_client import initialize_app

app = initialize_app(config)
```

Use per-product modules when you want a Firebase-JS-like style:

```python
from firebase_client.firestore import collection, doc, get_doc

users = collection(app.firestore, "users")
alice_ref = doc(users, "alice")
alice = get_doc(alice_ref)
```

That keeps the common entry point simple while avoiding name collisions around generic helpers like `ref`, `get`, or `list`.

## Product overview

### Auth

- Email/password sign-in
- Anonymous sign-in and custom-token sign-in
- Email action flows, including password reset and email-link sign-in
- OAuth-style sign-in and linking
- `AuthCredential`, `EmailAuthProvider`, and `OAuthProvider`
- `auth.current_user`, `User`, and `UserCredential`
- Auth state and ID-token listeners
- Emulator support

Auth refresh behavior:

- Local auth state inspection is cheap and non-networked
- `auth.refresh_if_needed()` remains available as an explicit convenience helper
- Authenticated requests refresh automatically when the current session is expired but refreshable
- Restoring an expired persisted session stays local until an authenticated request actually needs a fresh token

Auth persistence note:

- Public auth usage is centered on `auth.current_user`, `User`, and `UserCredential`
- Session persistence is intentionally explicit through `auth.to_session_dict()` and `auth.from_session_dict(...)`
- Serialized session dictionaries use Firebase API-style keys such as `idToken`, `refreshToken`, `localId`, `displayName`, and `photoUrl`

Security note:

- Session payloads contain client auth tokens
- Do not log them, display them, or store them somewhere publicly readable

### Firestore

- Document and collection references
- Immutable document and query snapshots
- Query helpers including `start_after`, `end_before`, `offset`, and `limit_to_last`
- Write sentinels such as server timestamp, increment, array union, array remove, and delete field
- Polling-backed `on_snapshot(...)` listeners for documents, collections, and queries
- Emulator support

Firestore realtime note:

- This SDK is REST-based
- `on_snapshot(...)` exists, but it is polling-backed rather than a native Firestore watch transport
- If you need true event streaming semantics, Realtime Database is the better fit in this SDK

### Storage

- Bucket-relative references
- References from `gs://...` and Firebase Storage URLs
- Bytes, string, and file uploads
- `upload_string(...)` with `raw`, `base64`, `base64url`, and `data_url`
- Resumable uploads with a Python-friendly `UploadTask`
- Polling-backed `watch_folder(...)` for lightweight local/remote drive-style sync
- Metadata reads and updates
- Download URLs
- Emulator support
- Alternate bucket access through `app.get_storage(...)`
- Optional user-scoped convenience layer through `storage.user_storage`

### Realtime Database

- References and CRUD
- `Query`-style constraints including `start_after` and `end_before`
- `DataSnapshot`
- SSE-backed listeners including `on_value` and child listeners
- Emulator support

### Functions

- Callable functions
- Callable references
- Alternate Functions instances via `app.get_functions(...)`
- Timeout options aligned with JS-style callable options
- Emulator support

## Security model

This package follows the Firebase client model:

- Public Firebase config is expected
- User credentials and client tokens are expected
- Security rules are the real protection boundary

If you need privileged access, use a real backend or `firebase-admin`. This SDK is intentionally not designed for backend authority.

## Framework examples

### Streamlit

```python
import streamlit as st

from firebase_client import initialize_app
from firebase_client.auth import on_auth_state_changed
from firebase_client.firestore import collection, get_docs

app = initialize_app(st.secrets["firebase"])

if "auth_bound" not in st.session_state:
    def _sync_user(user):
        st.session_state["firebase_user"] = user

    st.session_state["firebase_auth_unsubscribe"] = on_auth_state_changed(app.auth, _sync_user)
    st.session_state["auth_bound"] = True

email = st.text_input("Email")
password = st.text_input("Password", type="password")

if st.button("Sign in"):
    app.auth.sign_in_with_email_and_password(email, password)
    st.rerun()

user = st.session_state.get("firebase_user")

if user:
    st.write(f"Signed in as {user.email}")
    todos = get_docs(collection(app.firestore, "todos"))
    for snapshot in todos:
        st.write(snapshot.data())
```

### Gradio

```python
import gradio as gr

from firebase_client import initialize_app
from firebase_client.firestore import collection, get_docs

app = initialize_app(
    {
        "apiKey": "...",
        "projectId": "...",
        "storageBucket": "...",
    }
)


def sign_in_and_list_notes(email: str, password: str):
    app.auth.sign_in_with_email_and_password(email, password)
    snapshots = get_docs(collection(app.firestore, "notes"))
    return {
        "user": app.auth.current_user.email,
        "notes": [snapshot.data() for snapshot in snapshots],
    }


demo = gr.Interface(
    fn=sign_in_and_list_notes,
    inputs=["text", "text"],
    outputs="json",
)

demo.launch()
```

### Reflex

```python
import reflex as rx

from firebase_client import initialize_app
from firebase_client.firestore import collection, get_docs

firebase_app = initialize_app(
    {
        "apiKey": "...",
        "projectId": "...",
        "storageBucket": "...",
    }
)


class AppState(rx.State):
    user_email: str = ""
    items: list[dict] = []

    def sign_in(self, email: str, password: str):
        credential = firebase_app.auth.sign_in_with_email_and_password(email, password)
        self.user_email = credential.user.email or ""

    def load_items(self):
        snapshots = get_docs(collection(firebase_app.firestore, "items"))
        self.items = [snapshot.data() or {} for snapshot in snapshots]
```

## Documentation

This README stays high-level by design.

Detailed reference docs live in [`docs/`](./docs/README.md):

- [App](./docs/app.md)
- [Auth](./docs/auth.md)
- [Firestore](./docs/firestore.md)
- [Storage](./docs/storage.md)
- [Realtime Database](./docs/database.md)
- [Functions](./docs/functions.md)
- [Exceptions](./docs/exceptions.md)
- [Testing](./docs/testing.md)

Project planning and current priorities:

- [`TODO.md`](./TODO.md)
- [`AGENTS.md`](./AGENTS.md)

## Known limitations

- Firestore `on_snapshot(...)` is polling-backed, not a native watch transport
- Functions support is intentionally focused on callable functions
- Some less-used surfaces are still converging toward their final stable form
- This package is designed for client-authorized flows only

## Running tests

```bash
pytest
```

Run the emulator-backed integration suite:

```bash
./scripts/run_emulator_tests.sh
```

The emulator suite expects a supported `firebase-tools` runtime locally, which in practice means Node `18`, `20`, or `22`.
The local Firebase CLI project used by these tests lives under [`emulator/`](./emulator/), so `node_modules` and rules/config files stay out of the repository root.
The repository ships a [`.nvmrc`](./.nvmrc) pinned to Node `22` for the recommended local setup.

## Status

The SDK is in active stabilization. The goal is to converge toward an API that feels like the Firebase client SDK should feel in Python, then lock that shape down with strong docs, tests, and release discipline.
