Skip to content

kamkie/stackverse

Repository files navigation

Stackverse

CI CodeQL codecov

One app, every stack.

Stackverse is a single, deliberately non-trivial product — a bookmark manager — implemented multiple times across different languages, frameworks, and runtimes. Every implementation conforms to the same contract, so you can put any frontend in front of any gateway in front of any backend and the app still works.

The scope is small but exercises what real services need: OIDC auth, hierarchical role-based authorization, ownership rules, validation, pagination and filtering, runtime-managed internationalization (localized messages served and edited through the API), HTTP caching with ETag revalidation, API versioning with a live v1 → v2 migration (offset → cursor pagination, RFC-compliant deprecation headers), and a backoffice — moderation queue with a report state machine, app-level user blocking, an append-only audit trail, and a stats dashboard.

Why

  • Compare stacks on equal footing. Same spec, same architecture, same author, same conventions — and the same design across all frontends (shared stylesheet, no UI frameworks), so differences you see are differences between the stacks, not between authors or themes.
  • A reference for mentees. "How do I structure a Spring Boot service?" or "how does session auth work with a BFF?" — point at a complete, working, consistent example.
  • Production-shaped, not toy-shaped. Stateless services, gateway-owned sessions, OIDC, containers, migrations, health checks. Small scope, real architecture.

Full intent, goals, and non-goals: docs/INTENT.md

Why not RealWorld?

RealWorld is great, but its implementations are community-contributed — wildly inconsistent in quality and mostly unmaintained — and its spec mandates JWTs held by the SPA. Stackverse is single-author, consistent across stacks, and built on the BFF / token-handler pattern: the browser only ever sees a session cookie, tokens never leave the server side.

Architecture

Stateless applications; the session lives at the edge.

flowchart LR
    B[Browser UI<br/>client-routed SPA or Astro islands<br/>session cookie only] <-->|same-origin HTTP or HTTPS| G[Gateway / BFF<br/>interchangeable implementation]
    G <-->|pages and static assets| F[Frontend upstream<br/>static server or dev server]
    G -->|API proxy; optional Bearer JWT| A[Backend API<br/>stateless]
    G <-->|OIDC code flow| K[Keycloak]
    G <-->|session store| R[(Redis)]
    A --> P[(PostgreSQL)]
    A -->|JWT validation| K
