Skip to content

The Publish workspace

One fictional product — a multi-tenant publishing platform — told through five apps that share features and never call each other over RPC.

Publish is the story the NestRS monorepo tells. Not a crate name — a multi-tenant publishing platform: publications (orgs), authors (users), posts, live comments, background jobs, and an MCP assistant for drafting. Several binaries, one workspace, shared crates/features/, no chatty RPC between deployables.

Three names run through every page of these docs, and this is the whole map. hello is the one-route binary nestrs new writes, blog is what the tutorial builds on top of it, and Publish is the finished workspace every reference page draws its users, orgs, posts and audio snippets from. The first two you scaffold in your own workspace with nestrs new; only Publish is hosted in this repository. A snippet naming posts is Publish’s unless the page says otherwise.

AppPortRole in the storyTransports
auth3001Authors sign in (OAuth2, JWT issuance)HTTP
api3002Main API — users, orgs, audio hooksHTTP, GraphQL, OpenAPI
assistant3003AI tools over the audio pipeline (JWT-guarded)MCP
live3004Live comments and notificationsWebSockets
worker3005Async jobs — newsletters, audio queueQueue, Schedule, HTTP (health only)
Terminal window
git clone https://github.com/YV17labs/NestRS && cd NestRS/demo
nestrs run db up # Postgres — api + auth
nestrs run dev auth # issuer on :3001
nestrs run dev api # resource server on :3002
nestrs run dev live # WebSockets on :3004
nestrs run dev assistant # MCP on :3003
nestrs run dev worker # headless — needs Redis

api and auth share the same database and the features crate; the worker shares Redis with the API’s queue producer. Tokens are self-contained JWTs — the API verifies with a public key, never calls the issuer over HTTP.

The workspace ships a container and a Helm chart. One image holds every binary, so a Deployment’s command is the whole difference between the five apps — demo/Dockerfile builds it, and the chart under demo/charts/demo/ deploys it.

Terminal window
helm install demo demo/charts/demo \
--set secrets.SEAORM__URL="postgres://user:pass@db:5432/nestrs" \
--set secrets.REDIS__URL="redis://redis:6379"

Every app is declared in one map, and kind says what it becomes in the cluster. Migrations are an app like the others — the same image, another binary — rendered as a job that runs to completion before any deployment is updated.

charts/demo/values.yaml
apps:
migrate: { kind: job, bin: migrate, args: [up] }
api: { bin: api, port: 3002, host: api.example.com, ingress: true }
worker: { bin: worker, port: 3005 }

Set the hostnames rather than the URLs. The apps address each other by URL — the issuer a token is signed by, the audience it is minted for, the resource identifier discovery serves, the MCP Host allowlist — and each of those is some app’s public origin, so the chart derives them from apps.<name>.host.

Scaling the queue worker is the one decision the chart does not make for you. The worker runs one job at a time per #[process] method, and Publish’s processors are I/O — an S3 round-trip, one INSERT — so a pod draining thousands of jobs sits near 0% CPU and a CPU-driven autoscaler never fires. Queue depth is the signal that moves with the work, and KEDA is what reads it: the chart ships the triggers for both queues, disabled until you turn them on. The chart’s README carries the trade-offs, including why replicas stop at one rather than zero.

Three entry points — two learning paths (CLI) and one hosted reference (this repo):

  • Getting started and the tutorial scaffold hello and blog with the CLI — reproducible in any workspace, not checked into this repo.
  • Fundamentals walks blog step ① — inline handlers in apps/blog/ (vocabulary only). The tutorial builds the composed layout from page 1 onward.
  • Reference pages (HTTP, GraphQL, queue, MCP, …) point at the matching Publish app above — the complex example you clone and explore.
Feature (crates/features/)Publish role
postsBlog articles — api (HTTP, org + author scoped)
usersAuthors and editors — api
orgsPublications / tenants — api
oauthOAuth2 login on auth
audioMedia attachments + queue/schedule hooks
authn / authzJWT verification and row-level policy — api, not the tutorial blog