feat(api): let settings be read over GET, keep POST for writes - #172
Conversation
GET /api/settings now accepts ?keys=KEY,PREFIX_* and narrows the read the same way POST /api/settings/search does. Omitting keys returns the whole table, which is what this route has always done, so the change is additive. The three client callers move to it: the language probe on boot, the Admin Panel (unfiltered — it edits the whole table), and PhotoWidget, which reads its settings on every mount. POST /api/settings/search stays, unchanged and supported. It is documented in plugin-development.md, so third-party plugins are written against it and removing it would break them. Both routes now share one lookup function rather than two copies of the LIKE-building. Why bother: a read shaped as a write is not cacheable, and it lands in any audit of settings mutation. That second cost is not hypothetical — PhotoWidget mounts once per tab rotation on a wall display, so this POST fired at the cadence of a real write defect while that defect was being traced, and had to be recognized and set aside by hand. Tests cover the filter directly: an exact key does not match its siblings, a wildcard does not spill past its prefix, several keys combine, an empty filter means no filter rather than no results, and redaction still applies to the filtered path so WEATHER_API_KEY cannot be pulled out by naming it.
|
Reviewed critically and merging. I went after the redaction claim hardest, since that is the one where being wrong is expensive. Redaction holds, every way I could think of to askAgainst a live server with Both routes funnel through I also confirmed GET and POST return byte-identical key sets for the same filter, which is the real point of sharing The client callers work, and I proved the boot-critical oneThe language probe in Three sharp edges — two of them are yours to keep, one is new1. Your test asserts "an exact key must not match its siblings", but its siblings differ in length so it passes without exercising this. Not introduced here — 2. A very long filter 500s. 3. Repeated Fastify hands an array for a repeated param, None of these block: the first two are faithfully preserved behaviour and the third is benign. VerificationServer 225/225, client 281/281, translation parity, build clean, CI green on both jobs. Agreed on the motivation, too — a read shaped as a write showing up in an audit of settings mutation while chasing a real write defect is a good reason on its own, never mind the caching. |
GET /api/settings now accepts ?keys=KEY,PREFIX_* and narrows the read the same way POST /api/settings/search does. Omitting keys returns the whole table, which is what this route has always done, so the change is additive.
The three client callers move to it: the language probe on boot, the Admin Panel (unfiltered, it edits the whole table), and PhotoWidget, which reads its settings on every mount.
POST /api/settings/search stays, unchanged and supported. It is documented in plugin-development.md, so third-party plugins are written against it and removing it would break them. Both routes now share one lookup function rather than two copies of the LIKE-building.
Why bother: a read shaped as a write is not cacheable, and it lands in any audit of settings mutation. That second cost is not hypothetical - PhotoWidget mounts once per tab rotation on a wall display, so this POST fired at the cadence of a real write defect while that defect was being traced, and had to be recognized and set aside by hand.
Tests cover the filter directly: an exact key does not match its siblings, a wildcard does not spill past its prefix, several keys combine, an empty filter means no filter rather than no results, and redaction still applies to the filtered path so WEATHER_API_KEY cannot be pulled out by naming it.