Optimizing HeFeng Weather Integration: Icon Fonts, JWT Auth, Layered Caching, and Gzip

The article details a complete HeFeng weather integration for a mini‑program, covering front‑end icon‑font rendering, Rust‑based backend JWT authentication with Ed25519, three upstream API calls, a two‑tier cache for real‑time and historical data, gzip auto‑decompression, request‑timeout layering, and environment‑variable driven deployment configuration.

Tech Musings
Tech Musings
Tech Musings
Optimizing HeFeng Weather Integration: Icon Fonts, JWT Auth, Layered Caching, and Gzip

Overall Architecture

The system consists of a mini‑program front‑end, a Rust backend, and the HeFeng Weather service. The front‑end automatically fetches weather when a user records a mood with location, determines whether the request is for today or historical data, checks the cache, applies rate‑limiting, performs JWT authentication, calls the appropriate HeFeng API, writes the result to the cache, and returns the data.

Front‑end Implementation

Icon‑font solution

The WeatherIcon.vue component receives a HeFeng weather code (e.g., 100 for sunny) and generates a CSS class like qi-100. A global qweather-icons.css defines @font-face for the qweather-icons WOFF2 file (≈54 KB) and creates pseudo‑elements for each icon code.

<text class="weather-icon" :class="`qi-${code}`" :style="{ fontSize: `${size}rpx` }" />

Compared with emojis or images, the icon font is vector‑clear, small in size, and adapts to theme colors. A vertical‑align issue caused the icon to sit lower than surrounding text; the fix overrides ::before with vertical-align: baseline in a scoped style:

.weather-icon {
  display: inline-block;
  height: 1em;
  line-height: 1;
  &::before { vertical-align: baseline; }
}
Note: In a flex container the element’s own vertical-align is ignored, so the ::before rule must be overridden for the alignment to take effect.

Backend Implementation

JWT authentication (EdDSA / Ed25519)

HeFeng recommends JWT instead of an API key. The private key is parsed once at service start, and the issued token is cached in‑process. The token is refreshed when less than five minutes remain.

pub fn token(&self) -> Result<String, AppError> {
    // Fast path: reuse cached token if still valid
    let guard = self.cache.lock();
    if let Some(cached) = guard.as_ref() {
        if cached.exp > unix_now() + TOKEN_REFRESH_MARGIN_SECS {
            return Ok(cached.token.clone());
        }
    }
    // Slow path: re‑issue token
    self.refresh()
}

Requests include the header Authorization: Bearer <token> and are sent to the project‑specific API host assigned by HeFeng.

Three upstream APIs

/v7/weather/now

– real‑time weather for today (requires lng,lat). /geo/v2/city/lookup – converts coordinates to a city ID. /v7/historical/weather – up to 10‑day historical data (requires city ID, not coordinates).

Because the historical endpoint only accepts a city ID, the backend first calls the GeoAPI, caches the resulting LocationID for 24 hours, and then queries the historical data.

static LOCATION_ID_CACHE: LazyLock<Cache<String, String>> = LazyLock::new(|| {
    Cache::builder()
        .max_capacity(1000)
        .time_to_live(Duration::from_secs(24 * 60 * 60))
        .build()
});

Historical temperature is derived by averaging the daily high and low ( (tempMax + tempMin) / 2), and the icon is chosen from the 24‑hour data at noon (or the most frequent hour if noon is missing).

Caching and Request Optimization

Layered cache strategy

The original single cache with a 30‑minute TTL wasted calls for immutable historical data. The new design separates caches:

static REALTIME_CACHE: LazyLock<Cache<String, WeatherInfo>> = /* 30 min TTL */;
static HISTORICAL_CACHE: LazyLock<Cache<String, WeatherInfo>> = /* 24 h TTL */;

The handler selects the appropriate cache based on is_today. This reduces historical queries from every 30 minutes to once per 24 hours, achieving near‑100 % cache‑hit for the limited recent‑10‑day window.

Official docs advise cache duration to match data freshness; the key format {lat:.2},{lng:.2}:{date} truncates coordinates to two decimals (~1 km) so users in the same area share a single call.

Gzip auto‑decompression

HeFeng responses are gzip‑compressed; without decompression .json() fails. The global HTTP client enables gzip:

let http_client = reqwest::Client::builder()
    .timeout(Duration::from_secs(15))
    .connect_timeout(Duration::from_secs(5))
    .gzip(true)
    .build()?;

The gzip feature automatically adds Accept‑Encoding: gzip and transparently decompresses the body.

Avoiding retry storms

HeFeng advises against immediate retries on errors to prevent being flagged as DDoS. The implementation returns None on a 404 (e.g., subscription not enabled) without retrying:

if resp.status() == reqwest::StatusCode::NOT_FOUND {
    return Ok(None);
}

This fast‑failure approach follows the official request‑optimization guidance.

Other request‑optimisation points

On‑demand requests : The front‑end fetches weather only when recording today’s mood with location; no pre‑loading or polling.

Timeout layering : Global timeout 15 s, per‑request timeout 10 s to prevent a single slow upstream call from blocking the service.

LocationID cache : GeoAPI results cached for 24 h to avoid repeated coordinate‑to‑city conversions.

Configuration and Deployment

HeFeng‑related settings are injected via environment variables (see config.rs), for example: QWEATHER_API_HOST – project‑specific API host (optional, default provided). QWEATHER_PROJECT_ID – Project ID used as JWT sub (required). QWEATHER_KEY_ID – Credential ID used as JWT kid (required). QWEATHER_PRIVATE_KEY – PEM‑encoded Ed25519 private key (optional, preferred). QWEATHER_PRIVATE_KEY_FILE – Path to private‑key file (alternative to the PEM variable).

The private‑key file is mounted read‑only into the container via a Docker volume; its ownership must match the container’s non‑root user (use chown if necessary).

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

RustcachingJWTGzipMini ProgramHeFeng WeatherIcon Font
Tech Musings
Written by

Tech Musings

Capturing thoughts and reflections while coding.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.