smithery/psincraian

data-module

myfy DataModule for database access with async SQLAlchemy. Use when working with DataModule, AsyncSession, database connections, connection pooling, migrations, or SQLAlchemy models.

Installation

$ npx skills add smithery/psincraian --skill data-module

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/psincraian.

npx skills add smithery/psincraian

Browse all from smithery/psincraian

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 5,208 B
  • docs SUMMARY.md 201 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

DataModule - Database Access

DataModule provides async SQLAlchemy integration with connection pooling and REQUEST-scoped sessions.

Quick Start

from myfy.core import Application
from myfy.data import DataModule, AsyncSession
from myfy.web import route

app = Application()
app.add_module(DataModule())

@route.get("/users/{user_id}")
async def get_user(user_id: int, session: AsyncSession) -> dict:
    # session is auto-injected (REQUEST scope)
    result = await session.execute(select(User).where(User.id == user_id))
    return {"user": result.scalar_one_or_none()}

Configuration

Environment variables use the MYFYDATA prefix:

Variable Default Description
MYFYDATADATABASE_URL sqlite+aiosqlite:///./myfy.db Database connection URL
MYFYDATAPOOL_SIZE 5 Number of connections in pool
MYFYDATAMAX_OVERFLOW 10 Extra connections beyond pool_size
MYFYDATAPOOL_TIMEOUT 30.0 Seconds to wait for connection
MYFYDATAPOOL_RECYCLE 3600 Seconds before connection recycled
MYFYDATAPOOLPREPING True Test connections before use
MYFYDATAECHO False Log all SQL statements
MYFYDATAENVIRONMENT development Environment (blocks auto_create in production)

Supported Databases

# SQLite (development)
MYFY_DATA_DATABASE_URL="sqlite+aiosqlite:///./app.db"

# PostgreSQL (production)
MYFY_DATA_DATABASE_URL="postgresql+asyncpg://user:pass@localhost/db"

# MySQL
MYFY_DATA_DATABASE_URL="mysql+aiomysql://user:pass@localhost/db"

Defining Models

from sqlalchemy import String
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column

class Base(DeclarativeBase):
    pass

class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    email: Mapped[str] = mapped_column(String(255), unique=True)
    name: Mapped[str] = mapped_column(String(100))

Auto-Create Tables (Development Only)

from myfy.data import DataModule

app.add_module(DataModule(
    auto_create_tables=True,  # Only in development!
    metadata=Base.metadata,
))

Raises AutoCreateTablesProductionError if MYFYDATAENVIRONMENT=production.

Using Sessions in Routes

Sessions are REQUEST-scoped (one per HTTP request):

from myfy.data import AsyncSession
from sqlalchemy import select

@route.get("/users")
async def list_users(session: AsyncSession) -> list[dict]:
    result = await session.execute(select(User))
    users = result.scalars().all()
    return [{"id": u.id, "name": u.name} for u in users]

@route.post("/users", status_code=201)
async def create_user(body: UserCreate, session: AsyncSession) -> dict:
    user = User(**body.model_dump())
    session.add(user)
    await session.commit()
    await session.refresh(user)
    return {"id": user.id}

Using Sessions in Providers

from myfy.core import provider, REQUEST
from myfy.data import AsyncSession

@provider(scope=REQUEST)
def user_repository(session: AsyncSession) -> UserRepository:
    return UserRepository(session)

Transactions

Sessions auto-commit on success, rollback on exception:

@route.post("/transfer")
async def transfer(body: TransferRequest, session: AsyncSession) -> dict:
    # Both updates succeed or both rollback
    sender = await session.get(Account, body.sender_id)
    receiver = await session.get(Account, body.receiver_id)

    sender.balance -= body.amount
    receiver.balance += body.amount

    await session.commit()  # Explicit commit
    return {"success": True}

Using SessionFactory Directly

For background jobs or manual session management:

from myfy.data import SessionFactory

@provider(scope=SINGLETON)
def background_service(factory: SessionFactory) -> BackgroundService:
    return BackgroundService(factory)

class BackgroundService:
    async def process(self):
        async with self.factory.session_context() as session:
            # Manual session management
            await self.do_work(session)

Alembic Migrations

Initialize Alembic:

alembic init migrations

Configure alembic.ini:

sqlalchemy.url = driver://user:pass@localhost/dbname

Configure migrations/env.py:

from app.models import Base
target_metadata = Base.metadata

Create and run migrations:

alembic revision --autogenerate -m "Add users table"
alembic upgrade head

Best Practices

  1. Use async drivers - asyncpg for PostgreSQL, aiosqlite for SQLite
  2. Never share sessions - Sessions are REQUEST-scoped for a reason
  3. Use migrations in production - Don't use autocreatetables in production
  4. Configure pool size - Match to expected concurrent connections
  5. Enable poolpreping - Detects stale connections before use
  6. Use transactions - Group related operations in a single commit