Skip to content

feat(web): expose last cache refresh time as header on rest endpoints - #474

Merged
matheus1lva merged 2 commits into
mainfrom
feat/rest-last-refresh-header
Sep 14, 2026
Merged

matheus1lva merged 2 commits into
mainfrom
feat/rest-last-refresh-header

Conversation

@matheus1lva

@matheus1lva matheus1lva commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Summary

REST cache consumers had no way to tell how stale a response was. The cron handler now writes the completion timestamp to redis (rest:refresh:<job>, key derived from the cron path slug) after a successful run, and the REST routes return it as x-last-refresh (ISO 8601). Redis is needed because the crons and the routes run in separate serverless invocations.

Header is added to Access-Control-Expose-Headers so browsers can read it — the routes serve Access-Control-Allow-Origin: *, which otherwise hides custom headers.

Job → route mapping:

cron routes
refresh-cache /rest/list/vaults, /rest/list/vaults/[chainId], /rest/snapshot/[chainId]/[address]
reports-refresh /rest/reports/[chainId]/[address]
timeseries-refresh /rest/timeseries/[segment]/[chainId]/[address]

How to review

Start at packages/web/app/api/rest/cache.ts (the three new helpers) and packages/web/app/api/cron/handler.ts (where the write happens). The five route files are the same change repeated: the redis read for the header joins the existing Promise.all, so no extra round trip is serialized.

Two things worth checking:

  • The timestamp is written only after job() resolves, so a failed run leaves the previous value in place.
  • A redis failure on the read path returns no header rather than failing the request.

Test plan

  • Automated: npx vitest run app/api/cron app/api/rest — 41 passed at first land. Added (not re-run in the review-fix loop): cache.spec.ts covers setLastRefresh key+ISO (and default Date) plus lastRefreshHeaders hit / miss / throw; handler.spec.ts covers write on success, no write when the job throws, no write on an empty path slug, and 200 when the write throws; timeseries route.spec.ts asserts the route emits x-last-refresh for job timeseries-refresh.
  • Manual: hit a cron endpoint with the CRON_SECRET bearer, then curl -i one of the REST routes and confirm x-last-refresh matches.

Risk / impact

Low. Additive response header, no change to any payload. No migrations, no auth changes.

Known limits:

  • Responses are CDN-cached with s-maxage=900, so a cached hit serves the header captured at render time, not the current value.
  • The *-refresh-historical crons write their own keys, but no route reads them yet.
  • 404 and 400 responses omit x-last-refresh. List/snapshot/reports fetch the timestamp then drop it on 404; timeseries unknown-segment 404s before the redis read.
  • Successful crons await one extra redis SET before the kuma up ping. SET errors are swallowed (previous timestamp kept, cron still 200); a hung SET delays the 200.

Rollback is a revert; nothing depends on the redis key existing.

Cron handler records refresh timestamp in redis (rest:refresh:<job>) after a
successful run. REST routes read it and return x-last-refresh, exposed via CORS.
@vercel

vercel Bot commented Sep 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
kong Ready Ready Preview Sep 12, 2026 1:48am UTC

Request Review

@murderteeth murderteeth left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

Cron handler records a completion timestamp in redis after each successful run, and the five REST routes surface it as an x-last-refresh header with a matching Access-Control-Expose-Headers entry. Helper, handler, and timeseries route are covered by new specs; lint and the cron/rest vitest suites pass.

Verdict

APPROVE


How This Was Reviewed

Reviewed with the review-pr-workflow skill
5 review lenses, each finding independently verified by claude. 1 candidate finding was refuted and dropped.

@matheus1lva
matheus1lva merged commit 3851c54 into main Sep 14, 2026
7 checks passed
@matheus1lva
matheus1lva deleted the feat/rest-last-refresh-header branch September 14, 2026 22:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants