Skip to content

Architecture

Joël Deffner edited this page Jul 17, 2026 · 1 revision

Architecture

FlightMeet is a decoupled two-tier application: a React SPA frontend talking to a CodeIgniter 4 JSON API backend. Both live in one repository but are separate applications with their own toolchains.

┌─────────────────────────┐        HTTP / JSON        ┌──────────────────────────┐
│  React SPA (frontend/)  │  ───────────────────────► │  CodeIgniter 4 (app/)    │
│  React 19 + Vite + TS   │   /api/*  (fetch, cookies)│  PHP 8.2, JSON-API-only  │
│  react-router-dom, TSX  │  ◄─────────────────────── │  Shield auth, MariaDB    │
└─────────────────────────┘                           └──────────────────────────┘
        built into  public/  ◄── same origin in prod ──►  served by Apache/CI

The two tiers

Frontend: frontend/

A React 19 single-page application built with Vite and TypeScript. It owns all UI and all interactive state (joined status, filters, form input, chat view). Routing happens client-side with react-router-dom v7. See Frontend.

Backend: app/

A CodeIgniter 4.7 application on PHP 8.2 that exposes a JSON API under /api/* and nothing else. It handles persistence (MariaDB), authentication (CodeIgniter Shield, session-based), and integration with Open-Meteo for weather and geocoding. See Backend.

The single remaining CodeIgniter HTML view (welcome_message) is a leftover fallback; nothing is built on it.

How a request flows

In development

Two servers run side by side:

  1. php spark serve runs the CodeIgniter API (default http://localhost:8080).
  2. pnpm dev runs the Vite dev server with HMR at http://localhost:5173.

The Vite dev server proxies /api and /media to the backend (configured in frontend/vite.config.ts, target overridable via CI_BACKEND_URL in frontend/.env.local). The browser only ever talks to localhost:5173, so cookies and CSRF behave as same-origin.

In production

Everything is served from one origin. The SPA is compiled into public/ (which is also CodeIgniter's document root) and Apache dispatches requests via public/.htaccess:

Request path Handled by
api/*, media/* CodeIgniter's index.php (the JSON API)
An existing file (JS bundle, image, ...) Served directly by Apache
Everything else Falls through to index.html, where React Router takes over

This is why deep links like /meets/3 work in production without any server-side route definitions: Apache serves index.html, and the SPA resolves the path.

The production URL is https://team11.wi1cm.uni-trier.de/public/, so the Vite build uses base: '/public/' and the frontend prefixes API calls with that base (see frontend/src/lib/api.ts, which explains that without the prefix, the root .htaccess redirect would turn a POST into a GET).

State and data ownership

  • The database owns facts. Meets, participants, groups, members, messages, users.
  • Derived values are never stored. participantCount, memberCount and meet status (open/full) are always computed from the join tables at read time (spec NFR-4). There is no counter column to drift out of sync.
  • The SPA owns interaction state. Filters, search text, form drafts, optimistic join/leave feedback, and chat polling live client-side. State-changing responses (join, leave, edit) return the fresh full resource so every view can reconcile immediately.

Cross-cutting mechanics

  • Auth is session-cookie based (Shield). The SPA never handles tokens beyond the CSRF token; the browser carries the session cookie. See Authentication and Roles.
  • CSRF is globally enabled in session mode. Every mutating request needs the X-CSRF-TOKEN header. The SPA fetches the token from /api/auth/me (or the login/register response) and frontend/src/lib/api.ts attaches it automatically.
  • Error contract is uniform across the API: 422 { errors: { field: message } } for validation, 401/403/404/409 with { error: "..." } otherwise. The frontend's ApiError class parses both shapes. See API Reference.

Repository layout

fwebdev/
├── app/                    # CodeIgniter 4 application (the API)
│   ├── Config/             #   Routes.php, Auth.php, AuthGroups.php, Filters, ...
│   ├── Controllers/        #   Api.php, Weather.php, Api/* (domain), Api/Admin/*
│   ├── Database/           #   Migrations/ and Seeds/
│   ├── Filters/            #   ApiAuthFilter, AdminApiFilter
│   └── Libraries/          #   OpenMeteo.php, Participant.php
├── frontend/               # React SPA (Vite + TypeScript)
│   └── src/                #   pages/, components/, lib/
├── public/                 # CI document root + committed SPA build output
│   └── .htaccess           #   the production routing rules
├── docs/                   # SPECIFICATION, API contract, AUTH model
├── writable/               # CI logs, cache, session files
├── env / .env              # environment template / local config (not committed)
└── composer.json           # PHP dependencies (CodeIgniter, Shield)

Clone this wiki locally