Skip to main content
Daily GeoGame is built as a full-stack Next.js application using the App Router pattern. The architecture follows a client-server model with React Server Components and API routes.

High-level overview

The application is structured as a monolithic Next.js app with:
  • Frontend: React 19 components with TypeScript
  • Backend: Next.js 15 API routes
  • Database: MongoDB Atlas for persistent storage
  • External APIs: RestCountries and CIA World Factbook
  • Session management: HTTP-only cookies with UUID-based sessions

Frontend architecture

Component hierarchy

The main page (app/page.tsx) uses a suspense-wrapped component structure:
References:
  • Main page: app/page.tsx:15-119
  • Components in app/components/

State management

The useDailyGame hook (app/hooks/useDailyGame.ts) centralizes all game state:
Key responsibilities:
  • Fetches game data on mount via parallel API calls
  • Manages URL query params (?date=YYYY-MM-DD)
  • Handles guess submission with optimistic updates
  • Calculates day numbers from GAME_START_DATE
Reference: app/hooks/useDailyGame.ts:6-235

Data fetching pattern

The hook uses Promise.all for parallel requests on load:
Reference: app/hooks/useDailyGame.ts:89-93

Backend architecture

API routes

Four Next.js API routes handle all server-side logic: All routes are located in app/api/*/route.ts.

Daily game generation

The /api/daily route generates a new game if one doesn’t exist for the requested date:
The algorithm retries up to 5 times if a country lacks Factbook data, falling back to Poland if all attempts fail. Reference: app/api/daily/route.ts:72-136

Hint reveal logic

Hints are progressively revealed based on guess count:
Reference: app/api/daily/route.ts:139-156

Guess processing

The /api/progress POST endpoint calculates distance, bearing, and connection for each guess:
Reference: app/api/progress/route.ts:35-63

Database schema

MongoDB collections

daily_games

Stores one document per game date:

user_progress

Tracks progress for each session + date combination:
Compound index: { sessionId: 1, date: 1 }

Connection pooling

The MongoDB client uses a singleton pattern to reuse connections:
All API routes import clientPromise to share the same connection pool. Reference: app/lib/mongodb.ts:1-22

Data flow

User guess submission

Page load sequence

  1. Initial render: Show loading state
  2. Parallel fetch: Request /api/progress, /api/daily, /api/stats
  3. Progress check: Restore previous guesses from MongoDB
  4. Daily game: Get target country and hints (masked if not game over)
  5. Stats fetch: Load global player statistics
  6. State update: Render game with all data
Reference: app/hooks/useDailyGame.ts:84-133

Session management

Users are identified by a geo_session cookie containing a UUIDv4:
The session cookie:
  • Is created on first guess submission
  • Persists for 10 years
  • Links all user progress across games
  • Enables anonymous stat tracking
Reference: app/api/progress/route.ts:17-86

External integrations

RestCountries API

Fetched via the /api/country proxy route with Next.js caching:
Reference: app/api/country/route.ts:11-13

CIA World Factbook

Fetched server-side during game generation. The fetchFactbookData function tries 11 regional endpoints:
No caching is applied, but games are stored in MongoDB after generation. Reference: app/lib/factbook.ts:28-42

Performance considerations

  • Parallel fetching: All initial data loads use Promise.all
  • Static map data: SVG map bundled at build time
  • API caching: RestCountries responses cached indefinitely
  • Database indexes: Compound index on sessionId + date
  • Connection pooling: Shared MongoDB client across requests
  • Suspense boundaries: Prevent layout shifts during loading

Error handling

The app gracefully degrades when external services fail:
  • MongoDB unavailable: Game generation works in-memory
  • RestCountries timeout: Falls back to local countries.json
  • Factbook missing: Retries with different countries
  • Hint generation error: Returns empty array
Reference: app/api/daily/route.ts:40-44