Files
gurixandClaude 5bb27bdadf chore: archive add-graphql-member-api proposal and create capability specs
Archive completed GraphQL API implementation proposal:
- Move proposal to openspec/changes/archive/2025-11-20-add-graphql-member-api/
- Create capability specs in openspec/specs/:
  - database-layer: SQLAlchemy async with Alembic migrations
  - graphql-api: Strawberry GraphQL with FastAPI integration
  - member-crud: Member management with conditional validation
  - project-setup: Python 3.11+ with uv package manager

All 73 tasks completed and validated.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-20 13:26:43 +01:00

7.7 KiB

database-layer Specification

Purpose

TBD - created by archiving change add-graphql-member-api. Update Purpose after archive.

Requirements

Requirement: SQLAlchemy Member model with async support

The system MUST define a Member SQLAlchemy model with async session support for persisting member data with name, address, and contact information.

Model fields:

  • id: Integer primary key (auto-increment)
  • first_name: String (max 100 chars, required)
  • last_name: String (max 100 chars, optional)
  • street: String (max 200 chars, optional)
  • apartment_number: String (max 20 chars, optional)
  • zip: String (max 20 chars, optional)
  • city: String (max 100 chars, optional)
  • country: String (max 100 chars, optional)
  • email: String (max 255 chars, optional)
  • phone: String (max 50 chars, optional)
  • created_at: DateTime (auto-set on creation)
  • updated_at: DateTime (auto-update on modification)

Scenario: Member model is defined with proper constraints

Given src/models/member.py exists When the Member class is inspected Then it inherits from SQLAlchemy Base And tablename is "members" And first_name has nullable=False And all other data fields (last_name, email, phone, address fields) have nullable=True And string fields have length constraints via String(N)

Scenario: Timestamps are automatically managed

Given a new Member instance is created When the instance is added to session and committed Then created_at is set to current UTC timestamp And updated_at is set to current UTC timestamp When the instance is later modified and committed Then updated_at is updated to new UTC timestamp And created_at remains unchanged

Scenario: Model supports async operations

Given Member model is defined When async database session is used Then CRUD operations execute without blocking event loop And SQLAlchemy async patterns are followed (select, add, commit)

Requirement: Database connection and session management

The system MUST provide async database connection factory and session management with proper lifecycle handling.

Connection configuration:

  • Async engine using aiosqlite for SQLite
  • Connection pooling disabled for SQLite (single-writer)
  • Echo mode configurable via DEBUG setting

Scenario: Async engine is created on application startup

Given src/database.py defines engine initialization When FastAPI app starts Then async engine is created with database_url from config And engine is configured for SQLite with aiosqlite driver And SQL echo is enabled if DEBUG=True

Scenario: Session factory provides isolated sessions

Given async_session_maker is defined When resolver requests database session Then new AsyncSession is created from factory And session is isolated from other concurrent requests And session is properly closed after request completes

Scenario: Session lifecycle is managed via dependency injection

Given get_db_session() dependency is defined When FastAPI resolver depends on db_session Then session is yielded for resolver use And session is committed if no exceptions occur And session is rolled back if exceptions occur And session is closed in finally block

Requirement: Alembic database migration setup

The system MUST use Alembic for version-controlled schema migrations with async support for SQLite database.

Alembic configuration:

  • Migrations stored in migrations/versions/
  • Environment configured for async operations
  • Migration template includes docstring and revision metadata

Scenario: Alembic is initialized with project structure

Given alembic init migrations was run When migrations/ directory is inspected Then migrations/env.py exists with async configuration And migrations/versions/ directory exists for migration files And alembic.ini contains database connection template

Scenario: Initial migration creates members table

Given Alembic is configured When developer runs alembic revision --autogenerate -m "create members table" Then new migration file is generated in migrations/versions/ And upgrade() function contains CREATE TABLE for members And downgrade() function contains DROP TABLE for members And all Member model columns are included

Scenario: Migrations are applied to database

Given migration files exist in migrations/versions/ When developer runs alembic upgrade head Then all pending migrations are executed in order And members table is created in database And alembic_version table tracks current revision And command exits with success code

Scenario: Migrations are reversible

Given database is at current migration head When developer runs alembic downgrade -1 Then most recent migration is reversed And members table is dropped (for initial migration) And alembic_version is updated to previous revision

Requirement: Database initialization on application startup

The system MUST verify database connectivity and schema readiness when FastAPI application starts.

Scenario: Application startup checks database connection

Given FastAPI app has startup event handler When application starts Then database engine connection is tested And exception is raised if database is unreachable And startup log message confirms database ready

Scenario: Database file is created if missing

Given SQLite database file does not exist When application starts Then database file is created automatically And schema tables exist after migrations run And application continues startup normally

Requirement: Test database fixtures with isolation

The system MUST provide pytest fixtures for database testing with transaction rollback to ensure test isolation.

Scenario: Test database session fixture is available

Given tests/conftest.py defines db_session fixture When test function requests db_session parameter Then isolated AsyncSession is provided And session uses in-memory SQLite database (:memory:) And schema is created before test runs And all changes are rolled back after test completes

Scenario: Test database is independent from development database

Given tests use db_session fixture When tests create or modify data Then changes are not visible in development database And test data does not persist between test runs And tests can run in parallel without interference

Requirement: Database query helpers for common operations

The system MUST provide reusable query patterns for fetching, creating, updating, and deleting members.

Scenario: Get member by ID query

Given member exists in database with id=1 When query executes select(Member).where(Member.id == 1) Then Member instance is returned When query executes with non-existent id Then None is returned (not exception)

Scenario: List all members query

Given multiple members exist in database When query executes select(Member).order_by(Member.last_name.nulls_last(), Member.first_name) Then all members are returned as list And members are sorted by last name (nulls last), then first name And empty list is returned if no members exist

Scenario: Update member fields

Given member exists with id=1 When member attributes are modified and session commits Then database record is updated And updated_at timestamp is refreshed And created_at timestamp is unchanged

Scenario: Delete member from database

Given member exists with id=1 When session executes delete(member) and commits Then member is removed from database And subsequent queries for that id return None