REST (Representational State Transfer) is an architectural style for building distributed systems like web services. It relies on stateless client-server communication and uses HTTP as the protocol. REST aims to gain scalability, performance and reliability benefits from the web architecture. The cacheable constraint is one of the constraints of REST that enables caching mechanisms for improved performance.
What is caching and why is it used?
Caching is the temporary storage of data in order to reduce requests to the back-end system or database. It improves read performance and reduces network traffic by serving some requests from cache instead of making a network round trip to the origin server. This becomes especially important for systems with high loads. The benefits of caching include:
- Reduced latency – Reading data from a cache is faster than fetching it from the origin server.
- Better scalability – Caching eases load on the backend infrastructure such as databases and application servers.
- Cost savings – Less requests to backend can reduce network bandwidth and server costs.
Web caching is commonly used for static assets like images, CSS and JavaScript files. But caching can also happen for dynamic APIs and resources to improve performance.
Cacheable constraint in REST
The cacheable constraint suggests that data within a response should be labeled explicitly or implicitly as cacheable or non-cacheable. This allows caches and clients to determine if a response can be reused from cache later or not.
There are two types of caching:
- Private caching – The cached response is only usable by that client.
- Public caching – The cached response can be reused by multiple clients.
In REST, responses can be cached if they contain no client specific state. This allows reuse of those cached responses by other clients as well.
Implicit caching
By default, responses are implicitly cacheable in REST unless indicated otherwise. For HTTP based REST APIs, this means a 200 OK response without any additional headers can be cached by default. The original REST dissertation says:
“Unless a response contains both an expiration time and one or more cache validators, a cache MAY assume that the representation is fresh for an unspecified period of time.”
This allows implicit caching of resources and responses without any extra configuration. Clients can reuse these cached responses to avoid frequent requests to origin server.
Explicit caching
REST APIs can also indicate explicitly if a response is cacheable or not. This is done using HTTP headers like:
- Cache-Control – Specifies public/private caching and max age.
- Expires – Expiration timestamp for cached response.
- ETag – Token for cache validation and conditional requests.
For example:
Cache-Control: max-age=3600, public Expires: Thu, 05 Oct 2023 20:00:00 GMT
This labels the response as cacheable by any client for 3600 seconds. The Expires header also indicates when the cached response should be considered stale after.
Preventing caching
To prevent caching, the Cache-Control header can be used with values like:
- no-store – Prevents caching entirely.
- private – Allows private caching only.
- no-cache – Allows caching but requires validation before use.
For example:
Cache-Control: no-store
Responses for user profile update APIs can have such headers to prevent shared caching.
Cache validation
When a cached response is reused by a client, the server may need to validate if the cache is still current. Validation ensures the client has the latest representation.
REST uses ETag, Last-Modified headers and conditional GET requests for cache validation.
ETag
ETag or entity-tag is a unique identifier for a resource version computed from its contents. It is sent as a HTTP response header:
ETag: "82e22293907ce725faf5dc4bdaefdff4"
Clients can send this etag back in conditional GET requests to validate cache:
GET /api/orders/123 If-None-Match: "82e22293907ce725faf5dc4bdaefdff4"
If etag matches, server returns 304 Not Modified without any body indicating cached version is still current. Else, it returns 200 OK with full response.
Last-Modified
Last-Modified is the timestamp of when the resource was last changed on the server. It can similarly be used for validation:
Last-Modified: Tue, 04 Oct 2022 10:00:00 GMT GET /api/orders/123 If-Modified-Since: Tue, 04 Oct 2022 10:00:00 GMT
If unchanged, server returns 304 status code.
Both ETag and Last-Modified help avoid transferring unchanged data again.
Cache invalidation
When data changes on the server, any cached copies need to be invalidated and refreshed. This ensures new requests fetch updated data instead of stale cache.
REST APIs invalidate cache mainly using:
- Cache-Control headers with reduced max-age or no-cache.
- New ETag values generated from updated content.
For frequently changing resources, max-age can be kept low or set to no-cache. New updates also result in new ETag values which clients won’t have, so validation fails and new data is fetched.
Cache invalidation becomes essential for APIs dealing with mutable data.
HTTP caching
In addition to client caching, HTTP also provides built-in caching mechanisms via caches like Varnish, Nginx, CDNs. These intermediate caches reduce traffic to origin servers.
Key headers like Cache-Control, ETag, Expires handle caching at HTTP caches too. REST benefits from reusable HTTP caching infrastructure instead of reinventing at application layer.
Caching in practice
Here are some best practices for effective caching in REST APIs:
- Enable caching for endpoints returning static, read-only data like product catalog.
- Use explicit cache headers over implicit caching.
- Watch out for frequently changing resources, use lower max-age and validators.
- Invalidate cache on data mutations like POST, PUT, DELETE requests.
- Use separate caches for static assets, and API responses.
- Implement hierarchical caching with centralized caches.
Careful use of caching improves performance, scalability and efficiency of REST services.
Conclusion
The cacheable constraint is a key aspect of REST that enables caching to boost performance. Responses can be labeled cacheable or non-cacheable using headers like Cache-Control. Validation techniques like ETag ensure cache freshness. Caching improves speed and scalability if used judiciously, especially for resources that are static or slow-changing. The cacheable constraint also demonstrates how REST utilizes existing HTTP capabilities for better efficiency.