This is a demo project for a document workflow application. It attempts to solve the problem of automating several hours of administrative work for families upon the death of a loved one.
- Upload a death certificate image
- Automatically extract the relevant information
- Fill out a single form with informant's information
- Ready to download formatted letters (10+ documents) to send to the relevant institutions
For detailed scope and nomenclature, see Project.md.
The project is a monorepo with the following components:
- Enqueue APIs with FastAPI (Google Cloud Run)
- Extraction and Document Generation workers with FastAPI (Google Cloud Run)
- Extraction Prompt Management and Tracing via LangSmith
- Web app with Next.js (Vercel)
- Authentication with Firebase Authentication
- Database with Firestore
- Storage with Firebase Storage (Google Cloud Storage)
- Hosting with Vercel
This project uses a monorepo structure because we have tightly coupled services (API and web app) with coordinated deployment cycles and a small team working on features end-to-end. This structure reduces coordination overhead and enables atomic cross-service changes.
For a detailed exploration of monorepo vs polyrepo decision-making, see Monorepo vs Polyrepo: Team Structure Over Technology.
project/
├── api/ # Python FastAPI (Ruff)
├── apps/web/ # Next.js (ESLint + Prettier)
├── docs/ # Documentation
└── al-demo.code-workspace # Pre-configured IDE settings
- Architecture: System architecture, technology stack, and design patterns
- Authentication: Authentication flows, user roles, and security implementation
See docs/system/ for complete system documentation.
Complete setup guide for the project monorepo. Format-on-save and linting are pre-configured.
-
Open workspace file (required for proper IDE integration):
cursor al-demo.code-workspace
-
Install extensions (if not already installed):
- Ruff (charliermarsh.ruff)
- ESLint (dbaeumer.vscode-eslint)
- Prettier (esbenp.prettier-vscode)
-
Reload window:
Ctrl+Shift+P→ "Developer: Reload Window" -
Install dependencies:
# Root (required for Husky pre-commit hooks) npm install # Python API cd api && uv sync # Next.js App cd apps/web && npm install
That's it! Format-on-save is now active for all files.
Python (api/pyproject.toml):
- Ruff formatter (100 char line length)
- Python 3.13 + PEP 8 rules
- Format & auto-fix on save
- Pre-commit hooks via Husky
TypeScript/JavaScript (apps/web/):
- Prettier formatter (100 char line length)
- ESLint with Next.js rules
- Format & auto-fix on save
- Pre-commit hooks via Husky
Each project keeps its own .env file in its directory:
api/.env # Python API environment variables
apps/web/.env.local # Next.js app environment variables (gitignored)
Note: These files are gitignored. Never commit secrets to the repository.
The API uses .env for config. Set ENVIRONMENT=local when running locally—this is critical. Without it, Python uses Application Default Credentials and you will hit Google service account/auth errors. Copy from api/.env.example and set FIREBASE_SERVICE_ACCOUNT_PATH to your service account JSON file. Never commit service account files.
Uses Firebase client SDK (public env vars) and Admin SDK (server-only: FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY, FIREBASE_STORAGE_BUCKET). Copy from apps/web/.env.example.
For direct client uploads to work, set CORS on your Firebase Storage bucket. Use firebase/storage.cors.json as the config:
- Install gcloud CLI
- Authorize gcloud CLI (login and project management)
gcloud config set project YOUR_PROJECT_IDgcloud storage buckets update gs://BUCKET_NAME --cors-file=firebase/storage.cors.json
Root level (Husky, shared tooling):
# From repository root
npm install <package-name>Next.js app (apps/web):
# Option 1: From root using workspace
npm install <package-name> -w apps/web
# Option 2: From apps/web directory
cd apps/web && npm install <package-name>Python API (api):
cd api && uv add <package-name>cd api
# Dev server
uv run uvicorn app.main:app --reload
# Lint & format (use scripts)
./scripts/lint.sh # Check only
./scripts/fix.sh # Auto-fix & format
# Or use Ruff directly
uv run ruff check # Check
uv run ruff check --fix # Fix
uv run ruff format # Formatcd apps/web
# Dev server
npm run dev
# Lint & format
npm run lint # Check
npm run lint:fix # Auto-fix
npm run format # Format with Prettier
npm run format:check # Check formatting
npm run build # Production buildPre-commit hooks are automatically configured using Husky + lint-staged. They were set up during npm install.
What happens on commit:
-
Python files (
api/**/*.py):- Ruff checks and auto-fixes issues
- Ruff formats code
-
TypeScript/JavaScript files (
apps/web/**/*.{ts,tsx,js,jsx}):- ESLint checks and auto-fixes issues
- Prettier formats code
-
Other files (
apps/web/**/*.{json,css,md}):- Prettier formats code
Configuration: See lint-staged section in root package.json
To skip hooks (not recommended):
git commit --no-verifyRuff crashes: Disable extension, use CLI (./scripts/fix.sh)
Linting not working: Open via workspace file, then reload window
TypeScript version conflicts: Accept "Use Workspace Version" prompt
Python interpreter wrong: Set to api/.venv/bin/python
Python Google/Firebase auth errors: Ensure api/.env has ENVIRONMENT=local so the API uses the service account file instead of Application Default Credentials.
Cloud Run "private key to sign credentials" error: Set ENVIRONMENT=prod (or dev) in Cloud Run env vars so the API uses Application Default Credentials and IAM signBlob for signed URLs. Enable the IAM Service Account Credentials API. If the error persists, grant the Cloud Run service account roles/iam.serviceAccountTokenCreator on itself.
- run: |
cd api
uv run ruff check --no-fix
uv run ruff format --check- run: |
cd apps/web
npm ci
npm run lint
npm run format:checkal-demo.code-workspace- IDE settings (format-on-save, linters)api/pyproject.toml- Ruff configurationapps/web/eslint.config.mjs- ESLint rulesapps/web/.prettierrc.json- Prettier configapi/scripts/*.sh- Convenience lint/fix scripts