Skip to content

Development Guide

This guide covers day-to-day development workflows, testing, debugging, and code quality practices for Noumaris.

Quick Start

See Onboarding Guide for initial setup. Once set up:

bash
# Terminal 1: Docker services
docker-compose up -d

# Terminal 2: Backend
cd backend && poetry shell
python -m uvicorn src.noumaris_backend.api.main:app --reload

# Terminal 3: Frontend
cd frontend && npm run localhost

Development Workflow

Feature Development

  1. Create feature branch

    bash
    git checkout develop
    git pull origin develop
    git checkout -b feature/your-feature-name
  2. Make changes - See coding patterns below

  3. Test locally - Manual testing + pytest for backend

  4. Commit with meaningful messages

    bash
    git add .
    git commit -m "Add feature: brief description"
  5. Push and create PR

    bash
    git push -u origin feature/your-feature-name
    # Create PR on GitHub targeting 'develop' branch

Branch Naming

TypeFormatExample
Featurefeature/descriptionfeature/offline-mode
Bug fixfix/descriptionfix/websocket-auth
Documentationdocs/descriptiondocs/add-deployment-guide
Infrastructureinfra/descriptioninfra/terraform-staging

Commit Message Format

Add feature: Brief description (imperative mood)

- Detail 1
- Detail 2

Examples:

  • Add institution admin dashboard
  • Fix WebSocket connection timeout
  • Update deployment guide for Cloud Run
  • added stuff (vague, wrong tense)
  • WIP (commit unfinished work with description)

Backend Development

Database Migrations

Creating Migrations

After modifying backend/src/noumaris_backend/models/db_models.py:

bash
cd backend
poetry shell

# Generate migration
alembic revision --autogenerate -m "add_column_to_users"

# Review generated file in alembic/versions/
# Edit if needed (Alembic isn't perfect)

# Apply migration
alembic upgrade head

Migration Best Practices

  • Review auto-generated migrations - Alembic can miss renames or complex changes
  • Test migrations - Apply on fresh database to verify
  • Downgrade support - Ensure downgrade() function works
  • Data migrations - Use op.execute() for data changes
python
# Example: Add column with default
def upgrade():
    op.add_column('users', sa.Column('verified', sa.Boolean(), nullable=False, server_default='false'))

def downgrade():
    op.drop_column('users', 'verified')

Common Migration Commands

CommandDescription
alembic upgrade headApply all pending migrations
alembic downgrade -1Rollback one migration
alembic historyView migration history
alembic currentShow current revision
alembic revision -m "msg"Create blank migration

Database Seeding

bash
# Seed templates and tags
python seed_db.py

# Seed feature permissions
python scripts/seed_features.py

Modify seed_db.py to add new templates or demo data.

API Development Patterns

Adding New Endpoint

python
from fastapi import APIRouter, Depends, HTTPException
from src.noumaris_backend.api.auth import get_current_user
from src.noumaris_backend.models.db_models import User

router = APIRouter()

@router.get("/endpoint")
@limiter.limit("10/minute")  # Rate limiting
async def endpoint_name(
    current_user: User = Depends(get_current_user)
):
    # Use with statement for DB sessions
    with db_manager.create_session() as session:
        # Your logic here
        result = session.query(Model).filter_by(user_id=current_user.id).first()

        if not result:
            raise HTTPException(status_code=404, detail="Not found")

        return {"data": result}

