XMoravec
Blog

Playground Lab Engineering Notes: Architecture, Security, and Delivery Decisions

Published 02/22/2026 · 8 min read

ArchitectureSecurityFastAPINext.jsMongoDBDev Notes
Playground Lab chess interface used as architecture case study visual

When Playground Lab started, the goal was never ‘just a game frontend and an API.’ The direction was to build a platform where new game and tool modules could grow without collapsing contracts, security boundaries, or operational clarity.

1) Why decoupled frontend and backend

  • Next.js frontend and FastAPI backend run as separate services with clear ownership boundaries (UX vs domain logic).
  • Deployment and runtime tuning remain independent, which keeps scaling and operational iteration cleaner.
  • The trade-off is additional proxy/auth and contract discipline overhead, accepted intentionally for long-term maintainability.

2) API trust model and internal headers

Sensitive flows are not based on browser-provided identity headers. Frontend route handlers read trusted session state server-side, then inject internal headers that the backend validates before honoring internal-only behavior.

Users never see this layer directly, but it removes an entire class of identity spoofing mistakes from the public request surface.

3) Security controls beyond authentication

  • Explicit CORS origin policy (no production wildcard).
  • Endpoint rate limiting with transparent response headers and retry signaling.
  • Admin operations guarded by both role checks and active admin-mode toggles.
  • Identity normalization to reject blank or ambiguous principals before domain logic executes.

4) Mongo lifecycle and fail-fast startup

  • Connect and verify DB health on boot.
  • Ensure module indexes for auth/games before serving traffic.
  • Seed and sync baseline catalog data where required.
  • Emit startup status for operational visibility.

If core dependencies are unhealthy at startup, the service does not continue in a partially healthy state. That behavior is intentional to avoid hidden runtime failure later.

5) Contract strategy across Python and TypeScript

Pydantic schemas remain validation-authoritative on the backend, while the frontend consumes aligned typed payloads. This keeps schema drift risk low as Wordle, Chess, tools, and account flows evolve together.

python

1@lru_cache(maxsize=1)
2def _load_ranked_words() -> tuple[list[str], str, bool, str | None]:
3    try:
4        ranked = top_n_list("en", EXTENDED_POOL_SIZE * 3)
5    except Exception as error:  # noqa: BLE001
6        logger.warning("wordfreq top_n_list failed; using fallback list error=%s", error)
7        return _fallback_result(type(error).__name__)
8
9    normalized = _normalize_words(ranked)
10    if not normalized:
11        logger.warning("wordfreq produced no valid 5-letter words; using fallback list")
12        return _fallback_result("empty-wordfreq-result")
13
14    return normalized, "wordfreq", False, None

6) Deployment split and delivery posture

  • Frontend on Vercel, backend on Railway, data in Atlas, edge/DNS through Cloudflare.
  • Docker Compose remains the local reproducibility anchor for environment parity.
  • Type safety is enforced as baseline quality posture: strict TypeScript + strict mypy mode.

7) Tricky points and practical lessons

  • Keeping admin visibility aligned with real server mode state.
  • Allowing guest continuity without crossing account-only boundaries.
  • Designing asynchronous behavior (especially chess bot turns) without state incoherence.
  • Making rate-limit behavior transparent and debuggable for client UX.

The most durable decision was treating architecture and security as product features from day one. The visible value is games and tools; the lasting value is clear trust boundaries and contracts that survive scope growth.