Geolocation at the edge
by landro
What are we building?
Check it out! https://weatherflare.adhocracy.workers.dev
A rendered on demand static HTML page rendered via Javascript on Cloudflare workers based on the visitors geo-location without the use of client side javascript or location data.
What geolocation data is available via Cloudflare headers?
request.cf.colo
- The three-letter IATA airport code of the data center that the request hit, for example, "DFW".
country
request.cf.country
- Country of the incoming request. The two-letter country code in the request. This is the same value as that provided in the CF-IPCountry header, for example, "US".
request.cf.isEUCountry
- If the country of the incoming request is in the EU, this will return "1". Otherwise, this property will be omitted.
request.cf.city
- City of the incoming request, for example, "Austin".
request.cf.continent
- Continent of the incoming request, for example, "NA".
request.cf.latitude
- Latitude of the incoming request, for example, "30.27130".
request.cf.longitude
- Longitude of the incoming request, for example, "-97.74260".
request.cf.postalCode
- Postal code of the incoming request, for example, "78701".
request.cf.metroCode
- Metro code (DMA) of the incoming request, for example, "635".
request.cf.region
- If known, the ISO 3166-2 name for the first level region associated with the IP address of the incoming request, for example, "Texas".
request.cf.regionCode
- If known, the ISO 3166-2 code for the first-level region associated with the IP address of the incoming request, for example, "TX".
request.cf.timezone
- Timezone of the incoming request, for example, "America/Chicago".
Check it out! https://weatherflare.adhocracy.workers.dev
Complete cloudflare worker code (no client side javascript)
addEventListener('fetch', event => { | |
event.respondWith(handleRequest(event.request)); | |
}); | |
async function handleRequest(request) { | |
// https://api.weather.gov/points/38.8894,-77.0352 | |
const { latitude, longitude } = request.cf; | |
const headers = { | |
'content-type': 'application/json', | |
'User-Agent': ('landro.dev', 'jaylandro@hotmail.com') | |
} | |
const weatherLocationResponse = await fetch( | |
`https://api.weather.gov/points/${latitude},${longitude}`, { headers: headers } | |
); | |
console.log(weatherLocationResponse) | |
const weatherLocationData = await weatherLocationResponse.json(); | |
const forecastResponse = await fetch( | |
weatherLocationData.properties.forecast, { headers: headers } | |
); | |
const forecastData = await forecastResponse.json(); | |
const forecast = forecastData.properties.periods; | |
function iconHelper(shortForecastString) { | |
if (shortForecastString.includes("Snow")) | |
return '🌨'; | |
if (shortForecastString.includes("thunderstorms")) | |
return '⛈'; | |
if (shortForecastString.includes("Partly Cloudy")) | |
return '🌤'; | |
if (shortForecastString.includes("Cloud")) | |
return '☁️'; | |
if (shortForecastString.includes("Light Rain")) | |
return '🌦'; | |
if (shortForecastString.includes("Rain")) | |
return '🌧'; | |
return '☀️'; | |
} | |
let html = ` | |
<html> | |
<head> | |
<title>Geolocation: Weather</title> | |
</head> | |
<body> | |
<style> | |
body { | |
padding: 6em; | |
font-family: sans-serif; | |
} | |
h1 { | |
color: #f6821f; | |
} | |
.header { | |
grid-area: header; | |
margin-bottom: 0; | |
} | |
.icon { | |
grid-area: icon; | |
font-size: 3em; | |
font-style: normal; | |
} | |
.detailforecast { | |
grid-area: detailforecast; | |
line-height: 1.4; | |
max-width: 500px; | |
} | |
.day { | |
display: grid; | |
grid-gap: 10px; | |
grid-template-columns: 3.2em 3fr; | |
grid-template-areas: | |
"header header" | |
"icon detailforecast"; | |
margin-bottom: 1.2em; | |
} | |
</style> | |
<div id="container"> | |
<h1>Weather in ${weatherLocationData.properties.relativeLocation.properties.city}, ${weatherLocationData.properties.relativeLocation.properties.state}</h1> | |
<main> | |
<section> | |
${forecast.map(day => ` | |
<div class="day"> | |
<h3 class="header">${day.name}</h3> | |
<i class="icon">${iconHelper(day.shortForecast)}</i> | |
<span class="detailforecast">${day.detailedForecast}</span> | |
</div> | |
`).join(' ')} | |
</section> | |
</div> | |
</body>`; | |
return new Response(html, { | |
headers: { | |
'content-type': 'text/html; charset=UTF-8;', | |
}, | |
}); | |
} |