Key patterns:

  • Always use Depends(get_current_user) for authenticated endpoints
  • Always use with db_manager.create_session() as session: for database access
  • Apply rate limiting with @limiter.limit("X/minute")
  • Raise HTTPException for errors (don't return error dicts)

Database Session Management

python
# ✅ CORRECT - Context manager auto-commits and closes
with db_manager.create_session() as session:
    user = session.query(User).filter_by(id=user_id).first()
    user.name = "New Name"
    # Auto-committed on exit

# ❌ WRONG - No context manager
session = db_manager.create_session()
user = session.query(User).first()
# Session never closed, potential leak

Testing Backend

bash
cd backend
poetry shell
pytest

# Run specific test
pytest tests/test_api.py::test_create_document

# Run with output
pytest -v -s

Currently backend has minimal tests. Add tests in backend/tests/ following pytest conventions.

Frontend Development

Component Structure

src/components/
├── layout/          # MainLayout, Sidebar (app structure)
├── ui/              # Reusable primitives (Button, Dialog)
├── features/        # Feature-specific (TemplateCard)
└── views/           # Page components (DoctorDashboard)

Conventions:

  • Pages go in views/ - one per route
  • Reusable UI goes in ui/ - buttons, inputs, dialogs
  • Feature modules go in features/ - domain-specific components

State Management

TypeToolExample
Server stateReact QueryUser data, templates, documents
Auth stateAuthContextJWT token, user info
Form stateReact Hook FormTemplate creation, user forms
Local UI stateuseStateModal open/closed, filters

API Calls with React Query

jsx
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';

// Fetch data
const { data, isLoading, error } = useQuery({
  queryKey: ['templates'],
  queryFn: async () => {
    const response = await fetch(`${API_URL}/templates`, {
      headers: { Authorization: `Bearer ${token}` }
    });
    return response.json();
  }
});

// Mutation (create/update/delete)
const mutation = useMutation({
  mutationFn: async (newTemplate) => {
    const response = await fetch(`${API_URL}/templates`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`
      },
      body: JSON.stringify(newTemplate)
    });
    return response.json();
  },
  onSuccess: () => {
    queryClient.invalidateQueries(['templates']);  // Refetch
  }
});

Environment Variables

Frontend uses different env files per mode:

ModeFileUsage
Local dev.env.localhostnpm run localhost
Development.env.developmentnpm run dev
Staging.env.stagingnpm run build:staging
Production.env.productionnpm run build:prod

Never commit .env.localhost - gitignored for local API keys.

Styling Conventions

  • TailwindCSS for layout and utilities
  • Radix UI for accessible primitives (Dialog, DropdownMenu)
  • CSS variables for theming (--primary, --secondary)
  • Class Variance Authority (CVA) for variant components
jsx
// Example: Button component with variants
import { cva } from "class-variance-authority";

const buttonVariants = cva(
  "rounded px-4 py-2 font-medium transition",
  {
    variants: {
      variant: {
        primary: "bg-blue-600 text-white hover:bg-blue-700",
        secondary: "bg-gray-200 text-gray-900 hover:bg-gray-300",
      },
      size: {
        sm: "text-sm px-3 py-1",
        md: "text-base px-4 py-2",
      }
    },
    defaultVariants: {
      variant: "primary",
      size: "md"
    }
  }
);

Debugging

WebSocket Debugging

WebSocket transcription issues are common. Check:

  1. Connection ID - Logged for every connection

    INFO: WebSocket /transcribe connected (id: abc-123)
  2. Authentication - Token passed as query param

    javascript
    const ws = new WebSocket(`ws://localhost:8000/transcribe?token=${jwt}`);
  3. Deepgram connection - Check backend logs

    ERROR: Deepgram connection failed: timeout
  4. Rate limiting - Max 3 concurrent connections per user

    ERROR: Rate limit exceeded for user_id: 123

Common fixes:

  • Expired token → Refresh JWT
  • Connection timeout → Check Deepgram API key
  • Rate limit → Close other connections
  • Audio not streaming → Check microphone permissions

Database Debugging

Use psql to inspect database directly:

bash
# Connect to local database
docker exec -it noumaris-postgres psql -U medical_user -d medical_db

# Useful queries
\dt                              # List tables
SELECT * FROM users LIMIT 10;   # View users
SELECT * FROM alembic_version;  # Current migration
\q                              # Quit

API Debugging

FastAPI provides interactive docs:

Test endpoints directly with authorization:

  1. Get JWT token from frontend (DevTools → Application → Local Storage)
  2. Click "Authorize" button in Swagger UI
  3. Paste token
  4. Test endpoints

Frontend Debugging

Common issues:

IssueCheckFix
Auth stuck on "initializing"AuthContext logsClear localStorage, re-login
API calls fail (401)Token in headersRefresh token
CORS errorBackend CORS configAdd frontend URL to allowed origins
Component not re-renderingReact Query cacheInvalidate query

DevTools:

  • React DevTools - Component tree, props, state
  • Network tab - API calls, response codes
  • Console - Error messages, logs

Code Quality

Linting

Frontend:

bash
cd frontend
npm run lint        # Check for issues
npm run lint:fix    # Auto-fix

Backend: Python linting is manual. Follow conventions:

  • Type hints for function parameters and returns
  • Docstrings for public functions
  • PEP 8 style guide

Code Review Checklist

Before creating PR:

  • [ ] Code follows existing patterns
  • [ ] No hardcoded secrets or API keys
  • [ ] Database sessions use context manager
  • [ ] API endpoints have rate limiting
  • [ ] Frontend components are reusable
  • [ ] No console.log() or print() statements (use proper logging)
  • [ ] Commit messages are descriptive

Performance Best Practices

Backend:

  • Use select_related() / joinedload() to avoid N+1 queries
  • Add database indexes for frequently queried fields
  • Use async endpoints for I/O-heavy operations

Frontend:

  • Use React.memo() for expensive components
  • Lazy load routes: const Page = lazy(() => import('./Page'))
  • Optimize images (WebP format, lazy loading)
  • Debounce search inputs

Git Workflow

Working with Develop Branch

bash
# Update your branch with latest develop
git checkout develop
git pull origin develop
git checkout feature/your-feature
git rebase develop

# Resolve conflicts if any
# Then force push (safe on feature branches)
git push --force-with-lease

Resolving Merge Conflicts

bash
# During rebase, if conflicts occur:
git status                    # See conflicted files
# Edit files to resolve conflicts
git add .
git rebase --continue

# Or abort and try merge instead
git rebase --abort
git merge develop

Common Git Commands

CommandDescription
git statusCheck working tree status
git diffSee unstaged changes
git diff --stagedSee staged changes
git log --oneline -10View recent commits
git stashTemporarily save changes
git stash popRestore stashed changes
git reset HEAD~1Undo last commit (keep changes)

Local Keycloak Management

Updating Keycloak Configuration

Keycloak is managed via Terraform:

bash
cd terraform/keycloak

# Modify main.tf or terraform.tfvars.local
# Apply changes
terraform apply

# Type 'yes' when prompted

Creating Test Users

Use Keycloak Admin Console (http://localhost:8081):

  1. Login: admin / admin
  2. Select "noumaris" realm
  3. Users → Add user
  4. Set username, email, email verified: ON
  5. Credentials tab → Set password (temporary: OFF)
  6. Role Mappings → Assign realm roles (superadmin, institution_admin, resident, user)

Viewing Keycloak Logs

bash
docker-compose logs -f keycloak

Environment-Specific Notes

Running on Different Port

Backend default: 8000. To change:

bash
# Method 1: Environment variable
PORT=8001 python -m uvicorn src.noumaris_backend.api.main:app --reload

# Method 2: Uvicorn flag
python -m uvicorn src.noumaris_backend.api.main:app --reload --port 8001

Update frontend .env.localhost to match:

VITE_API_URL=http://localhost:8001

Using Different Databases

Default: Docker PostgreSQL on 5433. For external database:

bash
# Update backend/.env
DATABASE_URL=postgresql://user:password@host:port/dbname

# Run migrations
cd backend
alembic upgrade head

Next Steps

Internal documentation for Noumaris platform