Monorepo con: Astro Payload Supabase Postgres
Go to file
Julio Ruiz e1f55c8411 Inicial commit 2026-07-31 14:03:42 -05:00
.claude Inicial commit 2026-07-31 14:03:42 -05:00
apps Inicial commit 2026-07-31 14:03:42 -05:00
packages/typescript-config Inicial commit 2026-07-31 14:03:42 -05:00
supabase/volumes Inicial commit 2026-07-31 14:03:42 -05:00
.env.example Inicial commit 2026-07-31 14:03:42 -05:00
.gitignore Inicial commit 2026-07-31 14:03:42 -05:00
.npmrc Inicial commit 2026-07-31 14:03:42 -05:00
README.md Inicial commit 2026-07-31 14:03:42 -05:00
docker-compose.yml Inicial commit 2026-07-31 14:03:42 -05:00
package.json Inicial commit 2026-07-31 14:03:42 -05:00
pnpm-lock.yaml Inicial commit 2026-07-31 14:03:42 -05:00
pnpm-workspace.yaml Inicial commit 2026-07-31 14:03:42 -05:00
turbo.json Inicial commit 2026-07-31 14:03:42 -05:00

README.md

cdrdpyj

A TypeScript Turborepo wiring an Astro frontend to a Payload CMS backend, both running against a self-hosted Supabase stack (Postgres), orchestrated from a single docker-compose file — with portless for stable *.localhost dev URLs.

┌──────────────┐        REST /api        ┌──────────────┐        SQL         ┌───────────────────────────┐
│  Astro (web) │ ──────────────────────▶ │ Payload (cms)│ ─────────────────▶ │  Supabase (Postgres 17)   │
│  SSR, :4321  │                         │  Next, :3000 │   schema: payload  │  db · studio · kong ·     │
│ web.localhost│                         │              │                    │  auth · rest · storage    │
└──────────────┘                         └──────────────┘                    └───────────────────────────┘
      ▲ portless                                                              Studio dashboard :8000
  • apps/web — Astro 5 (SSR via @astrojs/node). Reads posts from Payload's REST API.
  • apps/cms — Payload 3 (on Next.js 16). Stores content in a dedicated payload schema in Postgres.
  • Supabase — trimmed self-hosted stack: db, studio, meta, kong, auth, rest, storage, imgproxy.
  • packages/typescript-config — shared tsconfig bases.

Prerequisites

Tool Version Notes
Node ≥ 24 portless and Payload both require Node 24+
pnpm ≥ 9 corepack enable or npm i -g pnpm
Docker + Compose v2 for the Supabase backend
portless latest pnpm add -g portless (optional but recommended)

Quick start

# 1. Configure — one env file drives the whole stack
cp .env.example .env

# 2. Install workspace deps
pnpm install

# 3. Start the Supabase backend (Postgres + Studio + Auth + REST + Storage)
docker compose up -d          # first run pulls images; give it a minute

# 4. Run the apps (Astro + Payload) with hot reload
pnpm dev

Then open:

What URL
Astro frontend http://localhost:4321 (or the portless URL below)
Payload admin http://localhost:3000/admin
Supabase Studio http://localhost:8000 (login: supabase / this_password_is_insecure_and_should_be_updated)
Postgres postgresql://postgres:postgres@localhost:5432/postgres

First time in Payload admin you'll create the initial admin user. Then add a Post (give it a slug) and it appears on the Astro homepage.


portless — stable *.localhost URLs

portless gives the frontend a memorable HTTPS URL instead of a port number, and injects $PORT automatically.

pnpm add -g portless      # once
pnpm dev                  # Astro is started via `portless web astro dev`

The web app is served at https://web.localhost. On first run portless generates and trusts a local CA and binds to :443 (may prompt for sudo).

Why only the frontend? Payload stays on plain http://localhost:3000 so Astro's server-side fetch to it uses plain HTTP — Node doesn't trust portless's local CA for server-to-server HTTPS. To also route Payload through portless (https://cms.localhost), run pnpm --filter cms dev:portless and set NODE_EXTRA_CA_CERTS to portless's CA so Astro can reach it over HTTPS.

Prefer no portless? Use pnpm --filter web dev:plain (plain astro dev).


Everything in Docker (optional)

The apps also ship with Dockerfiles behind a compose apps profile:

docker compose --profile apps up -d --build

This runs Astro (:4321) and Payload (:3000) as containers alongside Supabase. DATABASE_URI and the Astro→Payload URL are rewired to the internal Docker network automatically. The recommended day-to-day loop is still docker compose up -d (backend) + pnpm dev (apps) for hot reload.


Scripts

Run from the repo root:

Command Does
pnpm dev Turbo runs both apps (Astro via portless, Payload plain)
pnpm build Build all apps
pnpm typecheck Type-check all apps
pnpm supabase:up / :down Start / stop the Supabase backend
pnpm supabase:logs Tail backend logs
pnpm supabase:reset ⚠️ Stop and delete all Postgres data + volumes
pnpm stack:up / :down Backend and apps in Docker (apps profile)

Per-app: pnpm --filter cms generate:types, pnpm --filter cms generate:importmap.


How configuration works

There is one .env at the repo root, shared by everything:

  • docker-compose reads it natively.
  • Payload loads it from the root in apps/cms/src/payload.config.ts.
  • Astro loads it via vite.envDir pointed at the root; PUBLIC_* vars are exposed to the browser.

The Supabase ANON_KEY / SERVICE_ROLE_KEY in .env.example are the standard Supabase demo keys, signed with the matching demo JWT_SECRET. They work out of the box for local dev. Changing JWT_SECRET invalidates them — regenerate with Supabase's generate-keys steps if you do.


Project structure

.
├── docker-compose.yml         # single-file Supabase stack (+ apps profile)
├── .env.example               # one source of truth for all config
├── turbo.json                 # task pipeline
├── apps/
│   ├── web/                   # Astro SSR frontend
│   └── cms/                   # Payload 3 (Next.js) CMS
├── packages/
│   └── typescript-config/     # shared tsconfig bases
└── supabase/
    └── volumes/               # kong config + Postgres init SQL (roles, jwt, payload schema)

Production notes

This scaffold is tuned for local development. Before deploying anywhere real:

  • Regenerate every secret in .env (POSTGRES_PASSWORD, JWT_SECRET + matching keys, PAYLOAD_SECRET, DASHBOARD_PASSWORD, PG_META_CRYPTO_KEY, the S3 protocol keys).
  • Put Kong / the apps behind TLS and real domains; set SITE_URL, API_EXTERNAL_URL, SUPABASE_PUBLIC_URL, PAYLOAD_PUBLIC_SERVER_URL, PUBLIC_PAYLOAD_API_URL accordingly.
  • Switch Payload from dev "push" to committed migrations (pnpm --filter cms payload migrate:create).
  • Consider adding back the Supabase services this scaffold trims (Realtime, Edge Functions, Supavisor pooler) if you need them — see Supabase self-hosting docs.

Troubleshooting

  • Astro homepage shows "Couldn't reach the CMS" — Payload isn't running or the DB is down. Ensure docker compose up -d is healthy and pnpm dev shows the cms server on :3000.
  • Payload can't connect to Postgres — the DB exposes :5432; confirm docker compose ps shows cdrdpyj-db healthy and DATABASE_URI matches.
  • Ports already in use5432, 8000, 3000, 4321 must be free (or change them in .env / the app dev scripts).