You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ A search runs through a small, explicit pipeline — each stage is a separate, t
8
8
9
9
1.**Validate** — query params are checked before any network call: `query` is required, `count` is clamped (≤100), `lang`/`country` must be ISO two-letter codes, `from`/`to` must parse as ISO 8601, and `sortBy` ∈ {`publishedAt`, `relevance`}. Bad input fails fast with `400` instead of wasting an upstream call or quota.
10
10
2.**Cache** — parameters are normalized into a deterministic key and read through a pluggable store (in-memory by default, Redis when `REDIS_URL` is set). Cache failures are logged/metriced but do not fail article requests; identical in-flight misses in the same process share one upstream call.
11
-
3.**Upstream** — on a miss, GNews is called with a hard timeout; transport/provider failures surface as `502` rather than a hung request.
11
+
3.**Upstream** — on a miss, GNews is called with a hard timeout; transport/provider failures surface as `502`, and repeated failures open a short circuit that returns `503` without amplifying the outage.
5.**Respond** — legacy endpoints return raw article arrays, while `/api/v1/*` returns `{ data, meta }` envelopes with request/cache metadata and structured error bodies.
14
14
@@ -17,8 +17,8 @@ Full diagram and component notes live in [docs/ARCHITECTURE.md](docs/ARCHITECTUR
17
17
## Production-oriented features
18
18
19
19
-**Security:**[Helmet](https://helmetjs.github.io/) headers, configurable rate limiting, optional `TRUST_PROXY` for correct client IPs behind a load balancer, optional **`CLIENT_API_KEYS`** + `X-API-Key` on `/api/*`.
20
-
-**Reliability:** Upstream HTTP timeouts, response validation, `502` for provider/transport failures, graceful shutdown on `SIGTERM` / `SIGINT`.
21
-
-**Observability:** JSON logs via [Pino](https://getpino.io/), `x-request-id`, **`GET /metrics`** ([Prometheus](https://prometheus.io/) text format), cache hit/miss/error/coalescing + upstream latency metrics, and optional **OpenTelemetry** traces to OTLP (`OTEL_EXPORTER_OTLP_*`).
20
+
-**Reliability:** Upstream HTTP timeouts, response validation, `502` for provider/transport failures, provider circuit breaker with `503` short-circuiting, graceful shutdown on `SIGTERM` / `SIGINT`.
21
+
-**Observability:** JSON logs via [Pino](https://getpino.io/), `x-request-id`, **`GET /metrics`** ([Prometheus](https://prometheus.io/) text format), cache hit/miss/error/coalescing + upstream latency/circuit metrics, and optional **OpenTelemetry** traces to OTLP (`OTEL_EXPORTER_OTLP_*`).
22
22
-**Kubernetes-style probes:**`GET /health` (liveness), `GET /ready` (readiness when the API key is configured).
23
23
-**Supply chain:**`npm audit` in CI; **SPDX SBOM** artifacts; Docker builds with **SBOM + provenance**; **dependency review** on PRs; optional **SLSA-style lockfile attestation** on `main`; lockfile-only installs.
24
24
-**Contract:** OpenAPI at **`GET /openapi.yaml`** (also on disk as [docs/openapi.yaml](docs/openapi.yaml)).
Copy file name to clipboardExpand all lines: docs/ARCHITECTURE.md
+8-3Lines changed: 8 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,8 +17,9 @@ flowchart LR
17
17
2.**Express** (`src/app.ts`) applies middleware in order: trust-proxy (optional), **Pino** request logging, **metrics** observer, **Helmet**, JSON body parser, **rate limiting** (skips `/health`, `/ready`, `/openapi.yaml`, `/metrics`), then mounts `/api` routes.
18
18
3.**Controllers** validate query parameters and map domain results to HTTP status codes.
19
19
4.**News service** builds cache keys from normalized search parameters (`query`, `count`, `lang`, `country`, `from`, `to`, `sortBy`), reads through `getCacheStore()` (in-memory or **Redis** when `REDIS_URL` is set), coalesces identical in-flight misses per process, and delegates upstream fetches to the GNews provider adapter. Cache backend errors are logged and metriced without failing the article request.
20
-
5.**Response mapping** keeps legacy `/api/articles*` endpoints backward compatible with raw arrays, while `/api/v1/*` returns `{ data, meta }` envelopes with `requestId`, normalized filters, and cache status.
21
-
6.**Title** and **source** endpoints reuse the search call, then narrow results in memory (exact title match; case-insensitive source name match).
20
+
5.**Provider adapter** maps domain search options to GNews parameters, validates provider payloads, records upstream metrics, and opens a short circuit after repeated provider failures so outages are shed locally instead of amplified.
21
+
6.**Response mapping** keeps legacy `/api/articles*` endpoints backward compatible with raw arrays, while `/api/v1/*` returns `{ data, meta }` envelopes with `requestId`, normalized filters, and cache status.
22
+
7.**Title** and **source** endpoints reuse the search call, then narrow results in memory (exact title match; case-insensitive source name match).
22
23
23
24
### A search, end to end
24
25
@@ -85,6 +86,10 @@ Article arrays are stored per normalized search key with a **600-second** TTL (`
85
86
86
87
The service treats the cache as a quota and latency optimization, not as a hard dependency. Read failures fall through to the upstream provider, write failures return the upstream response without caching it, and both paths emit warning logs plus cache error metrics. Within a single process, concurrent misses for the same normalized key are coalesced so only the first request calls the provider.
87
88
89
+
## Provider Circuit Breaker
90
+
91
+
The GNews provider adapter tracks consecutive provider failures. After `UPSTREAM_CIRCUIT_FAILURE_THRESHOLD` failures, it opens a cooldown window (`UPSTREAM_CIRCUIT_COOLDOWN_MS`) and returns `503` without making another upstream call. After the cooldown, one request is allowed through; success closes the circuit, while another failure opens it again.
92
+
88
93
## Metrics
89
94
90
-
`src/metrics/register.ts` exports a single Prometheus registry used by `/metrics`. HTTP middleware records response counts, while `newsService`records cache hits/misses/errors/coalesced misses, upstream request outcomes, and upstream latency buckets.
95
+
`src/metrics/register.ts` exports a single Prometheus registry used by `/metrics`. HTTP middleware records response counts, while `newsService`and the provider adapter record cache hits/misses/errors/coalesced misses, upstream request outcomes, upstream latency buckets, and circuit breaker events.
Copy file name to clipboardExpand all lines: docs/OPERATIONS.md
+4-1Lines changed: 4 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -10,6 +10,8 @@
10
10
|`LOG_LEVEL`|`info` (non-test) | Pino level (`trace`–`fatal`, or `silent`). |
11
11
|`GNEWS_BASE_URL`|`https://gnews.io/api/v4`| Upstream provider base URL. Override only for local integration tests, benchmarks, or compatible provider mocks. |
@@ -52,8 +54,9 @@ On shutdown the server closes the Redis connection when that backend was used.
52
54
|`news_cache_errors_total`| `operation=get|set` | Cache backend errors that were tolerated by falling through to upstream or returning an uncached upstream response. |
|`news_upstream_circuit_events_total`| `event=opened|short_circuit|half_open|closed` | Provider circuit breaker state transitions and short-circuited requests. |
55
58
56
-
Use cache hit rate and coalesced miss counts to understand quota protection, cache error metrics to detect Redis/backend trouble, and upstream latency/error metrics to separate provider trouble from local API trouble.
59
+
Use cache hit rate and coalesced miss counts to understand quota protection, cache error metrics to detect Redis/backend trouble, upstream latency/error metrics to separate provider trouble from local API trouble, and circuit events to see when repeated provider failures are being shed locally.
0 commit comments