Loading
  • The gateway is the OIDC client. It handles login/logout, keeps the session (cookie ↔ Redis), and relays the access token to the backend on each proxied request.
  • The backend is fully stateless: it validates the bearer JWT against the IdP and serves the API. Any instance can serve any request.
  • The frontend is a browser application served through the gateway. It may be a client-routed SPA or a static multi-page build with hydrated islands; either way it knows nothing about tokens. It makes same-origin /api/* calls (the browser attaches the session cookie) and asks /auth/session who is logged in.

Details: docs/ARCHITECTURE.md

The contract

Every implementation must satisfy:

The contract is executable: the black-box suite in conformance/ runs any backend through the API rules above (./scripts/conformance.sh), and the e2e/ suite drives any composed stack through the UI. Those two suites are the canonical acceptance gates. Additional testing-tool examples live under testing/ as showcase variants: they compare tools and representative workflows without replacing or expanding the canonical gates. The Schemathesis showcase in testing/schemathesis-api/ generates bounded OpenAPI property tests against a running backend, the Robot Framework showcase in testing/robot-acceptance/ demonstrates keyword-driven UI acceptance tests against a running stack, the Selenium showcase in testing/selenium-e2e/ and the Cypress showcase in testing/cypress-e2e/ drive representative browser flows through the composed stack, the Bruno showcase in testing/bruno-api/ provides a curated API-client collection for direct-backend exploration, the k6 showcase in testing/k6-system/ runs system smoke and light-load checks against a composed stack without making benchmark claims, the axe-core showcase in testing/axe-a11y/ runs automated accessibility checks against representative browser states, the OWASP ZAP showcase in testing/zap-security/ runs a passive baseline security smoke scan against a running gateway, the Hurl showcase in testing/hurl-api/ documents representative HTTP API flows as executable plain text, the Postman showcase in testing/postman-api/ provides a Newman/Postman CLI collection for representative API workflows, and the Tracetest showcase in testing/tracetest-otel/ asserts that one gateway API action produces a single trace spanning gateway and backend spans. CI runs affected implementation builds and contract-suite legs on pull requests, then keeps the full sweep on main, scheduled, and manual runs. Trusted main-branch jobs also submit build-time dependency snapshots where CI can provide a richer resolved graph than static manifest parsing (see docs/RUNNING.md).

Implementation matrix

Component Stack Directory Status Coverage
Backend Spring Boot (Kotlin) backends/spring-kotlin ✅ done coverage
Backend Spring Boot (Java) backends/spring-java ✅ done coverage
Backend Ktor (Kotlin) backends/ktor-kotlin ✅ done coverage
Backend ASP.NET Core (C#) backends/dotnet ✅ done coverage
Backend Elixir Phoenix backends/elixir-phoenix ✅ done coverage
Backend Go (stdlib + chi) backends/go ✅ done coverage
Backend Go (Echo) backends/go-echo ✅ done coverage
Backend Grails (Groovy) backends/grails ✅ done coverage
Backend Micronaut (Java) backends/micronaut-java ✅ done coverage
Backend Node.js (TypeScript) backends/node-ts ✅ done coverage
Backend Node.js (NestJS) backends/node-nestjs ✅ done coverage
Backend Open Liberty Java (Maven) backends/open-liberty-java ✅ done coverage
Backend PHP Laravel backends/php-laravel ✅ done coverage
Backend Python Django + DRF backends/python-django ✅ done coverage
Backend Python FastAPI backends/python-fastapi ✅ done coverage
Backend Play Framework (Scala) backends/play-scala ✅ done coverage
Backend Scala http4s backends/scala-http4s ✅ done coverage
Backend Quarkus Java (Maven) backends/quarkus-java ✅ done coverage
Backend Rust Axum backends/rust-axum ✅ done coverage
Backend Ruby on Rails API backends/ruby-rails ✅ done coverage
Gateway Spring Cloud Gateway (Kotlin) gateways/spring-cloud-gateway ✅ done coverage
Gateway Go (stdlib + chi) gateways/go ✅ done coverage
Gateway Node.js Fastify gateways/node-fastify ✅ done coverage
Gateway OpenResty (nginx + Lua) gateways/openresty ✅ done coverage
Gateway Apache APISIX gateways/apisix ✅ done coverage
Gateway Python Starlette gateways/python ✅ done coverage
Gateway Rust Axum gateways/rust ✅ done coverage
Gateway YARP (ASP.NET Core) gateways/yarp ✅ done coverage
Frontend React frontends/react ✅ done coverage
Frontend Angular frontends/angular ✅ done coverage
Frontend Astro frontends/astro ✅ done coverage
Frontend Lit (Web Components) frontends/lit ✅ done coverage
Frontend Qwik frontends/qwik ✅ done coverage
Frontend SolidJS frontends/solid ✅ done coverage
Frontend Svelte frontends/svelte ✅ done coverage
Frontend Vanilla TypeScript frontends/vanilla-ts ✅ done coverage
Frontend Vue frontends/vue ✅ done coverage

Line counts per variant — split into source / tests / config / docs, with a language breakdown and Dockerfile size — are sorted by ascending source LOC within each component, using implementation path as the deterministic tie-breaker. The generated tables live in docs/CODE-STATS.md. Files come from git ls-files, counts from tokei; the single generator is tools/code-stats.mjs. Regenerate with ./scripts/code-stats.sh --write docs/CODE-STATS.md (PowerShell: ./scripts/code-stats.ps1 -Write docs/CODE-STATS.md).

Quickstart

All run modes (frontend-only dev, full stack, observability, logs) are covered in docs/RUNNING.md; the short version:

Infrastructure only (PostgreSQL, Redis, Keycloak with a pre-imported realm):

docker compose up -d

Full stack — pick any combination via env vars:

BACKEND_IMAGE=stackverse/backend-spring-kotlin:local \
GATEWAY_IMAGE=stackverse/gateway-yarp:local \
FRONTEND_IMAGE=stackverse/frontend-react:local \
docker compose --profile app up

or, building the images first: BUILD=1 ./scripts/run-stack.sh (PowerShell: ./scripts/run-stack.ps1 -Build).

For development — infra in Docker, each module as a hot-reloading dev process in its own terminal tab, logs tee'd to .logs/ — use ./scripts/dev-stack.sh (PowerShell: ./scripts/dev-stack.ps1). With any stack running, the end-to-end suite drives the real app through every required screen: ./scripts/e2e.sh (PowerShell: ./scripts/e2e.ps1). With just infra and a backend, ./scripts/conformance.sh (PowerShell: ./scripts/conformance.ps1) checks that backend against the API contract directly. For optional OpenAPI property fuzzing against the same running backend, use ./scripts/schemathesis-api.sh (PowerShell: ./scripts/schemathesis-api.ps1). For a curated API-client walkthrough of the same direct-backend surface, run the Bruno showcase from testing/bruno-api. For optional k6 system smoke and light-load checks against a running composed stack, use ./scripts/k6-system.sh (PowerShell: ./scripts/k6-system.ps1). For a readable Hurl API showcase against the same backend target, use ./scripts/hurl-api.sh (PowerShell: ./scripts/hurl-api.ps1). For an optional passive security smoke scan against a running gateway, use ./scripts/zap-security.sh (PowerShell: ./scripts/zap-security.ps1). For optional trace-based observability assertions against a composed stack with OpenTelemetry enabled, use ./scripts/tracetest-otel.sh (PowerShell: ./scripts/tracetest-otel.ps1). For an optional Postman/Newman API showcase, run yarn test from testing/postman-api against the same direct backend target.

To populate a small repeatable local dataset for demos, run the seed against the running backend API:

./scripts/seed-test-data.sh
./scripts/seed-test-data.ps1

The seed uses the dev Keycloak users and the public API, so it works with any backend implementation. It is idempotent by seed title and leaves one open report plus one hidden bookmark for the backoffice screens. Wipe local data with docker compose down -v before recreating the stack.

Then open http://localhost:8000 and log in as demo / demo (regular user), moderator / moderator (reports queue, dashboard), or admin / admin (full backoffice).

Service URL
App (gateway) http://localhost:8000
Keycloak admin http://localhost:8180 (admin / admin)
PostgreSQL localhost:5432 (stackverse / stackverse)

Repository layout

spec/          the OpenAPI contract
docs/          functional spec, architecture
backends/      one directory per backend implementation
gateways/      one directory per gateway implementation
frontends/     one directory per frontend implementation
conformance/   black-box API contract suite, run directly against any backend
e2e/           black-box Playwright suite for any composed stack
testing/       optional testing-tool showcase suites and their conventions
infra/         shared infrastructure config (Keycloak realm, ...)
scripts/       build/run/test helpers, each as a .ps1 + .sh pair
tools/         implementation-neutral developer helpers used by scripts
compose.yaml   infra + pluggable app combination
.github/       CI + CodeQL workflows, Dependabot config

License

MIT

About

One app, every stack - the same bookmark manager implemented across multiple backends, gateways, and frontends under a single contract (BFF session auth, i18n, moderation, API versioning)

Topics

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors