Files
weatherapi/docs/internal/http-adapter.md

5.0 KiB

HTTP Adapter

This document describes the internal HTTP adapter under internal/adapters/inbound/httpapi. The public endpoint contract belongs in docs/api.md.

Purpose

The HTTP adapter turns feedapi route definitions into calls on the application service boundary. It owns route registration, query binding, request validation, forecast day-slice filtering, response envelopes, and template names.

Inputs and Outputs

Inputs:

  • feedapi HTTP requests;
  • query parameters;
  • an implementation of the adapter-local Service interface.

Outputs:

  • endpoint.Definition values registered by runtime composition;
  • response.Envelope{Data: ...} values for successful requests;
  • typed feedapi errors for invalid query parameters;
  • endpoint-specific template names for text rendering.

Boundaries

The adapter may:

  • define routes and supported response formats;
  • bind and validate query parameters;
  • call the Service interface;
  • choose the presenter function for an endpoint;
  • filter forecast copies for /today and /tomorrow.

The adapter must not:

  • execute SQL;
  • open databases;
  • mutate repository-returned models in place;
  • duplicate public API reference text;
  • move unit conversion or timezone presentation out of presenters.

Config Fields Used

The HTTP adapter does not read config directly. Feedapi applies server and renderer configuration before requests reach these handlers. Template names are declared in endpoint definitions, but templates.base_dir is loaded by feedapi.

External Adapters Used

  • feedapi/endpoint for route definitions.
  • feedapi/render for declared output formats.
  • feedapi/response for success envelopes.
  • feedapi/bind and feedapi/errors for query validation failures.
  • presenter package for payload shaping.

State

The adapter has no durable state. forecastNow is package-level state only to make forecast day filtering testable. Do not add request caches or cross-request mutable state here.

Route Registry

Definitions(svc) returns all implemented route definitions. It includes:

  • observations;
  • active alerts;
  • current conditions;
  • weather stories;
  • forecast discussions;
  • hourly and narrative forecasts.

Each route uses endpoint.GET, declares JSON/XML/text production, and names a text template. Feedapi owns routing, format negotiation, response rendering, middleware, and error normalization after definitions are registered.

Query Binding

There are three binder shapes:

  • bindQuery: format and units;
  • bindPrecisionQuery: format, units, and precision;
  • bindForecastPrecisionQuery: format, units, precision, and timezone;
  • bindTimezoneQuery: format, units, and timezone.

All binders use feedapi binding helpers with RejectUnknown: true. Supported common query values are lowercased and trimmed before binding where applicable.

precision defaults to 0 and must be between 0 and 2. Timezone parsing is available only through binders used by forecast, discussion, and weather story routes.

Timezone Parsing

Timezone parsing accepts:

  • IANA names through time.LoadLocation;
  • configured aliases such as Chicago and Stl;
  • common US abbreviations handled as fixed zones;
  • signed UTC offsets.

If both tz and TZ are provided, they must match case-insensitively. Invalid timezone input is converted to a feedapi invalid-parameter error.

Forecast Day Slices

Forecast base routes return the latest run unchanged except for presentation. /today and /tomorrow routes call filterForecastRunByDaySlice.

Filtering behavior:

  • nil runs remain nil;
  • missing timezone means UTC;
  • day selection is based on forecastNow() in the resolved timezone;
  • periods are included when period.StartTime falls on the target local date;
  • the run is shallow-copied and Periods is replaced with the filtered slice.

The package variable forecastNow exists so endpoint tests can make day-slice behavior deterministic.

Failure Behavior

Binder failures become feedapi invalid-parameter responses. Handler service errors are returned unchanged to feedapi for runtime normalization. Nil service payloads are presented as nil data, so response rendering can produce data: null.

Templates

Endpoint definitions bind template names only. Template loading and rendering belong to feedapi. Template files live under templates/ and are operator configuration through templates.base_dir.

Tests to Inspect Before Changing

  • internal/adapters/inbound/httpapi/endpoints_test.go
  • internal/adapters/inbound/httpapi/presenter/payload_test.go when changing presenter interaction
  • docs/api.md when query behavior, route behavior, or payload behavior changes

Invariants

  • Preserve strict unknown-query rejection.
  • Keep route-specific query policy in binder functions.
  • Keep endpoint handlers thin: bind, call service, present, envelope.
  • Keep day-slice filtering in the HTTP adapter, not in the repository.
  • Preserve top-level data envelopes and nil-data behavior.