Skip to content

🏗️ Architecture

The monorepo

LIPAIX uses a monorepo — a single Git repository that contains multiple applications and shared packages. This means all the code for the web app, the Discord bot, and the documentation lives in one place, making it easier to share code and keep things consistent.

lipaix/
├── apps/
│   ├── web/              # Next.js 15 + PayloadCMS 3
│   └── discord-bot/      # Discord.js 14 bot
├── shared/
│   └── common/           # Shared TypeScript code
└── docs/
    └── vitepress/        # This documentation

The repository uses pnpm workspaces to manage dependencies across all these packages from a single pnpm install at the root.


apps/web — Web application

This is the largest part of the project. It's a Next.js 15 application that runs three distinct sections in one process:

Route groups

Next.js organizes routes using route groups — folders in parentheses that group related routes without affecting the URL. The apps/web app has three:

(frontend) — The public website

Everything visitors see at lipaix.com: the home page, event listings, event detail pages, the About page, Impro, Videos, and Partners pages.

(payload) — MyLipaix and API

The PayloadCMS MyLipaix interface (at /admin) and all the custom API endpoints under /api/v1/. PayloadCMS runs inside Next.js and shares the same process.

(live) — The live show display

The public display page at /live/[eventId] used during Public Investigation shows. It connects to the server via a real-time stream (SSE) to receive updates as the coordinator makes changes in the Live Admin.

PayloadCMS

PayloadCMS is a headless CMS (Content Management System) — think of it as a configurable database with a built-in MyLipaix interface. In LIPAIX, it handles:

  • The database schema (all collections are defined in TypeScript)
  • The MyLipaix UI
  • Authentication (Discord OAuth)
  • A REST API for the collections

The PayloadCMS configuration lives at apps/web/src/payload.config.ts.


apps/discord-bot — Discord bot

A standalone Node.js service built with Discord.js 14. It:

  • Registers slash commands with Discord
  • Listens for slash command interactions
  • Calls the web app's /api/v1/ API to fetch data
  • Formats the response and sends it back to the user

It runs as a separate process, completely independent of the web app.


shared/common — Shared code

A TypeScript package imported by both apps/web and apps/discord-bot. It contains:

  • Adapters: transforms data from the PayloadCMS format into domain objects
  • Message builders: formats Discord messages (lineup posts, availability lists)
  • Type definitions: shared TypeScript types for events, players, availabilities, etc.

This avoids duplicating business logic across both apps.


How data flows

Public website → data

Server Components in the (frontend) route group call the PayloadCMS local API directly (in-process, no HTTP). This is fast and secure — no network round-trip needed.

Discord bot → data

The bot calls the web app's REST API (/api/v1/) over HTTP, authenticating with the LIPAIX_API_TOKEN.

Live show display → data

The /live/[eventId] page subscribes to a Server-Sent Events (SSE) stream at /api/v1/pi-session/[eventId]/stream. The Live Admin pushes updates through this stream in real time.

Public site      →  PayloadCMS local API  →  PostgreSQL
Discord bot      →  /api/v1/ (HTTP)       →  PostgreSQL
Live display     →  /api/v1/.../stream    →  PostgreSQL (SSE)

Technology summary

LayerTechnology
Frontend frameworkNext.js 15 (App Router, React 19)
CMS & MyLipaixPayloadCMS 3
DatabasePostgreSQL
StylingTailwindCSS
Discord botDiscord.js 14
LanguageTypeScript (everywhere)
Package managerpnpm workspaces
HostingRailway

Released under the MIT License.