Full-featured Qwik + Fastify production site for alden.dev.
- ✅ Qwik 1.19.2 + Qwik City with SSR/SSG
- ✅ TypeScript 5.4.5 + Vite 7.3.1 multi-stage build (SSG blocker resolved)
- ✅ Tailwind CSS 4.2.1 with v4 engine
- ✅ Full routing: home, about, engineering, production, blog, contact, resume
- ✅ Dynamic routes for blog posts and project details
- ✅ Blog backed by PostgreSQL database (3 launch posts seeded and verified)
- ✅ Structured data (Person, WebSite, Article, CreativeWork schemas)
- ✅ Accessibility verified (skip links, landmarks, ARIA labels, motion preferences)
- ✅ Responsive design and mobile navigation
- ✅ Environment-based configuration with ORIGIN, PORT, HOST, DATABASE_URL
- ✅ Production Docker build with multi-stage optimization
- ✅ Static site generation (SSG) with full route prerendering
- Node.js 20+ (specified in Dockerfile)
- pnpm 8+ (package manager)
- PostgreSQL 12+ (if running blog locally; optional for development)
Create a .env file based on .env.example:
# Server configuration
ORIGIN=http://localhost:4173 # URL for SSR/prerendering
PORT=3000 # Fastify server port
HOST=0.0.0.0 # Bind address
NODE_ENV=production
# Database configuration (for blog functionality)
DATABASE_URL=postgresql://user:password@host:port/database # Optional; falls back to static data
# Private admin access (server-side HTTP Basic Auth)
ADMIN_BASIC_AUTH_USERNAME=admin
ADMIN_BASIC_AUTH_PASSWORD=change-me
# Contact form delivery (Amazon SES)
AWS_REGION=us-east-1
CONTACT_FORM_FROM_EMAIL=no-reply@example.com
CONTACT_FORM_TO_EMAIL=you@example.com
CONTACT_FORM_SUBJECT_PREFIX=Alden Gillespy WebsiteThe admin credentials are server-only values for the /admin route group. Do not expose them as
client-side environment variables or commit production secrets.
The contact form sends through Amazon SES on the server.
AWS_REGION: SES region for the verified sender identity.CONTACT_FORM_FROM_EMAIL: verified SES sender address or domain-backed mailbox used as theFromaddress.CONTACT_FORM_TO_EMAIL: mailbox where inquiries should be delivered.CONTACT_FORM_SUBJECT_PREFIX: optional prefix for inbound subject lines.
AWS credentials are normally supplied by your deployment platform, IAM role, container task role,
or local AWS profile rather than hard-coded in .env. For local manual testing, you can also set
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and optionally AWS_SESSION_TOKEN.
Development mode (with live reload):
pnpm install
pnpm run devThen visit http://localhost:5173/
Production build (static prerendering + SSG):
pnpm run buildThis executes:
build.types— TypeScript type checkingbuild.client— Vite client bundlelint— ESLint validationbuild.server— SSR bundle + Fastify adapter- SSG Phase — Prerender all routes to static HTML
Serving production build:
pnpm run serveThen visit http://localhost:3000/
- Minification: Currently disabled in production build
- Reason: Workaround for Qwik SSG initialization incompatibility with esbuild
- Impact: ~2-3x larger pre-gzip bundles; ~15-30 KB increase post-gzip
- Status: Stable and acceptable for v3.0.0; see Performance Audit for details
- Future: Re-enable after investigating esbuild/Qwik root cause
The blog system is database-backed (PostgreSQL) for content management:
- Schema:
blog_poststable with published flag, timestamps, markdown content - Connectivity: Environment-driven config; SSL/TLS support via
PGSSLMODE - Content: 3 launch posts seeded in PostgreSQL
- Routing: Dynamic
/blog/[slug]routes query database at build time (SSG enumeration) - SEO: Sitemap automatically includes published blog slugs from database
For local development without a database, the system can fall back to static content (configured in src/lib/db.ts).
This app has a minimal Fastify server implementation. The production server serves prerendered static HTML and handles dynamic requests (if any).
The build pipeline produces:
- Static HTML files (one per route)
- Versioned JS/CSS chunks with content hashing (immutable caching)
- Optimized async Qwik component bundles