A comprehensive Pokémon resource web application for the video game series, built with modern web technologies. Browse Pokédex entries, view detailed stats, moves, abilities, evolutions, and encounter locations across all Pokémon generations.
🔗 Live Site: newbarktown.ca
- Generation-specific game data - All page data is tailored to the game you are playing.
- Detailed Pokémon Information - Stats, types, abilities, and flavor text
- Move Data - Complete movesets organized by learn method (level-up, TM/HM, egg moves, tutors)
- Evolution Chains - Visual evolution paths with trigger conditions
- Encounter Locations - Where to find Pokémon in specific game versions
- Type Effectiveness - Offensive and defensive type matchups
- Framework: Next.js 14 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS + CSS Modules
- State Management: React Query + React Context
- Data Source: PokéAPI (REST & GraphQL)
- Deployment: Cloudflare Pages via @cloudflare/next-on-pages
This project follows Next.js 14 App Router conventions with a clear separation between route-specific and reusable code:
pokemechanics/
├── app/ # Route-specific components & pages
│ ├── pokemon/[name]/[game]/[dex]/ # Pokemon detail route
│ │ ├── _components/ # Route-specific components (not a route)
│ │ │ ├── abilities/ # Ability display components
│ │ │ ├── card/ # Pokemon card components
│ │ │ ├── encounters/ # Encounter location components
│ │ │ ├── evolutions/ # Evolution chain components
│ │ │ ├── flavor-text/ # Flavor text components
│ │ │ ├── moves/ # Move-related components
│ │ │ ├── navigation/ # Navigation components
│ │ │ ├── sprites/ # Sprite display components
│ │ │ ├── stats/ # Stats display components
│ │ │ ├── type-efficacy/ # Type effectiveness components
│ │ │ └── types/ # Type display components
│ │ ├── page.tsx # Pokemon detail page
│ │ └── *.tsx # Route components
│ │
│ ├── pokedex/[gen]/ # Pokedex route
│ │ └── page.tsx # Pokedex listing page
│ │
│ ├── api/ # API proxy routes
│ │ ├── graphql/ # GraphQL proxy (CORS handling)
│ │ └── rest/ # REST API proxy
│ │
│ ├── helpers/ # Server-side data fetching
│ │ ├── graphql/ # GraphQL query helpers
│ │ └── rest/ # REST API helpers
│ │
│ ├── layout.tsx # Root layout
│ ├── loading.tsx # Global loading UI
│ └── Client.tsx # Client-side providers wrapper
│
└── src/ # Reusable/shared code
├── components/ # Shared UI components (used across routes)
│ ├── common/ # Generic components (Box, Tooltip, etc.)
│ └── header/ # Header components
│
├── context/ # React context providers
├── hooks/ # Custom React hooks
├── utils/ # Utility functions
├── lib/ # Business logic & helpers
│ ├── getVariantPokemonName.ts # Determine correct variant based on region
│ └── findVarietyForRegion.ts # Find variety matching a region
├── types/ # TypeScript type definitions
├── constants/ # App-wide constants
└── styles/ # Global styles & CSS modules
/app directory - Route-specific code
- Components that are only used by a single route should be colocated with that route
- Use
_components/subdirectory (with underscore prefix) to store components without creating routes - Example:
app/pokemon/[id]/_components/encounters/contains encounter components only used on Pokemon detail pages - Benefits: Better code splitting, easier maintenance, clearer dependencies
/src directory - Reusable code
- Components used across multiple routes (Header, Autocomplete, ErrorBoundary, etc.)
- Shared utilities, hooks, types, and constants
- Business logic that isn't tied to a specific route
- If a component is only used in one place, it should live in
/appnear that route
File Naming Conventions
- All components are server components by default (no special naming needed)
"use client"directive at top of file - Client components (require interactivity)*.module.css- CSS Modules for component-scoped styles_folder/- Private folders (underscore prefix prevents Next.js route creation)page.tsx- Next.js route pageslayout.tsx- Next.js layoutsloading.tsx- Next.js loading UI
Use REST API (/app/helpers/rest/) when:
- You need all data across generations for a resource
- The REST endpoint provides complete, well-structured data
- Example:
fetchPokemonByName()- returns complete Pokemon object with all game data
Use GraphQL (/app/helpers/graphql/) when:
- You need specific fields or want to reduce payload size
- You need to filter or aggregate data with variables
- You want more control over the exact data shape
- Example:
getPokedexById()- fetch only needed fields for a specific generation
Server Components (default):
- Fetch directly from external APIs:
https://pokeapi.coor `https://graphql.pokeapi.co/v1beta2 - Better performance, reduced bundle size, SEO-friendly
// Server component - fetch directly
const data = await fetchFromGraphQL({
query,
variables,
// endpoint defaults to external API
});Client Components:
- Use
/api/graphqlproxy route to avoid CORS issues - Use with React Query for caching and state management
- Required for interactive features
// Client component - use proxy
const { data } = useQuery(["key"], async () => {
return await fetchFromGraphQL({
query,
variables,
endpoint: "/api/graphql", // Use Next.js API route
});
});Context: Regional variants (Alolan, Galarian, Hisuian, Paldean) require special handling to ensure correct data fetching and URL structure.
Key Helper Functions:
-
getVariantPokemonName(speciesData, regionName)- Determines correct variant name- Returns variant name (e.g., "rattata-alola") if Pokemon has regional form for that region
- Returns base name if no variant exists for the region
- Used for Pokemon data and GraphQL queries to get correct encounters/moves
-
findSpriteFromPokemonData- Determines the correct sprite url to use given:- pokemonData = The response data from the pokeapi REST endpoint /pokemon/:name
- generationName e.g. "generation-i"
- versionGroup e.g. "red-blue"
Implementation Flow:
- Extract name from URL parameter then use it in the
/pokemon/:nameREST endpoint - Fetch species data with species name:
fetchPokemonSpeciesByName(speciesName) - Determine region from version group and pokedex data
- Get actual variant name:
getVariantPokemonName(speciesData, regionName) - Fetch Pokemon data and GraphQL with variant name for correct encounters
URL Structure:
- Sitemap generates variant URLs:
/pokemon/rattata-alola/sun-moon/original-melemele - Navigation can use base names, variant detection happens server-side
- Ensures correct data for encounters (e.g., Alolan Rattata encounters in Sun/Moon)
TypeScript path aliases are configured in tsconfig.json:
import Component from "@/components/common/Component"; // src/components/common/Component
import { useHook } from "@/hooks/useHook"; // src/hooks/useHook
import { fetchData } from "@/utils/api"; // src/utils/api
import type { Pokemon } from "@/types"; // src/types/index- Node.js 18.x or higher
- npm 9.x or higher (comes with Node.js)
-
Clone the repository
git clone https://github.com/adammcodes/pokemechanics.git cd pokemechanics -
Install dependencies
npm install
-
Run development server
npm run dev
-
Open in browser
http://localhost:3000
npm run dev- Start Next.js development server (port 3000)npm run preview- Build and preview locally in Cloudflare Workers runtime (port 8787)
npm test- Run unit tests with Vitestnpm run test:ui- Run Vitest with UInpm run test:coverage- Run tests with coverage reportnpm run test:e2e- Run Playwright E2E testsnpm run test:e2e:ui- Run Playwright E2E tests with UI
npm run deploy- Build and deploy to Cloudflare Workersnpm run cf-typegen- Generate TypeScript types for Cloudflare environment
npm run build- Build Next.js (now handled bydeploy)npm start- Start production server (not used with Cloudflare Workers)
For local static generation of all ~8,500+ Pokemon pages, you'll need a self-hosted instance of PokeAPI to handle the high volume of requests during the build process.
-
Self-Hosted PokeAPI - Set up a local PokeAPI instance following the official setup guide
- GraphQL endpoint:
http://localhost:8080/v1/graphql - REST endpoint:
http://localhost/api/v2
- GraphQL endpoint:
-
Sufficient Server Resources - The build process makes thousands of API requests. Ensure your local PokeAPI instance has adequate resources to handle the load.
Create or update your .env.local file to point to your local PokeAPI instance:
# Local PokeAPI endpoints
NEXT_PUBLIC_POKEAPI_REST_ENDPOINT=http://localhost/api/v2
NEXT_PUBLIC_POKEAPI_GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphql
POKEAPI_REST_ENDPOINT=http://localhost/api/v2
POKEAPI_GRAPHQL_ENDPOINT=http://localhost:8080/v1/graphqlTo prevent overwhelming the local PokeAPI server, the build uses two levels of throttling:
1. Next.js Static Generation Concurrency (next.config.js)
experimental: {
staticGenerationMaxConcurrency: 4; // Max 4 pages building simultaneously
}2. API Request Concurrency (src/utils/rateLimiter.ts)
const API_CONCURRENCY_LIMIT = 10; // Max 10 concurrent API requestsYou can adjust these values based on your server's capacity. Higher concurrency = faster builds but more server load.
Use the monitoring script to track build progress and identify any 503 errors:
node scripts/monitored-build.jsThis script:
- Runs the standard
npm run buildprocess - Monitors output for 503 errors and route failures
- Logs all errors to
build-errors.log - Reports build statistics when complete
A successful build will show:
============================================================
BUILD COMPLETE - SUMMARY
============================================================
Build exit code: 0 (SUCCESS)
Total routes generated: 8,617
Pokemon routes generated: 8,590
✅ No 503 errors detected in build output!
============================================================
503 Service Unavailable Errors:
- Indicates your local PokeAPI server is overwhelmed
- Solutions:
- Reduce
staticGenerationMaxConcurrencyinnext.config.js - Reduce
API_CONCURRENCY_LIMITinsrc/utils/rateLimiter.ts - Increase resources allocated to your PokeAPI instance
- Check
build-errors.logto identify which Pokemon are failing
- Reduce
Build Hangs or Times Out:
- Verify your local PokeAPI instance is running and accessible
- Check that environment variables are correctly set in
.env.local - Ensure no firewall is blocking local API requests
Missing Routes in Build:
- Check the prerender manifest:
.next/prerender-manifest.json - Look for errors in the build output related to specific Pokemon
- Review
build-errors.logfor detailed error messages
After a successful build, preview the static site locally:
npx next start -p 3001Then test specific Pokemon pages to verify the build:
- http://localhost:3001/pokemon/pikachu/red-blue/kanto
- http://localhost:3001/pokemon/charizard/red-blue/kanto
- http://localhost:3001/pokemon/arceus/platinum/extended-sinnoh
All pages should return 200 OK and display complete Pokemon data.
- Selected Version Group is referred to as
gamearound the codebase - URL Search Params - /[name]/[version-group]/[dex]
- Cookies - Persist user preferences (selected game, theme)
- React Context - Share game selection across components (
GenerationContext) - React Query - Client-side data fetching, caching, and synchronization
- Tailwind CSS - Utility-first styling for layouts and common patterns
- CSS Modules - Component-scoped styles for complex UI (animations, custom layouts)
- CSS Custom Properties - Theme variables for consistent design
- Comprehensive TypeScript types in
src/types/index.ts
This app uses PokéAPI, a free RESTful and GraphQL API for Pokémon data.
REST API: https://pokeapi.co/api/v2/
GraphQL API: https://graphql.pokeapi.co/v1beta2
API routes in /app/api/ act as proxies to handle CORS for client-side requests.
This app is deployed on Cloudflare Workers using @opennextjs/cloudflare, which provides:
- Free tier - 100,000 requests/day included
# First time setup - Login to Cloudflare
npx wrangler login
# Preview locally before deploying (recommended)
npm run preview
# Opens at http://localhost:8787
# Deploy to production
npm run deploywrangler.jsonc- Cloudflare Worker configurationopen-next.config.ts- OpenNext adapter configuration for Cloudflare
Monitor production errors and requests in real-time:
# Stream live logs with formatting
npx wrangler tail pokemechanics --format pretty
# Filter by status
npx wrangler tail pokemechanics --status error # Only errors
npx wrangler tail pokemechanics --status ok # Only successful requestsAdd logging anywhere in your code - logs will appear in wrangler tail:
# View recent deployments
npx wrangler deployments list
# Rollback to previous version if needed
npx wrangler rollback <version-id>
# Check Worker status
npx wrangler whoamiFor production monitoring, consider adding:
- PokéAPI - Pokémon data source
- Pokémon © Nintendo/Game Freak/Creatures Inc.