This repository started as a successful job application challenge implementation of a frontend for the Star Wars API (SWAPI) and has been evolved into a production-oriented (yet not fully production ready) Angular monorepo template optimized for responsive server-first rendering and ready for custom APIs and services.
Currently the SWAPI is down due to a expired TLS certificate, but data is mocked with an artificial request delay at the moment until I created my own backend.
-
The first request can take longer, as the fly.io machine will automatically suspend if there was no traffic for a while.
- Modern Angular architecture with standalone components, zoneless change detection, and lazy-loaded routes
- Hybrid rendering setup with CSR, SSR, and SSG in one codebase
- Device-aware server-first routing using Client Hints + URL device context parameters
- Strict environment/schema validation and shared typed contracts across packages
- Defensive third-party API integration for SWAPI with explicit DTO-to-model mapping
- Docker-ready build and runtime setup
| Package | Responsibility |
|---|---|
packages/client |
Angular app (browser + server entry, routes, pages, UI blocks/components) |
packages/server |
Hono host for security checks, device context handling, redirects, SSG file serving, SSR fallback |
packages/shared |
Shared runtime/types, routing constants, device context schema, code generators |
- Validate host/protocol (
SWAPI_ALLOWED_HOSTS, target-aware HTTP/HTTPS checks). - Read device context from Client Hints headers (
Sec-CH-UA-*, viewport hints). - Normalize URL to a device-context prefix segment like
r;format=mobile;width=768. - Serve static assets from the built browser output.
- Serve prerendered HTML (SSG) when a matching file exists.
- Fall back to Angular SSR for all non-prerendered HTML routes.
- Static prerendered paths are generated from
@swapi/shared/routing/ssg-paths. - Currently includes:
errorhomewith all generated device-context variants
- At the moment this results in
98prerendered routes in total.
- Routes:
home,movies,movie/:id,characters,character/:id,planets,planet/:id,error - SWAPI resources: Films, People, Planets
- HTTP retry interceptor and explicit SWAPI DTO/model mapping layer
- Responsive & a11y friendly UI
- The search input in the header is only UI demonstration, and has no functionality.
- DeviceService with route-aware and browser-aware breakpoint handling
- Clear client/server request behavior for stable, predictable runtime behavior.
- Automatic cancellation of obsolete in-flight requests during fast navigation to reduce unnecessary backend load.
- CDN-ready caching model with explicit cache headers and cache tagging support.
- Consistent HTTP error and timeout responses with cache-safe semantics.
- Isolated SSR execution in a worker pool with controlled concurrency and bounded queueing.
- Worker recovery and runtime metrics for observability and operational diagnostics.
- Graceful shutdown with in-flight request draining for safer deploys and restarts.
SWAPI is intentionally integrated defensively because of schema and data inconsistencies.
- See details in docs/swapi.md
- Includes known API behavior differences, mapping strategy, and fallback decisions
As of May 2026, the public swapi.dev API is unstable due to an expired TLS certificate.
To keep the app reliably usable across environments, the client currently serves SWAPI responses from local mock data.
- Mock source: SWAPI fixture data (
Juriy/swapi) transformed to SWAPI-compatiblefilms,people, andplanetsAPI responses - Activation:
SWAPI_USE_MOCK=truein client environment files - Mock transport behavior: includes an artificial per-request delay (
150-450ms) for both success and404responses
This is a temporary fallback until the app is switched to its own backend.
-
Node.js
Version: see .node-version
It's recommended to have a node version manager compatible with .node-version set up for automatic installation and update of the Node.js version used in the project.
-
Bun
Version: see .bun-version but any works, as it is installed and only run from project dependencies.
Only necessary to install the dependencies. The bun binary that is to be used for in the project will be installed via
package.jsonand is used throughout the tasks viabunx bunin order to align the bun version for every developer and with the production runtime environment (a fixed version of oven/bun docker image is used). -
direnv
-
Taskfile
It's recommended to install Taskfile on the system plus setting up shell completion.
-
Fly.io CLI
Only if you intend to deploy from your local environment instead of CI.
cp ./Taskfile.template.yml ./Taskfile.yml
task iLocal URLs:
- App's vite server in development mode:
http://localhost:4200 - App's vite server in production mode:
http://localhost:4300 - App's hono server in development mode:
http://localhost:50000 - App's hono server in production mode:
http://localhost:51000 - App's cloudflare worker:
http://localhost:52000
Use T_BUILD_LEVEL and T_TARGET_ENVIRONMENT explicitly when needed:
T_BUILD_LEVEL:development,releaseT_TARGET_ENVIRONMENT:local,ci,testing,production
Taskfiles derive the app environment variables from the Build Matrix variables:
T_BUILD_LEVEL:SWAPI_BUILD_LEVELT_TARGET_ENVIRONMENT:SWAPI_TARGET_ENVIRONMENT
Taskfiles load the remaining environment variables via dotenv from .env files like:
packages/client/.env/.env.<T_TARGET_ENVIRONMENT>.<T_BUILD_LEVEL>packages/server/.env/.env.<T_TARGET_ENVIRONMENT>.<T_BUILD_LEVEL>- Docker runtime variants additionally use
packages/server/.env/.env.<T_TARGET_ENVIRONMENT>.<T_BUILD_LEVEL>.docker
See the project documentation in the docs folder.
See the project roadmap documents in the docs/roadmap folder.