Skip to content
TALA
Esc
navigateopen⌘Jpreview
On this page

Quickstart

Get both halves of TALA running against a local Postgres, including the connection traps that cost the most time.

Ten minutes to a working local stack, assuming you already have Postgres and the credentials.

Prerequisites

  • Node v22.16.0 — pinned in backend/.nvmrc. Newer works; older does not.
  • PostgreSQL running locally, or a Supabase connection string.
  • AWS S3 bucket, CloudFront distribution, and Lambda credentials.
  • Resend API key, plus MAIL_FROM_NAME and MAIL_FROM_EMAIL on a verified domain.
  • Google and GitHub OAuth client credentials — optional; everything else works without them.

Backend

Copy the environment file

cd backend
cp .env.example .env

.env.example is the authoritative reference for every variable, and it documents the connection-string traps below. Read it before debugging a database failure.

Fill in the required values

Two variables are checked at boot, and the app refuses to start if either fails:

  • SECRET_KEY — at least 32 characters, and must not contain change-me. .env.example ships exactly that placeholder, so you must replace it or the boot check fails.
  • BUFFER_KEY — exactly 64 hex characters.

DATABASE_URL is not part of that check but is obviously required; a bad one fails at the first query instead of at boot. Add the AWS, Resend, and OAuth credentials as you need them.

# Both of these must be replaced, not left as shipped.
SECRET_KEY=$(openssl rand -hex 32)
BUFFER_KEY=$(openssl rand -hex 32)

A Homebrew-default local Postgres is built without SSL, so it also needs:

DB_SSL=false

Install, migrate, run

npm install
npm run migration:run
npm run start:dev

The API listens on port 8000. Swagger UI is at http://localhost:8000/api-docs and the liveness probe at http://localhost:8000/health — note that /health sits outside the api global prefix.

Frontend

cd frontend
echo "VITE_API_URL=http://localhost:8000" > .env
npm install
npm run dev

Vite serves on http://localhost:5173, which is also the CORS fallback origin the backend allows when FRONTEND_URL is unset.

Supabase connection traps

Shared environments run on Supabase. Use the session pooler, never the direct host:

postgresql://postgres.<ref>:<password>@aws-1-<region>.pooler.supabase.com:5432/postgres
The direct host is IPv6-only

db.<ref>.supabase.co publishes an AAAA record and no A record. It is unroutable from most laptops and IPv4 hosts unless you pay for the IPv4 add-on.

Port 6543 breaks TypeORM

5432 is session mode and is the one you want. 6543 is transaction mode, which breaks TypeORM’s prepared statements.

The username is not plain postgres

It is postgres.<project-ref>. Newer projects also use the aws-1- region prefix, not aws-0-.

Never set DB_SSL=false against Supabase

That flag is a local-Postgres-only escape hatch for a server built without SSL. Against a hosted database it disables transport encryption outright.

Row-level security is not automatic

Supabase publishes the public schema through PostgREST and grants anon and authenticated full DML on everything created there. The anon key is public by design. TypeORM knows nothing about any of this, so tables created by a migration land wide open.

The LockDownPublicSchema migration enables RLS on every public table and revokes those roles’ grants and default privileges.

Regenerating the API reference

The API reference in these docs is built from docs/openapi.json, which is a snapshot of the backend’s live Swagger document. Refresh it whenever a controller changes:

cd backend && npm run start:dev   # terminal 1
cd docs    && npm run spec        # terminal 2

Then rebuild the docs:

cd docs && npm run build

Was this page helpful?