All Docs
FeaturesCalmony Sanctions MonitorUpdated March 12, 2026

Improving API Performance with Response Caching (PERF-12)

Improving API Performance with Response Caching (PERF-12)

Released in v0.1.86

Background

The sanctions screening platform exposes several API routes consumed by the monitoring dashboard and compliance workflows. Prior to this release, every route used Next.js's force-dynamic export, which completely disables caching and issues a live database query on every request.

For routes like /api/dashboard/stats — which aggregates counts that change at most a few times per hour — and /api/sanctions/changes — which reflects a list updated once nightly — this was unnecessarily expensive. Under moderate load, each page refresh triggered redundant DB reads with no benefit to data freshness.

What Changed

/api/dashboard/stats

This endpoint returns per-user aggregate statistics (total screened, matches found, pending reviews, etc.). Because the data is user-specific, any caching must be scoped privately to prevent one user's cached response from being served to another.

The response now carries:

Cache-Control: private, max-age=60, stale-while-revalidate=300
DirectiveValueMeaning
privateCache is bound to the individual user; not stored by shared proxies
max-age60 sClient considers the response fresh for 1 minute
stale-while-revalidate300 sClient may serve stale data for up to 5 minutes while fetching a fresh copy in the background

/api/sanctions/changes

This endpoint lists changes to the OFSI consolidated sanctions list. The underlying data is refreshed by the nightly sync job, so sub-minute freshness is not required.

The response now carries:

Cache-Control: public, max-age=300, stale-while-revalidate=3600
DirectiveValueMeaning
publicResponse may be stored by shared caches (CDN, reverse proxy)
max-age300 sResponse is considered fresh for 5 minutes
stale-while-revalidate3600 sStale response may be served for up to 1 hour while revalidating

force-dynamic Removal

Routes that do not require a live DB read on every request have had export const dynamic = 'force-dynamic' removed. This allows Next.js to apply its standard caching heuristics, reducing unnecessary compute on the server.

Data Safety Considerations

  • User data isolation is preserved by using private on all user-scoped endpoints. A shared cache will never store or serve another user's dashboard statistics.
  • Sanctions list accuracy is not materially affected. The nightly sync is the authoritative update mechanism; a 5-minute cache window does not delay actionable compliance information.
  • Cache TTLs are deliberately conservative relative to actual data-change frequency.

Summary

EndpointBeforeAfter
/api/dashboard/statsNo cache, DB query every requestprivate, 60 s fresh / 5 min SWR
/api/sanctions/changesNo cache, DB query every requestpublic, 5 min fresh / 1 hr SWR
Other routesforce-dynamicNext.js default caching