Historical population data browser powered by an Umbraco headless CMS backend.
Live demo: https://population-checker.vercel.app/
Repository: https://github.com/suedeapple/population-checker
API Repository: https://github.com/suedeapple/population-checker-umbraco
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Language | TypeScript |
| Styling | SCSS (Sass) with CSS custom properties |
| CMS | Umbraco via the Delivery API |
| API client | orval — generated from Umbraco's OpenAPI spec |
| Charts | Recharts |
| Icons | Font Awesome |
- Node.js 20+
- A running Umbraco instance with the Delivery API enabled
Copy .env.local and fill in the values:
NEXT_PUBLIC_UMBRACO_BASE_URL="https://localhost:44341" # Umbraco base URL, no trailing slash
UMBRACO_REVALIDATE_SECRET="your-secret" # Shared secret for the revalidation webhook
UMBRACO_REVALIDATE_ACCESS_CONTROL_ORIGIN="*" # CORS origin allowed to call /revalidate
NODE_TLS_REJECT_UNAUTHORIZED=0 # Disable TLS verification for local dev (self-signed cert)
Note:
NODE_TLS_REJECT_UNAUTHORIZED=0is set automatically by thedevandgeneratescripts. Do not set it in production.
npm installnpm run devOpens at http://localhost:3000. TLS certificate validation is disabled automatically so the dev server can reach a local Umbraco instance running on a self-signed cert.
The TypeScript client under src/api/ is generated from Umbraco's live OpenAPI spec. Re-run whenever the Umbraco content model changes:
npm run generateThis calls orval, which fetches the spec from NEXT_PUBLIC_UMBRACO_BASE_URL/umbraco/swagger/delivery/swagger.json and writes typed models to src/api/model/ and fetch functions to src/api/.
Do not edit files in
src/api/by hand — they will be overwritten on the next generate.
npm run build
npm run startsrc/
├── app/
│ ├── page.tsx # Home — fetches all continents/countries, passes to HomeSearch
│ ├── [continent]/
│ │ ├── page.tsx # Continent page
│ │ └── [country]/
│ │ └── page.tsx # Country page — flag, description, population table and chart
│ └── revalidate/
│ └── route.ts # POST endpoint called by Umbraco to bust the Next.js data cache
├── api/ # orval-generated — do not edit
│ ├── model/ # TypeScript types from the OpenAPI spec
│ ├── content/ # Content Delivery API fetch functions
│ └── media/ # Media Delivery API fetch functions
├── components/
│ ├── Header.tsx
│ ├── Footer.tsx
│ ├── HomeSearch.tsx # Client component — search input + filtered continent/country list
│ ├── CountryList.tsx
│ └── PopulationChart.tsx
├── umbraco/
│ └── index.ts # All data-fetching functions (getContinentPages, getSettings, etc.)
├── lib/
│ └── utils.ts # Shared utilities (toSlug, etc.)
├── styles/
│ ├── globals.scss # Design tokens (CSS custom properties) and all component styles
│ └── reset.scss
└── custom-fetch.ts # orval fetch mutator — rewrites URLs to NEXT_PUBLIC_UMBRACO_BASE_URL
| Content type | Key properties |
|---|---|
settings |
siteName, disclaimer |
continent |
— |
country |
capital, description, iso2Code, iso3Code |
year |
population |
The expected tree structure is:
Home
└── Settings
Continents
└── [Continent]
└── [Country]
└── [Year]
- Flags — served from flagcdn.com using the country's
iso2Codeproperty. No API key required. - Population figures — sourced from the World Bank public API and stored as
yearcontent nodes in Umbraco. The site does not call the World Bank API at runtime.
Next.js caches all Umbraco responses with the content tag. When content is published in Umbraco, it should POST to /api/revalidate with the header x-revalidate-secret matching UMBRACO_REVALIDATE_SECRET. This triggers an on-demand revalidation of all cached content.