Caching
Caching in CAP can mean HTTP response validation, retaining the result of a query or function, or querying a replicated copy of service data. The core, application plugins and a backing store answer different parts of that concern. HTTP response caching, query-result caching, remote-service response caching and application-level memoisation are different choices; stale-data behaviour and tenant isolation need to be considered for the chosen layer.
Showing artifacts, examples and content with confirmed Node.js applicability. View All CAP
Overview
Supports ETag-based conditional HTTP reads for annotated entities in the CAP Node.js runtime.
A Node.js coverage assessment has not been written. View all flavors
Replicates a service into tenant-aware SQLite for querying with the original service’s queries; @cds.replicate selects entities and exposes TTL, preload and static replication options.
Retains query, request and function results, with expiry and optional entity-write invalidation through @cache annotations.
Caches federated responses through cds-cachingcds-cachingA caching plugin for CAP covering queries, requests, function results and remote service responses, through a read-through API or @cache annotations, with HCQL and MCP support.independent, cap, or retains entity snapshots through cds-data-pipelinecds-data-pipelineDeclarative scheduled pipelines between CAP services — each one a linear READ, MAP, WRITE from a single source to a single target, with tracking, retry, delta support and a management API.independent, cap. Writable delegates require manual invalidation or a shorter TTL.
A Node.js coverage assessment has not been written. View all flavors
Artifacts
Tasks
Cache a remote service response
A read that would call out to a remote service on every request is answered from a retained response until it expires.
Covers a response from a service the application calls out to. Retaining the result of a local query is a separate task, and so is copying the remote data into a local database and querying that.
cds-cachingcache.rt.send takes the request and the remote service and returns the retained response with the ttl the call states. The call is written where the application would otherwise send to the service.
Remaining work: Choose the ttl and call the cache instead of the service. The plugin documents the call, not which reads are worth retaining.
cds-data-federationOn a delegated entity the response strategy routes the federated read through cds-caching, so the retaining is declared on the consumption view rather than written into a handler. The entity strategy answers a different shape of the same wish: it stores a snapshot of the remote projection in secondary SQLite and serves reads from there until the ttl runs out.
Conditions: cache.strategy is left at its default of response cds-caching is installed, which the page names as an optional peer dependency
Remaining work: Install the peer dependency and annotate the view. The strategy selects a cache, it is not a second one; the entity strategy brings cds-data-pipeline and @cap-js/sqlite with it.
@sap/cdsWhether the CAP Node.js runtime retains a remote service response is not established here. Capire's CAP-level Service Integration guide, read on 2026-09-19, covers importing an API, integrating the model and writing the integration logic, and does not mention caching.
Remaining work: Read the remote services and destinations documentation on capire before treating this as settled. Absence from the integration guide is not a statement that the runtime does nothing.
Cache the result of a function call
A function the application would otherwise run on every call returns a value retained from an earlier call until it expires.
Covers retaining what a function returns, under a key that carries the arguments it was called with. Retaining the result of a query and retaining a response from a remote service are separate tasks, and so is where the retained value is kept.
cds-cachingTwo routes to the same outcome. A @cache annotation on a function in the CDS model retains what it returns for the ttl it states, so the decision sits on the declaration. In code, cache.rt.wrap takes a name and the function and hands back a cached version of it, and cache.rt.exec runs a function once with its arguments and retains what came back. The arguments are part of the generated key, so a call with different arguments is held separately.
Remaining work: Name the function and choose the ttl. Whether a function returns the same value for the same arguments stays the application's to decide: the pages read here document the key and the expiry, not which functions are worth retaining or safe to retain.
@sap/cdsWhether the CAP Node.js runtime retains what a function returns is not established here. No capire page documenting a result cache for a function or an action was located for this assessment.
Remaining work: Read the Node.js runtime and performance documentation on capire before treating this as settled. Nothing read here states that the runtime does this, and nothing read here states that it does not.
Keep a cached result from reaching the wrong tenant, user or locale
A result retained for one tenant, one user or one language is not returned to a request that belongs to another.
Covers the key a retained entry is held under and where the store keeps it. Tenant isolation of the application's own data, and the subscription mechanics themselves, belong to multitenancy and are assessed there. Nothing here establishes that a leak was observed or ruled out in any application.
@cap-js-community/commonThe replica is held in a tenant-aware SQLite database, so each tenant queries its own copy. One annotation opts out of that: static replication is stated as non-tenant aware, which is the arrangement for rows that are the same for everyone.
Conditions: The entity is not annotated for static replication, which the README states is not tenant aware
Remaining work: Check every entity annotated for static replication for whether its rows really are the same for all tenants. User and locale are not dimensions this section names at all.
cds-cachingTenant, user and locale are three separate dimensions of the cache key with three different defaults. Tenant awareness turns itself on when multitenancy is detected. User awareness is the application's to set, because a result a handler filters after the query has run carries the same key for every caller. Locale is off, so responses in different languages share one entry until it is switched on. Where the entries sit follows the store: with the cds store each tenant's cache data is held in that tenant's own HDI container.
Conditions: Multitenancy is detected, which is what turns tenant-aware keys on, and the opt-out is not set User awareness is set for caches whose responses vary per user without the query varying Locale awareness is set for caches holding translated texts
Remaining work: Decide per cache which dimensions belong in the key and set the ones that do not default. The plugin documents the switches, not which of an application's responses vary by user or language.
Refresh cached reads after a write
A read answered from a cache reflects a write to the underlying data within a bound the application sets.
Covers what clears or ages out a retained result. Which reads are retained, and where they are kept, are separate tasks.
@cap-js-community/commonThe replica ages out on a time to live given in milliseconds, and the same annotation block carries auto and preload for when the copy is made. Time is the bound this section states.
Remaining work: Set the ttl per entity. Whether a write through the replicated service clears its copy is not stated in the section read here, so the ttl is the only bound this assessment records.
cds-cachinginvalidateOnWrite clears the cached queries for an entity when its data changes, registered as after handlers for CREATE, UPDATE and DELETE. Tags are the other route: keys set under a tag are cleared together by deleteByTag, which is how a write can clear a result assembled from several keys.
Remaining work: Set the flag per entity, or assign tags and call deleteByTag from the handler that writes. A write that reaches the data by another route is not covered by either.
cds-data-federationAutomatic clearing on a write. A write through a writable delegate does not clear the cache under either strategy, and the documentation records that as a stated behaviour with the alternative named.
Remaining work: Clear the entry from the write path, or shorten the ttl so the window is the staleness the application accepts.
Serve repeated reads of a rarely written entity from a cache
Reads of an entity that is read constantly and written rarely are answered inside the application instead of going to the database on every request.
Covers retaining the result of a read. Keeping it fresh after a write is a separate task, and so is where the retained result is kept.
@cap-js-community/commonAnswers the same outcome by a different mechanism: it holds a copy of a service in SQLite and runs the original query against the copy, so the read shape in the application does not change. The README gives the db service as its example.
Remaining work: Install @cap-js/sqlite as a dependency rather than a development dependency, and annotate the entities to replicate.
cds-cachingA @cache annotation on a service entity retains the results of reads on it for the ttl it states, and it sits on the projection rather than in a handler. Where the decision belongs in code instead, cache.rt.run takes the query and the service and returns the result with the ttl the call states.
Remaining work: Decide the ttl entity by entity. The annotation is per projection, so each one is its own decision, and nothing picks the entities worth retaining.
@sap/cdsWhether the CAP Node.js runtime holds a query result between requests is not established by the pages read for this assessment. Capire's generic service providers page documents ETag validation, which answers a conditional request rather than retaining a result, and the service integration guide read on 2026-09-19 does not mention caching at all.
Remaining work: Read the CAP Node.js databases and performance documentation before treating this as settled. Two guessed capire URLs for a performance guide returned 404 on 2026-09-19, so the page that would answer it was not located.
Skip resending a record the client already has
A client that re-reads an entity it has already fetched gets 304 and no body back when the record has not changed since.
Covers a conditional read over HTTP on a served entity. Retaining a query, request or function result inside the application is a separate task, and so is telling a client how long it may reuse what it has without asking again.
@sap/cdsA read carrying If-None-Match is answered with 304 and no body while the annotated element still matches. Capire's example is a GET on Employees/111 with the previous timestamp in the header.
Remaining work: Annotate an element with @odata.etag. Capire annotates modifiedAt from the managed aspect, which changes on each modification, and the client has to send back what it received.
Tell a client how long a served file stays fresh
A client that has fetched a file served by the application keeps using it for a stated number of seconds without asking the application again.
Covers the freshness window the application sends with a response. Answering a conditional request with 304, which still costs a round trip, is a separate task, and so is retaining a result inside the application.
@sap/cdsCapire scopes the Cache Control feature to the Java runtime, so the same annotation is not documented as doing anything on Node.js.
Remaining work: Set the header from the application's own code, or take one of the other mechanisms this capability records. The page does not state what a Node.js runtime does with @http.CacheControl when the annotation is in the model.
Content
Details
Coverage
Comparison properties
These are the questions used to compare what the artifacts do. Values come from their documentation and may apply to a particular ecosystem or runtime. Each recorded value in the comparison opens its statement and sources.
For your agent
- Ref
capability:caching- JSON
- /capabilities/caching.json
- MCP
- Connecting an agent