Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/igorek05m/daily-geogame/llms.txt

Use this file to discover all available pages before exploring further.

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:
Home (Suspense boundary)
└── GameContent
    ├── GameHeader (stats, navigation)
    ├── WorldMap (D3-based SVG map)
    ├── GuessInput (country autocomplete)
    ├── LastHint (latest unlocked hint)
    ├── GuessList (user guesses with feedback)
    ├── HintGrid (6 hint packages)
    ├── Footer
    └── StatsModal (game results)
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:
const {
  gameDate,        // Current game date (YYYY-MM-DD)
  dayNumber,       // Days since GAME_START_DATE
  guesses,         // Array of Country objects
  targetCountry,   // Today's answer (null until game over)
  gameOver,        // Boolean flag
  hasWon,          // Boolean flag
  hintPackages,    // Array of 6 hint packages
  loading,         // Initial load state
  userStats,       // Personal stats
  globalStats,     // Daily leaderboard stats
  submitGuess,     // Async function
  changeDate,      // Navigate to prev/next day
} = useDailyGame();
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:
const [progRes, gameRes, statsRes] = await Promise.all([
  fetch(`/api/progress?date=${gameDate}`),
  fetch(`/api/daily?date=${gameDate}`),
  fetch(`/api/stats?date=${gameDate}`)
]);
Reference: app/hooks/useDailyGame.ts:89-93

Backend architecture

API routes

Four Next.js API routes handle all server-side logic:
RouteMethodPurpose
/api/dailyGETReturns daily game data (target country, hints)
/api/progressGET/POSTLoads/saves user progress for a date
/api/statsGETFetches global stats for a date
/api/countryGETProxies RestCountries API with caching
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:
// 1. Check if game exists in database
let game = await collection.findOne({ date: targetDate });

if (!game) {
  // 2. Randomly select a country
  const candidate = countries[Math.floor(Math.random() * countries.length)];
  
  // 3. Fetch RestCountries data
  const details = await fetchCountryDetails(candidate.alpha2);
  
  // 4. Fetch CIA Factbook data
  const factbookData = await fetchFactbookData(fipsCode);
  
  // 5. Generate 6 hint packages
  const hintPackages = generateHintPackages(factbookData, details);
  
  // 6. Save to database
  await collection.insertOne({ date, targetCountry, hintPackages });
}
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:
const guessesCount = progress?.guesses?.length || 0;
const allowedHintsCount = guessesCount + 1; // Start with 1 hint

const hints = game.hintPackages.map((pkg, index) => {
  if (isGameOver || index < allowedHintsCount) {
    return pkg; // Show full hint
  }
  return { ...pkg, hints: [{ label: "???", value: "???" }] }; // Hide
});
Reference: app/api/daily/route.ts:139-156

Guess processing

The /api/progress POST endpoint calculates distance, bearing, and connection for each guess:
guesses = guesses.map((g) => {
  const distance = getDistanceFromLatLonInKm(g.latlng, target.latlng);
  const bearing = getBearingAngle(g.latlng, target.latlng);
  
  let connection = "none";
  if (target.alpha3Code === g.alpha3Code) connection = "guess";
  else if (target.borders.includes(g.alpha3Code)) connection = "neighbor";
  else if (target.subregion === g.subregion) connection = "subregion";
  else if (target.region === g.region) connection = "region";
  
  return { ...g, distance, bearing, connection };
});
Reference: app/api/progress/route.ts:35-63

Database schema

MongoDB collections

daily_games

Stores one document per game date:
{
  date: "2026-03-02",           // ISO date string (unique index)
  targetCountry: Country,        // Full country object
  hintPackages: HintPackage[],   // Array of 6 hints
  createdAt: Date                // Timestamp
}

user_progress

Tracks progress for each session + date combination:
{
  sessionId: "uuid-v4",          // From geo_session cookie
  date: "2026-03-02",            // ISO date string
  guesses: Country[],            // Array with distance/bearing
  won: boolean,                  // True if correct guess made
  lastPlayed: Date               // Last update timestamp
}
Compound index: { sessionId: 1, date: 1 }

Connection pooling

The MongoDB client uses a singleton pattern to reuse connections:
// app/lib/mongodb.ts
let client: MongoClient;
let clientPromise: Promise<MongoClient>;

client = new MongoClient(process.env.MONGODB_URI);
clientPromise = client.connect();

export default clientPromise;
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:
const cookieStore = await cookies();
let sessionId = cookieStore.get('geo_session')?.value;

if (!sessionId) {
  sessionId = uuidv4(); // Generate new session
}

response.cookies.set('geo_session', sessionId, {
  secure: process.env.NODE_ENV === 'production',
  httpOnly: true,
  sameSite: 'strict',
  maxAge: 60 * 60 * 24 * 365 * 10 // 10 years
});
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:
const res = await fetch(`https://restcountries.com/v3.1/alpha/${code}`, {
  cache: 'force-cache' // Indefinite cache
});
Reference: app/api/country/route.ts:11-13

CIA World Factbook

Fetched server-side during game generation. The fetchFactbookData function tries 11 regional endpoints:
for (const region of REGIONS) {
  const url = `${BASE_URL}/${region}/${fipsCode}.json`;
  const response = await axios.get(url);
  if (response.status === 200) return response.data;
}
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

Build docs developers (and LLMs) love