Affordable Housing Portal is Civic Tech Waterloo Region's affordable housing platform. It helps housing seekers find affordable housing listings and gives housing providers a place to publish richer, more accessible listing information.
The app is a Next.js 16 App Router application using React 19, TypeScript, Tailwind CSS 4, shadcn/ui primitives, NextAuth credentials auth, Drizzle ORM, Postgres, and Zod-based API contracts.
Current product areas include:
- signed-in listing search, map/list views, and listing detail pages
- partner/admin listing authoring with draft autosave and image uploads
- partner/admin "My Listings" management
- admin account invites, account management, and custom listing fields
- listing filters powered by admin-configured listing field definitions
Start with docs/README.md. The developer reference is split by topic so new contributors can find the right level of detail quickly:
- Getting Started for local setup, environment variables, database setup, and scripts
- Architecture for the App Router structure, service/repository boundaries, and feature workflow
- Domain Model for database tables, enums, relationships, and migration rules
- Listings for listing search, listing details, authoring, draft autosave, image uploads, and custom fields
- Auth and Admin for NextAuth credentials auth, invites, roles, access checks, and admin tools
- API Reference for route handlers, schemas, endpoint behavior, and error responses
- Deployment and Operations for CI, Docker, Infisical, runtime settings, and migration expectations
- Testing and Quality for Jest, linting, formatting, hooks, and review expectations
- ADR 0001 for the server-first listings data decision
Prerequisites:
- Node.js 22.12 or newer
- npm with the committed
package-lock.json - a reachable Postgres database
Install dependencies:
npm ciCreate a local environment file:
cp .env.example .env.localSet DATABASE_URL to your local database. The committed example uses:
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5432/affordable_housing_portalIf you use the Docker Compose database from this repo, set the host port to 5433:
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:5433/affordable_housing_portalApply migrations and seed local development data:
npm run db:migrate
npm run db:seedStart the app:
npm run devOpen http://localhost:3000.
The Docker Compose workflow starts Postgres and the development app together, then runs migrations and seed data before next dev:
npm run docker:upTransactional emails, currently admin invites, are queued durably in Postgres with pg-boss instead of being sent inline. Invite creation and job enqueueing happen in the same transaction, and the worker started from instrumentation.ts records whether the provider accepted the request or the job permanently failed. Provider acceptance does not confirm delivery to the recipient's mail server; delivered, bounced, failed, and suppressed webhook outcomes are not currently reconciled.
Set EMAIL_WORKER_ENABLED=true on the long-lived app server that should process jobs. Docker Compose enables it by default. See Auth and Admin for submission behavior and Deployment and Operations for worker, retry, dead-letter, and secret-rotation guidance.
npm run typecheck
npm run lint
npm run format:check
npm test
npm run test:unit
npm run buildUse npm run format and npm run lint:fix for mechanical fixes.
app/ Pages, layouts, server actions, route handlers, and route-local UI
components/ Shared React components and shadcn/ui primitives
content/ Static content such as product verbiage
db/ Drizzle schema, lazy database client, and seed scripts
drizzle/ Generated SQL migrations and Drizzle snapshots
docs/ Developer documentation and ADRs
lib/ Server-side domain services, repositories, auth, policies, and utilities
public/ Static assets served by Next.js
shared/schemas/ Zod schemas and inferred TypeScript contracts shared across layers
test/ Test-only mocks and helpers
Development tasks are managed through GitHub Issues.
Before opening a PR, run the relevant checks and include testing notes. The pre-commit hook runs secret scanning, lint-staged, and npm run typecheck; the pre-push hook runs npm run build.