@molecule/api-weather
Core interface · weather · API (Node) · v1.0.3 · Apache-2.0
Provider-agnostic weather data interface (current observations, daily and hourly forecasts) with WMO 4677 codes and metric units.
npm install @molecule/api-weatherHow it works
@molecule/api-weather is the weather core interface on the API (Node) side: the API your app calls, with no vendor inside.
Choose the implementation by bonding one of its 2 providers: @molecule/api-weather-open-meteo, @molecule/api-weather-openweather.
import { setProvider, getCurrent, getForecast } from '@molecule/api-weather'
import { provider as openMeteo } from '@molecule/api-weather-open-meteo'
setProvider(openMeteo)
const now = await getCurrent({ lat: 40.7128, lon: -74.006 })
const week = await getForecast({ lat: 40.7128, lon: -74.006 }, 7)Providers (2): @molecule/api-weather-open-meteo, @molecule/api-weather-openweather
Works with: @molecule/api-bond, @molecule/api-i18n
Reference
Provider-agnostic weather data interface for molecule.dev.
Defines the WeatherProvider interface for current observations, daily
forecasts, and hourly forecasts. Bond packages (Open-Meteo, OpenWeather,
NWS, etc.) implement this interface. Application code uses the convenience
functions (getCurrent, getForecast, getHourly) which delegate to the
bonded provider.
Values are normalized to metric units: temperature in degrees Celsius, precipitation in millimetres, wind in kilometres per hour, humidity as percent (0–100). Condition codes follow WMO 4677 — providers that speak a different vocabulary MUST translate before returning.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider, getCurrent, getForecast } from '@molecule/api-weather'
import { provider as openMeteo } from '@molecule/api-weather-open-meteo'
setProvider(openMeteo)
const now = await getCurrent({ lat: 40.7128, lon: -74.006 })
const week = await getForecast({ lat: 40.7128, lon: -74.006 }, 7)
Type
core
Installation
npm install @molecule/api-weather @molecule/api-bond @molecule/api-i18n
API
Interfaces
WeatherLocation
Geographic coordinate, expressed in decimal degrees (WGS 84).
interface WeatherLocation {
/**
* Latitude in decimal degrees, positive north.
*/
lat: number
/**
* Longitude in decimal degrees, positive east.
*/
lon: number
/**
* Optional IANA timezone (e.g. `'America/New_York'`). Providers that
* don't support timezone selection MAY ignore this. When omitted,
* providers should default to UTC or the location's local timezone.
*/
timezone?: string
}
WindReading
Wind measurement.
interface WindReading {
/**
* Wind speed in kilometres per hour (km/h).
*/
speedKmh: number
/**
* Wind direction in compass degrees (0 = north, 90 = east, etc.). May be
* `null` if the upstream provider doesn't report it (e.g. calm conditions).
*/
directionDeg: number | null
/**
* Wind gust speed in kilometres per hour, if reported by the provider.
*/
gustKmh?: number
}
CurrentWeather
A single point-in-time weather observation or forecast for a location.
Fields use SI / metric units (Celsius, mm, km/h, percent 0–100). Providers MUST normalize their upstream payloads to these units before returning.
interface CurrentWeather {
/**
* Time the observation/forecast applies to.
*/
time: Date
/**
* Air temperature, degrees Celsius.
*/
temperatureC: number
/**
* Apparent ("feels like") temperature, degrees Celsius.
*/
feelsLikeC: number
/**
* Relative humidity, percent (0–100).
*/
humidity: number
/**
* Liquid-equivalent precipitation accumulated over the reporting interval,
* in millimetres.
*/
precipitationMm: number
/**
* Wind reading.
*/
wind: WindReading
/**
* Provider-agnostic numeric weather code (WMO 4677 where supported, e.g.
* `0` = clear, `61` = light rain). Providers that don't speak WMO MUST
* map their native codes onto WMO before returning.
*/
code: number
/**
* Short, locale-neutral English summary derived from `code`
* (e.g. `'Clear'`, `'Light rain'`). Localization is the consumer's
* responsibility — the summary is intended as a developer-friendly
* default, not a user-facing string.
*/
summary: string
}
DailyForecast
Daily forecast entry (one row per forecast day).
Daily entries summarize the day, so temperatures are min/max and precipitation is the day's total accumulation.
interface DailyForecast {
/**
* Date the forecast applies to (00:00 in the location's timezone).
*/
date: Date
/**
* Daily minimum air temperature, degrees Celsius.
*/
temperatureMinC: number
/**
* Daily maximum air temperature, degrees Celsius.
*/
temperatureMaxC: number
/**
* Daily minimum apparent temperature, degrees Celsius.
*/
feelsLikeMinC: number
/**
* Daily maximum apparent temperature, degrees Celsius.
*/
feelsLikeMaxC: number
/**
* Mean relative humidity for the day, percent (0–100). Providers that
* don't expose a daily mean SHOULD compute one from the hourly values.
*/
humidity: number
/**
* Total liquid-equivalent precipitation accumulated for the day, in
* millimetres.
*/
precipitationMm: number
/**
* Maximum wind reading observed (or forecast) for the day.
*/
wind: WindReading
/**
* Provider-agnostic numeric weather code (WMO 4677). See
* `CurrentWeather.code`.
*/
code: number
/**
* Short English summary derived from `code`. See
* `CurrentWeather.summary`.
*/
summary: string
}
WeatherProvider
Weather data provider interface.
All weather providers (Open-Meteo, OpenWeather, NWS, fixtures, etc.) implement this interface. The interface is intentionally minimal so providers with different upstream APIs can satisfy it identically.
Providers MUST normalize all returned values to metric units (Celsius, mm, km/h, percent 0–100) and MUST map upstream condition codes onto WMO 4677 numeric codes.
interface WeatherProvider {
/**
* Returns the current observed weather for a location.
*
* @param location - Latitude/longitude (and optional timezone).
* @returns Normalized `CurrentWeather` reading.
*/
getCurrent(location: WeatherLocation): Promise<CurrentWeather>
/**
* Returns a daily forecast for a location.
*
* @param location - Latitude/longitude (and optional timezone).
* @param days - Number of forecast days, including today. Implementations
* MAY clamp to whatever range their upstream supports. Defaults to `7`.
* @returns Array of normalized `DailyForecast` entries, ordered
* ascending by date.
*/
getForecast(location: WeatherLocation, days?: number): Promise<DailyForecast[]>
/**
* Returns an hourly forecast for a location.
*
* @param location - Latitude/longitude (and optional timezone).
* @param hours - Number of forecast hours, starting from the current hour.
* Implementations MAY clamp to whatever range their upstream supports.
* Defaults to `24`.
* @returns Array of normalized `HourlyForecast` entries, ordered
* ascending by time.
*/
getHourly(location: WeatherLocation, hours?: number): Promise<HourlyForecast[]>
}
Types
HourlyForecast
Hourly forecast entry (one row per forecast hour).
Hourly entries are point-in-time, so they share the field shape with
CurrentWeather.
type HourlyForecast = CurrentWeather
Functions
setProvider(provider)
Registers a weather provider as the active singleton. Called by bond
packages (e.g. @molecule/api-weather-open-meteo) during application
startup.
function setProvider(provider: WeatherProvider): void
provider— The weather provider implementation to bond.
getProvider()
Retrieves the bonded weather provider, throwing if none is configured.
function getProvider(): WeatherProvider
Returns: The bonded weather provider.
hasProvider()
Checks whether a weather provider is currently bonded.
function hasProvider(): boolean
Returns: true if a weather provider is bonded.
getCurrent(location)
Returns the current observed weather for a location, using the bonded provider.
function getCurrent(location: WeatherLocation): Promise<CurrentWeather>
location— Latitude/longitude (and optional timezone).
Returns: Normalized current weather reading.
getForecast(location, days)
Returns a daily forecast for a location, using the bonded provider.
function getForecast(location: WeatherLocation, days?: number): Promise<DailyForecast[]>
location— Latitude/longitude (and optional timezone).days— Number of forecast days. Defaults to7.
Returns: Array of normalized daily forecast entries.
getHourly(location, hours)
Returns an hourly forecast for a location, using the bonded provider.
function getHourly(location: WeatherLocation, hours?: number): Promise<CurrentWeather[]>
location— Latitude/longitude (and optional timezone).hours— Number of forecast hours. Defaults to24.
Returns: Array of normalized hourly forecast entries.
Available Providers
| Provider | Package |
|---|---|
| Open-Meteo | @molecule/api-weather-open-meteo |
| OpenWeather | @molecule/api-weather-openweather |
Injection Notes
Requirements
Peer dependencies:
@molecule/api-bond^1.0.1@molecule/api-i18n^1.0.1
Runtime Dependencies
@molecule/api-bond@molecule/api-i18n
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip:
- Requesting weather for a real location (
getCurrent) renders PLAUSIBLE current conditions in the UI — a temperature in a sane range (roughly -60 to 60 °C), a non-emptysummary/condition, plus the humidity (0–100) and wind the widget shows — never null, NaN, "--"/placeholder, or a hardcoded constant that never moves. Reload and confirm values track the provider. - If a forecast is shown,
getForecastreturns MULTIPLE future days with DISTINCT values (min/max temps and codes vary day to day), andgetHourlylikewise — not one row repeated N times or every day identical. - Changing the location changes the data: fetch two clearly different coordinates (e.g. a tropical vs. a polar city) and confirm the rendered current + forecast actually differ. Identical output for different lat/lon means the location never reached the provider.
- Units: the core normalizes everything to metric (Celsius, km/h, mm). If the UI offers a °F / imperial toggle it is an app-side conversion — flipping it must consistently convert EVERY displayed value (current, feels-like, forecast min/max, wind) while the fetched data stays metric; no half- converted mix (a °C label over an °F number, or wind left in km/h).
- An unknown place name (if the app geocodes) or out-of-range coordinates surfaces a clear "not found" — not a blank widget — and a provider outage or rate-limit (429) surfaces a graceful message that leaves the last-known reading or an empty state. The app never crashes, spins forever, or shows NaN/undefined.
- The provider API key (if the bonded provider needs one) stays server- side — never shipped to the browser or a client bundle — and the weather endpoint is not an open unbounded proxy: it accepts only the app's own locations, not arbitrary caller-supplied upstream URLs or keys.