This guide explains how to set up, develop, test, preview, and deploy the Pair Research app. It is intended to keep the development workflow consistent across contributors.
For code organization, contribution rules, file size guidelines, comments, TSDoc, and testing expectations, see CONTRIBUTING.md.
The app uses:
- Node.js >= 22
- pnpm for package management
- Cloudflare Workers for deployment
- Cloudflare Durable Objects for group pairing realtime coordination
- Cloudflare R2 for storage
- Wrangler for local Cloudflare development and deployment
Environment variables are managed separately from the repository. Do not commit secrets or local environment files.
Make sure Node.js >= 22 is installed:
node --versionIf Node.js is not installed, or if your version is too old, install the LTS version from the official Node.js website:
https://nodejs.org/en/download
Using a Node version manager such as nvm is recommended if you work across multiple projects.
Check whether pnpm is available:
pnpm --versionIf pnpm is not available, enable it through Corepack:
corepack enable pnpmIf this does not work, follow the official pnpm installation guide:
This repository includes recommended VS Code extensions for a better development experience, such as linting, formatting, and framework-related tooling.
If you open the project in VS Code for the first time, VS Code may prompt you to install the recommended extensions. You can also install them manually:
- Open the Extensions panel in VS Code.
- Search for
@recommended. - Install the workspace-recommended extensions.
These recommendations are defined in .vscode/extensions.json.
From the project root directory, install dependencies with pnpm:
pnpm installYou can also use the shorter alias:
pnpm iThis project uses Cloudflare Workers for deployment and R2 for storage. To run Cloudflare-related development commands locally, you need to be logged in with Wrangler.
Run:
pnpm wrangler loginYou must log in with the DTR Cloudflare account, not a personal Cloudflare account. Otherwise, you may not have access to the correct R2 bucket and related Cloudflare resources.
If you encounter credential or permission issues later, run the same command again to relogin.
Copy the example environment file:
cp .env.example .envThen open .env and fill in the required values. The comments in .env.example explain what each value is used for and how to obtain it.
This project uses both variables and secrets.
- Public Vite build values use
VITE_*names and are read throughimport.meta.env. For local development, add them to.env. For production builds, configure them as GitHub Actions secrets. - Worker runtime variables are non-sensitive values available to the Cloudflare Worker at runtime. Keep production runtime variables in the Cloudflare dashboard and deploy with
wrangler deploy --keep-varsso Wrangler does not clear them. - Secrets are sensitive values and must not be stored in
wrangler.jsonc. For local development, add them to.env. For deployed Cloudflare environments, add any missing remote secrets with:
pnpm wrangler secret put SECRET_NAMEAfter adding, removing, renaming, or changing any variable or secret used by the codebase, regenerate the Cloudflare environment types:
pnpm cf-typegenThis keeps the generated bindings in sync with the environment values expected by the application.
wrangler.jsonc defines the runtime bindings used by the Worker:
R2_BUCKETstores uploaded avatar objects.GROUP_SESSIONSis a Durable Object namespace. The app uses oneGroupSessionDOinstance per group ID to coordinate active pool tasks, ratings, pair creation, reset events, and realtime WebSocket fan-out. Active pool task and rating edits are staged in Durable Object SQLite storage first; Postgres persistence happens when the pairing is created or the pool is reset.
When adding or renaming a binding, update wrangler.jsonc, run pnpm cf-typegen, and verify the generated cloudflare-env.d.ts change.
Start the local development server:
pnpm devThen open:
http://localhost:3000/
To build and preview the production version locally, run:
pnpm previewThen open:
http://localhost:4000/
Use this when you want to check behavior closer to the deployed production build.
Auto-formatting on save and commit is already configured. Make sure the recommended VS Code extensions are installed so linting and formatting feedback appears during development.
To manually lint and fix issues, run:
pnpm run lint:fixRun all unit tests:
pnpm testUse this for logical changes, utility functions, algorithms, and non-UI behavior.
Run Durable Object restart, recovery, and WebSocket checks in the local Workers runtime:
pnpm run test:workersThese tests use an isolated Worker configuration and mocked Postgres access without remote bindings.
If you change the Prisma schema, regenerate the Prisma client:
pnpm run prisma:generateGenerated files should not be edited by hand. This includes:
src/routeTree.gen.tscloudflare-env.d.tsprisma/generated/client/**
If generated files are out of sync, rerun the relevant generation command instead of manually editing them.
pnpm devstarts the app locallypnpm buildbuilds the app for productionpnpm previewpreviews the production build locallypnpm run typecheckchecks TypeScript without emitting filespnpm lintruns ESLintpnpm run lint:fixruns ESLint and applies safe fixespnpm testruns Vitest unit testspnpm deploybuilds and deploys with Wrangler
The app deploys to Cloudflare Workers with Wrangler:
pnpm deployThe deploy script uses wrangler deploy --keep-vars so dashboard-managed runtime variables are preserved when wrangler.jsonc does not define vars.
Wrangler should write logs inside the repository instead of a user-level preferences directory:
WRANGLER_LOG_PATH=.wrangler/logs pnpm deployThe GitHub production workflow sets WRANGLER_LOG_PATH automatically.
Run the automated release preflight before public deployment:
pnpm run release:preflightThe preflight checks required release environment values, production URL formats, Worker secret declarations, R2 and Durable Object bindings, production routes, public route metadata, static production links, migration artifacts, and CI gates.
Configure these GitHub Actions secrets before enabling the production deployment workflow. The app reads these client-safe values through import.meta.env during the Vite build:
VITE_SUPABASE_URLVITE_SUPABASE_PUBLISHABLE_KEYVITE_SITE_BASE_URLVITE_CLOUDFLARE_TURNSTILE_SITE_KEYVITE_GOOGLE_CLIENT_ID
For local overrides, use Vite's normal .env files with the same VITE_* names.
Configure this public runtime value in both GitHub Actions secrets and the production Cloudflare Worker environment. It is server-only and is not read through Vite:
R2_PUBLIC_DOMAIN
Configure these GitHub Actions secrets before enabling the production deployment workflow. They are passed to the production release gates and Worker runtime:
DATABASE_URLSUPABASE_SECRET_KEYCLOUDFLARE_TURNSTILE_SECRET_KEYCONTACT_ADMIN_EMAILCONTACT_FROM_EMAILRESEND_API_KEY
Configure these additional GitHub Actions secrets for the production deploy step only. Do not put them in .env.example, local .env files, or Worker runtime configuration:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
Also configure the required Cloudflare Worker secrets in the production Cloudflare environment:
DATABASE_URLSUPABASE_SECRET_KEYCLOUDFLARE_TURNSTILE_SECRET_KEYCONTACT_ADMIN_EMAILCONTACT_FROM_EMAILRESEND_API_KEY
Do not put Cloudflare Worker secrets in wrangler.jsonc. The secrets.required section is a local declaration aid; it does not prove that the remote production environment already has those secrets.
Before deploying publicly, the automated release gates must pass:
pnpm run release:preflightpnpm run lint:cipnpm run typecheckpnpm run testWRANGLER_LOG_PATH=.wrangler/logs pnpm run build
If a release gate fails, fix the repository, environment, or deployment resource configuration instead of bypassing the gate.
Before opening a pull request, run:
pnpm run lint:fix
pnpm test
pnpm previewThis helps catch linting issues, test failures, and production-preview problems before CI.