A modern real estate platform built with Next.js and deployed on Cloudflare Workers.
Live demo: earth-and-home-nextjs.denniskinuthiaw.workers.dev
Layer | Technology |
|---|---|
Framework | Next.js 16 (App Router, Turbopack, React Compiler) |
Language | TypeScript (strict) |
UI | Tailwind CSS v4 + DaisyUI v5 + shadcn/ui |
Database | Cloudflare D1 (SQLite) via Drizzle ORM |
Auth | Better Auth (Google OAuth) |
Storage | Cloudflare R2 (media) + Cloudflare Images |
Cache | R2 Incremental Cache with Regional Cache |
State | TanStack Query |
Deployment | Cloudflare Workers via OpenNext |
pnpm add -g wrangler)wrangler login)1pnpm install
Create a .env file:
1NODE_ENV=development2BETTER_AUTH_URL=http://localhost:30103NEXT_PUBLIC_BETTER_AUTH_URL=http://localhost:30104BETTER_AUTH_SECRET=<generate-a-random-secret>5GOOGLE_CLIENT_ID=<your-google-oauth-client-id>6GOOGLE_CLIENT_SECRET=<your-google-oauth-client-secret>
1pnpm db:migrate:local
1pnpm dev
Open http://localhost:3010.
Script | Description |
|---|---|
| Start dev server (Turbopack, port 3010). Runs local D1 migrations automatically. |
| Production build |
| Lint with oxlint |
| Format with oxfmt |
Script | Description |
|---|---|
| Generate Drizzle migration files from schema changes |
| Apply migrations to local D1 |
| Apply migrations to production D1 |
| Open Drizzle Studio for local DB |
| Regenerate Better Auth schema after plugin changes |
Script | Description |
|---|---|
| Build and preview locally in Workers runtime |
| Build and deploy to Cloudflare Workers |
| Build and upload without making live |
| Generate |
Script | Description |
|---|---|
| Bundle size analysis |
| Lighthouse audit against localhost |
The app requires these Cloudflare resources (configured in wrangler.jsonc):
Resource | Binding | Name |
|---|---|---|
D1 Database |
|
|
R2 Bucket (cache) |
|
|
R2 Bucket (media) |
|
|
Cloudflare Images |
| — |
Worker Self-Reference |
Create them if deploying to your own account:
1wrangler d1 create earth-and-home-db2wrangler r2 bucket create earth-and-home-nextjs-opennext-cache3wrangler r2 bucket create earth-and-home-media
Update the database_id in wrangler.jsonc with the ID returned by the D1 create command.
Build-time public variables go in .env.production:
1NEXT_PUBLIC_BETTER_AUTH_URL=https://your-worker.workers.dev
NEXT_PUBLIC_* variables are inlined at build time by Next.js. They do not work as Cloudflare runtime secrets.
Runtime secrets are set via Wrangler CLI:
1wrangler secret put BETTER_AUTH_SECRET2wrangler secret put BETTER_AUTH_URL3wrangler secret put GOOGLE_CLIENT_ID4wrangler secret put GOOGLE_CLIENT_SECRET
1# Apply any pending D1 migrations to production2pnpm db:migrate:remote34# Build and deploy5pnpm run deploy
The deploy script automatically stubs the @vercel/og WASM file (~2 MB) to stay within the 3 MB Worker size limit, then restores it after upload.
NEXT_PUBLIC_BETTER_AUTH_URL in .env.production and BETTER_AUTH_URL secret to match1├── drizzle/ # D1 migration files2├── scripts/3│ ├── set-production-secrets.js # Bulk secret management4│ ├── list-production-secrets.js # List configured secrets5│ └── stub-resvg-wasm.sh # WASM stub for bundle size6├── src/7│ ├── app/ # Next.js App Router pages & API routes8│ ├── components/9│ │ ├── common/ # Reusable UI components10│ │ ├── dashboard/ # Dashboard feature components11│ │ ├── theme/ # Theme provider & toggle12│ │ └── ui/ # shadcn/ui primitives13│ ├── config/ # Site configuration14│ ├── data-access-layer/ # Server-side data fetching15│ ├── db/16│ │ └── schema/ # Drizzle schema definitions17│ ├── hooks/ # React hooks18│ ├── lib/19│ │ ├── auth/ # Better Auth client & server20│ │ ├── db/ # D1 database connection21│ │ └── tanstack/ # TanStack Query setup22│ ├── services/ # Domain-specific API services23│ └── types/ # TypeScript type definitions24├── middleware.ts # Edge middleware (auth guard)25├── open-next.config.ts # OpenNext caching config26├── wrangler.jsonc # Cloudflare Worker config27└── drizzle.config.ts # Drizzle Kit config (local D1)
The app uses R2 Incremental Cache with Regional Cache (open-next.config.ts):
…(truncated)
WORKER_SELF_REFERENCE
|