Full-stack analytics dashboard consuming the public SpaceX API. Provides real-time mission tracking, fleet analytics, Starlink constellation monitoring, cost analysis, emissions estimates, and more — all through interactive D3.js visualizations.
Built as a technical assessment for the Full-Stack Engineer role at QuadSci.ai.
Browser ──▶ Nginx (SSL + Vue 3 SPA) ──▶ FastAPI Backend ──▶ Redis Cache ──▶ SpaceX API v4/v5
| Layer | Technology | Purpose |
|---|---|---|
| Frontend | Vue 3 + TypeScript + Vite + D3.js | SPA with interactive charts and visualizations |
| Backend | FastAPI (Python 3.11+) | API gateway, data aggregation, caching |
| Cache | Redis 7 | TTL-based cache with stampede prevention |
| SSL | Let's Encrypt + Nginx | HTTPS with auto-renewal |
| Containers | Docker + Docker Compose | Single-command deployment |
| CI/CD | GitHub Actions | Lint, type-check, test, deploy on push to main |
https://quadsci.juantoledo.com.mx
docker compose up --build| Service | URL |
|---|---|
| Frontend | http://localhost |
| API | http://localhost:8000 |
| API Docs | http://localhost:8000/docs |
See the dedicated READMEs for each project:
spacex-dashboard/
├── backend/ # FastAPI REST API (Python 3.11+)
│ ├── app/
│ │ ├── api/ # Route handlers (14) + middleware + rate_limit
│ │ ├── cache/ # Redis cache with stampede prevention
│ │ ├── clients/ # SpaceX API HTTP client with retry
│ │ ├── schemas/ # Pydantic models (14 modules)
│ │ ├── services/ # Business logic layer (15 modules)
│ │ ├── utils/ # Shared calculations
│ │ ├── config.py # Environment-based settings
│ │ └── main.py # Application entry point
│ └── tests/ # 208 Pytest tests (96% coverage)
├── frontend/ # Vue 3 SPA (TypeScript strict)
│ ├── src/
│ │ ├── api/ # Axios HTTP client (17 functions)
│ │ ├── assets/ # Global CSS, design tokens, transitions
│ │ ├── components/
│ │ │ ├── charts/ # 13 D3.js visualizations
│ │ │ ├── common/ # 10 reusable UI components
│ │ │ └── layout/ # Header, footer, progress bar, space background
│ │ ├── router/ # 11 lazy-loaded routes
│ │ ├── stores/ # 7 Pinia stores
│ │ ├── types/ # TypeScript interfaces
│ │ ├── utils/ # Shared utilities
│ │ ├── views/ # 11 page components
│ │ └── __tests__/ # 242 Vitest tests (47 files)
│ └── package.json
├── .github/workflows/
│ ├── ci.yml # Lint, type-check, test, coverage, security audit
│ └── deploy.yml # Deploy to EC2 via SSH with rollback
├── .pre-commit-config.yaml # Ruff + ESLint + file hygiene hooks
├── docker-compose.yml # API + Nginx + Redis with health checks
└── .env.example
| Section | Description |
|---|---|
| Overview | Mission stats, launch cadence, next/latest mission, site performance, cross-domain recommended actions |
| Launches | Filterable launch log with pagination, year-by-year breakdown |
| Launch Detail | Individual mission page with crew, payloads, cores, image gallery |
| Fleet | Rocket showcase with specs, recovery stats, and booster reuse tracking |
| Rocket Detail | In-depth rocket page with stages, dimensions, and image gallery |
| Starlink | Interactive 3D globe, satellite positions, version distribution |
| Economics | Cost per kg to LEO, annual spend trends, spend by vehicle |
| Emissions | Estimated CO2 by vehicle, annual trends, fuel breakdown, reuse savings |
| History | Interactive timeline of major SpaceX milestones |
| Landing | Landing pad map with clustering, pad comparison, success rates |
| Starman | Roadster live telemetry, orbital diagram, Mars distance tracker |
Additional features:
- Recommended Actions — cross-domain actionable recommendations (e.g., fleet + economics + emissions) with action type, priority badges, and domain pills. AI-generated when available, deterministic fallback otherwise
- AI Chat Assistant with markdown-formatted responses and typewriter effect
- AI Fun Fact — a random SpaceX curiosity shown on each site visit
- Dark / Light theme with smooth transitions
- Fully responsive layout (mobile, tablet, desktop) with safe-area support for iPhone
- Animated space background with twinkling stars and shooting stars
- Image lightbox for all galleries
- Error Testing Lab — Easter egg to demonstrate error handling (triple-click SPACEX in header)
The dashboard includes an Error Testing Lab to demonstrate how errors are handled across the stack. Useful for QA, demos, and onboarding.
How to open: Triple-click the "SPACEX" brand in the header (three clicks within ~500 ms).
What it demonstrates:
- ErrorState — Inline error panel with RETRY button (used across all views)
- NotificationToast — Toast notifications for non-blocking errors
- API errors — Real HTTP responses (404, 500, 502, 503, timeout) from the backend
The backend endpoint GET /api/dev/trigger-error?code=404|500|502|503|timeout returns simulated errors. The timeout variant waits 5 seconds before responding. See Frontend README and Backend README for details.
| Method | Path | Description |
|---|---|---|
| GET | /health |
Health check (Redis connectivity) |
| GET | /api/dashboard |
Aggregated metrics and insights |
| GET | /api/rockets |
All rockets with launch stats |
| GET | /api/rockets/{id} |
Single rocket detail |
| GET | /api/launches |
Paginated launches (filterable) |
| GET | /api/launches/{id} |
Single launch detail |
| GET | /api/starlink |
Paginated Starlink satellites |
| GET | /api/starlink/positions |
Satellite positions for globe |
| GET | /api/starlink/stats |
Starlink aggregate statistics |
| GET | /api/cores/stats |
Fleet and booster reuse statistics |
| GET | /api/launchpads |
All launch sites with performance |
| GET | /api/economics |
Cost analysis and spend data |
| GET | /api/emissions |
Emissions estimates by vehicle |
| GET | /api/history |
Historical milestones |
| GET | /api/landing |
Landing pad data and statistics |
| GET | /api/roadster |
Roadster / Starman live telemetry |
| GET | /api/ai/status |
Check if AI features are available |
| POST | /api/ai/chat |
AI chat with SpaceX data context |
| GET | /api/ai/fun-fact |
Random AI-generated SpaceX fun fact |
| GET | /api/notifications/stream |
Server-Sent Events stream for real-time notifications |
| POST | /api/notifications/send |
Send test notification (admin/internal) |
| GET | /api/dev/trigger-error |
Easter egg: simulated errors (404, 500, 502, 503, timeout) for error-handling demos |
For detailed query parameters and response schemas, see the Backend README.
The dashboard integrates AI through three features, powered by Groq (free tier, no credit card required):
Recommended Actions — The Overview page's "Recommended Actions" section generates cross-domain actionable recommendations by analyzing data from all domains simultaneously (missions, fleet, economics, emissions, Starlink, landing). Each recommendation proposes a specific action (optimize, investigate, scale, reduce, or monitor), includes a priority level (high/medium/low), and indicates which data domains it cross-references. When AI is enabled, a "Smart Actions" badge appears; if not, the system generates equally useful deterministic actions using rule-based cross-domain analysis.
AI Chat Assistant — A floating chat button (bottom-right corner) opens a conversational interface with markdown-formatted responses (bold, lists, paragraphs) and a typewriter effect. Users can ask natural language questions about any SpaceX data — missions, rockets, Starlink, costs, emissions, landing operations, historical milestones, launch sites, and even the Tesla Roadster's current position. The AI has full context of all dashboard data sources and maintains conversation history for follow-up questions.
Fun Fact on Load — Each time the site is opened, a subtle banner appears below the header with a short AI-generated curiosity about SpaceX (e.g., a surprising statistic or record). The banner auto-dismisses after 12 seconds and can be closed manually.
All three features follow a graceful degradation pattern: the dashboard works perfectly without an AI API key. To enable AI, add a free Groq key to your environment:
GROQ_API_KEY=your_key_hereGet a free key at console.groq.com.
The AI service uses a context builder (_build_data_context) that assembles a compact text summary of all SpaceX program data before sending it to the LLM. The context includes:
- Missions — Total launches, success rate, launches by year
- Fleet — Active boosters, landings, landing success rate
- Starlink — Total satellites tracked
- Rockets — Per-rocket launch stats and success rates
- Emissions — CO2 by vehicle, annual trends, fuel breakdown, reuse savings
- Economics — Spend by vehicle, annual trends, top customers, mass by orbit
- History — Last 15 milestones
- Landing — Pad stats, RTLS vs ASDS comparison
- Launch sites — Launchpad performance
- Roadster — Starman telemetry (speed, distance, orbit)
When generate_ai_insights is called from the dashboard, it receives prefetched data (rockets, launches, starlink stats, fleet, launchpads) already fetched for the Overview page, avoiding redundant service calls. For chat and fun-fact, the context builder fetches fresh data from the cached services. All data is formatted with markdown-style section headers for the LLM to parse.
| Resource | TTL | Rationale |
|---|---|---|
| Rockets | 24 h | Rarely changes |
| Launches | 2 min | Updates frequently around launch events |
| Starlink | 5 min | Large dataset, moderate update rate |
| Dashboard | 5 min | Full response cached; avoids re-aggregation |
| Launch latest | 2 min | Latest completed launch |
| Launch next | 2 min | Next scheduled launch |
| Economics | 5 min | Derived from launches and rockets |
| Emissions | 5 min | Derived from launches and rockets |
| Cores | 24 h | Changes infrequently |
| Launchpads | 24 h | Static infrastructure data |
| History | 24 h | Historical events, rarely updated |
| Landing | 24 h | Pad data changes infrequently |
| Roadster | 24 h | Telemetry updates slowly |
Stampede prevention: On cache miss, a Redis SET NX lock is acquired before calling the SpaceX API. Concurrent requests wait briefly and read from cache once it is populated, avoiding redundant upstream calls.
- FastAPI over Flask — Native async I/O for concurrent SpaceX API and Redis calls. Auto-generated OpenAPI docs. Type safety with Pydantic.
- Redis-only (no SQL database) — All data is sourced from the SpaceX API and cached in Redis. No persistent storage is needed since the upstream API is the source of truth.
- TypeScript (strict mode) — Full frontend type coverage with no
anytypes. Interfaces mirror backend Pydantic schemas for end-to-end type safety. - D3.js over charting libraries — Full control over rendering, interactivity, and custom visualizations (3D globe, orbital diagrams, animated maps).
- Monorepo — Single repository for backend and frontend simplifies CI, Docker Compose, and code review.
- Cross-domain recommendations — The Insight model supports
action_type,priority, anddomainsfields. Both AI and deterministic paths produce actionable recommendations that cross-reference multiple data domains, using the same schema for graceful degradation.
The project maintains strict quality standards enforced through automated tooling and continuous integration.
| Metric | Value |
|---|---|
| Total tests | 450 (208 backend + 242 frontend) |
| Backend coverage | 96% (enforced minimum: 90%) |
| Backend lint errors | 0 (Ruff check + format) |
| Frontend lint errors | 0 (ESLint, 1 intentional warning for v-html in AI chat markdown) |
| Python module docstrings | 48/48 (100%) |
| Test files (frontend) | 47 covering views, stores, charts, components, utils, router |
Automated enforcement:
- Backend:
pytest --cov-fail-under=90rejects PRs below the coverage threshold - Linting: Ruff (Python) and ESLint (TypeScript/Vue) run in CI and via pre-commit hooks
- Security:
pip-auditandnpm auditscan dependencies on every CI run - Pre-commit hooks:
.pre-commit-config.yamlruns Ruff, ESLint, trailing-whitespace fixes, YAML validation, and large-file checks before each commit
The frontend is optimized for fast initial load and smooth navigation.
Overview load optimizations (backend):
- Parallel fetches — Rockets, launches, Starlink stats, fleet, and launchpads are fetched concurrently via
asyncio.gather, reducing cold-cache load time by ~40–60% - Prefetched AI context — When generating insights for the Overview, the AI service receives data already fetched for the dashboard instead of re-fetching, saving ~3–8 seconds
- Dashboard cache — The full
/api/dashboardresponse is cached (5 min TTL); subsequent loads return in ~50–200 ms - Latest/next cache — Latest and next launch endpoints are cached (2 min TTL) to avoid repeated SpaceX API calls
- Starlink single request — Starlink data uses
pagination: falsefor one API call instead of 12+ paginated requests
Lazy loading:
- All 11 route views use dynamic
import()for code splitting AiChatandFunFactcomponents are loaded viadefineAsyncComponent, removing them from the critical path and eliminating 2 API calls from the initial renderSpaceBackgroundcanvas animation is deferred viarequestIdleCallbackso it does not compete with content rendering
Navigation UX:
RouteProgressBar— A slim accent-colored progress bar at the top of the viewport that animates during route transitions, providing visual feedback for lazy-loaded viewsview-fadetransitions — Content fades in smoothly (250ms) when switching between views
Loading states:
SkeletonLoadersupports 8 layout variants (rect,circle,text,card,stat,table,chart,hero) so each view's loading state mirrors its actual content structure- All 11 views use view-specific skeleton layouts that replicate headers, stat cards, charts, and tables
Multiple layers of security are applied across the stack.
Rate limiting:
- Global middleware limits all
/api/endpoints to 120 requests per minute per IP address (Redis-backed) - AI endpoints have additional per-route limits: chat (20/min), fun-fact (10/min)
Input validation:
- All API inputs are validated through Pydantic schemas with strict types
- AI chat messages are sanitized via a
field_validatorthat enforces a 2,000 character limit and strips common prompt-injection patterns (ignore previous instructions,system:, etc.)
Infrastructure:
- HTTPS enforced via Nginx with HSTS, X-Frame-Options, X-Content-Type-Options, Content-Security-Policy, and Permissions-Policy headers
- Docker containers run as non-root users
.envfiles are excluded from version control via.gitignorepip-auditandnpm auditrun in CI to catch vulnerable dependencies.well-known/security.txtprovides a security contact endpoint
This project was iteratively evaluated and improved through a structured AI-assisted quality audit. The process consisted of three rounds of expert evaluation, each followed by targeted improvements until all categories reached production-grade standards.
| Category | Score | Key Evidence |
|---|---|---|
| Architecture & Structure | 9.5 | Clean layered architecture, 48/48 module docstrings |
| Backend Quality | 9.8 | Null-safe Redis cache, typed function signatures, structured logging |
| Frontend Quality | 9.7 | Focus trap, 8 skeleton variants, aria-hidden on all decorative elements |
| Tests & Coverage | 9.8 | 445 tests, 96% backend coverage, 47 frontend test files covering all views/charts |
| DevOps & CI/CD | 9.8 | Docker healthchecks, coverage enforcement, security scanning, pre-commit hooks |
| Security | 9.7 | Global rate limiting, prompt-injection sanitization, security.txt, HSTS/CSP headers |
| Documentation | 9.5 | 3 README files (project, backend, frontend), API reference, deployment guide |
| UX/UI & Accessibility | 9.8 | Skip-to-content, focus trap, ARIA on 13 charts, route progress bar, view transitions |
| Performance | 9.0 | 92% bundle reduction, lazy-loading, deferred animations, chunk splitting |
| Maintainability | 9.7 | 100% docstrings, pre-commit hooks, coverage thresholds, CI hardening |
Overall: 9.73 / 10
These numbers are not estimates — they are the output of automated tooling:
Backend: 208 tests passed | 96.12% coverage | 0 Ruff errors | 80 files formatted
Frontend: 242 tests passed | 47 test files | 0 ESLint errors | 0 type errors
Bundle: 12.94 KB main | 105 KB vendor-vue | 88 KB vendor-d3 (all gzipped ~50% smaller)
The project is deployed at https://quadsci.juantoledo.com.mx using GitHub Actions for CI/CD.
Push to main → GitHub Actions (CI: lint, test, build) → SSH to EC2 → docker compose up --build
| Component | Details |
|---|---|
| Instance | EC2 c7i-flex.large (2 vCPU, 4 GB RAM) — Amazon Linux 2023 |
| SSL | Let's Encrypt (auto-renewal via certbot) |
| Domain | quadsci.juantoledo.com.mx (DNS A record → Elastic IP) |
| Containers | API (512 MB), Nginx (256 MB), Redis (256 MB) |
-
EC2 instance — Create an instance with Amazon Linux 2023, 20 GB storage, and ports 22/80/443 open. Install Docker, Docker Compose plugin, and Docker Buildx.
-
Elastic IP — Assign an Elastic IP for a fixed public address.
-
SSL certificate — Run
sudo certbot certonly --standalone -d your-domainon the instance. The Nginx container mounts/etc/letsencryptfor HTTPS. -
GitHub Secrets — Add the following in the repository's Settings > Secrets > Actions:
| Secret | Description |
|---|---|
EC2_HOST |
Elastic IP or public IP of the instance |
EC2_USER |
ec2-user (Amazon Linux) or ubuntu (Ubuntu) |
EC2_SSH_KEY |
Full contents of the .pem key file |
GROQ_API_KEY |
Groq API key for AI features |
- Deploy — Push to
mainor trigger manually via the Actions tab. The workflow runs CI checks first, then SSHs into the instance, pulls the latest code, and rebuilds all containers.
- Docker and Docker Compose (for containerized and production deployment)
- Python 3.11+ (for local backend development)
- Node.js 20+ (for local frontend development)