Weather data with weatherapi-client
Motoko bindings for WeatherAPI.com, generated
from its OpenAPI spec. All nine operations live in a single module,
Apis/APIsApi, and all nine are read-only GETs.
Backend
A canister that reads the current temperature and a three-day maximum series. Non-replicated is the default, so you only supply the key:
The nine operations
days is an enum, not a number: ForecastWeatherDaysParameter is #_1_ … #_14_
and MarineWeatherDaysParameter is #_1_ … #_7_ (the underscores are how the
generator escapes numeric enum values — #_3_, not #_3 or 3).
API key setup
- Sign up at weatherapi.com — the free tier covers current weather, 3-day forecast, astronomy, timezone, search and IP lookup. History, future, marine and 14-day forecasts need a paid plan and return 403 on free keys.
- Copy the key from the dashboard and pass it as
auth = ?#apiKey key. - The client appends it as
?key=…(WeatherAPI takes noAuthorizationheader), so it appears in the request URL. Keep it in a stable variable written by an admin-only call, and never log the built URL.
Calls are non-replicated by default
The package ships is_replicated = ?false in defaultConfig, and that is a
correctness requirement here, not just a cost saving. Every response carries
per-request clocks — Location.localtime / localtime_epoch,
Current.last_updated / last_updated_epoch — which change second to second. A
replicated outcall has every subnet node issue its own request and demands
bit-identical bodies, so those fields would break consensus on most calls while
burning ~13× the cycles. You don't set it yourself; the default is correct.
Override with is_replicated = ?true only together with a transform that
strips the volatile fields.
Everything is optional
WeatherAPI marks no response field required, so the generated models are
all-optional: RealtimeWeather200Response.current : ?Current,
Current.temp_c : ?Float, and so on. Reach through them with a do ? block
(do ? { res.current!.temp_c! }) rather than nested switches, and decide what
an absent field means for your caller — the API omits fields your plan does not
cover (for example air_quality without the aqi=yes parameter).
Empty string and zero mean "omit"
Optional query parameters are dropped when they are "" or 0, because
WeatherAPI rejects empty lang= and zero-valued numerics. So passing "" for
lang, dt, alerts, aqi and 0 for unixdt, tp is how you say "not
supplied" — there is no ?Text parameter to leave null.
One consequence worth knowing: hour = 0 does not select midnight, it omits
the hour filter entirely and you get the whole day's hourly array. Filter the
returned hour : ?[ForecastForecastdayInnerHourInner] yourself if you need
00:00.
Errors
Non-2xx responses and decode failures throw Error.reject(…). The client is
generated with diagnostics, so the message is
HTTP <status> body[<n>B]=<first 100 chars>: <reason> — enough to tell a 401
(bad key) from a 403 (endpoint not on your plan) from a 400 (q not resolvable)
without extra logging. Catch with try/catch and surface
Error.message(err).
