Metadata-Version: 2.3
Name: nyxa-db
Version: 0.1.0
Summary: Database layer for Nyxa — SQLAlchemy models, sessions, and db CLI
Keywords: fastapi,sqlalchemy,database,nyxa,orm
Author: Al-Amin Islam Nerob
Author-email: Al-Amin Islam Nerob <alamin@aincoder.com>
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Dist: nyxa>=0.1.0
Requires-Dist: sqlalchemy>=2.0.0
Requires-Dist: alembic>=1.14.0
Requires-Python: >=3.11
Project-URL: Homepage, https://nyxadev.com
Project-URL: Repository, https://github.com/NyxaDev/nyxa
Description-Content-Type: text/markdown

# nyxa-db — batteries-included database layer for Nyxa

SQLAlchemy 2.0 models, sessions, Alembic migrations, and seeders for apps built
with [nyxa](https://pypi.org/project/nyxa/). Core `nyxa` does **not**
depend on this package.

## Recommended stack

| Layer | Choice | Notes |
| --- | --- | --- |
| App / CLI | `nyxa` | No SQLAlchemy required |
| Persistence | `nyxa-db` | Soft-loaded into `nyxa` CLI when installed |
| Default local DB | SQLite | `{project}/database/app.db` when `DATABASE_URL` is unset |
| Production DB | PostgreSQL | Set `DATABASE_URL=postgresql+psycopg://…` |
| Postgres extras | `nyxa-postgres` (optional) | URL helper, pool defaults soft-applied by `get_engine`, compose snippet |

Swap later by changing `DATABASE_URL` only — models, migrations, and `get_session`
stay the same. Install `nyxa-postgres` for tuned pool settings and `docker compose`
scaffolding (`init --database postgres`). Alternate adapters (e.g. SQLModel) are out
of scope for Phase 2; apps import from `nyxa_db` so a future adapter package can
replace the binding without rewriting controllers.

Scaffold a new app with DB wiring:

```bash
uv run nyxa init myapi --database sqlite   # or postgres | none
```

## Install

```bash
uv add nyxa-db
```

## Models & sessions

```python
from typing import Annotated

from fastapi import Depends
from sqlalchemy.orm import Session

from nyxa_db import Field, Model, get_session


class User(Model):
    id: int
    name: str
    email: str = Field(unique=True)


def list_users(session: Annotated[Session, Depends(get_session)]) -> list[User]:
    _ = session  # binds request session for Model helpers
    return User.all()
```

Slim annotations compile to SQLAlchemy columns. Use `Field(...)` for unique / FK /
defaults. Explicit `Mapped` / `mapped_column` still works. Model API helpers:
`all` / `find` / `where` / `create` / `save` / `paginate` (requires bound session).

Relationships: `HasOne` / `HasMany` / `BelongsTo` / `BelongsToMany`, plus
`User.with_("notes").get()` for `selectinload`. Docs:
[`docs/database/relationships.mdx`](../../docs/database/relationships.mdx).

Default URL: `DATABASE_URL`, or SQLite at `{project}/database/app.db`.

## CLI

```bash
uv run nyxa make:migration create_posts_table  # Schema Builder stub
uv run nyxa make:model User -m                 # model + Schema Builder migration
uv run nyxa make:model User -m --autogenerate  # model + Alembic autogenerate
uv run nyxa make:model Post -f                 # model + factory
uv run nyxa make:factory User                  # database/factories/user_factory.py
uv run nyxa make:seeder User                   # database/seeders/user_seeder.py
uv run nyxa db migrate                         # upgrade head
uv run nyxa db rollback                        # downgrade -1
uv run nyxa db status                          # URL, connection, revision
uv run nyxa db seed                            # run all seeders
```

First migrate / make:migration scaffolds `database/migrations/` and `alembic.ini` when missing.

## Schema Builder

Hand-written migrations (recommended) use a fluent Schema Builder / Blueprint API (compiles to Alembic). The migration owns
the schema; models define the Python mapping.

```python
from nyxa_db.schema import Schema

def upgrade() -> None:
    with Schema.create("posts") as table:
        table.increments("id")
        table.string("title")
        table.timestamps()

def downgrade() -> None:
    Schema.drop("posts")
```

Raw Alembic revisions and Schema Builder revisions coexist under `database/migrations/versions/`.
Put models under `src/{app}/models/` (needed for `--autogenerate`).

## Factories

```python
from database.factories.user_factory import UserFactory

user = UserFactory.make(email="a@example.com")       # unsaved
user = UserFactory.create(session, name="Ada")       # add + flush
users = UserFactory.create_many(session, 3)          # batch
```

```text
database/factories/
  user_factory.py    # class UserFactory(Factory[User])
```

Subclass ``nyxa_db.Factory``, set ``model``, and implement ``definition()``.

## Seeders

```text
database/seeders/
  user_seeder.py    # def run(session) or async def run(session)
```

Each module exposing `run(session)` is imported and executed with a short-lived
session (committed on success). Example playground seeder inserts one sample user.
