Exploring Etag and If-Match in NestJS
Optimizing Concurrency Handling with ETag and If-Match
HTTP headers play an important role in building an efficient RESTful API. Two such headers, ETag
and If-Match/If-None-Match
, are instrumental in handling concurrency and caching in RESTful APIs.
In this post, we will delve into Etag
and If-Match/If-None-Match
headers, and explore how to use them in NestJS. Correctly using them will allow us to design a robust RESTful API capable of handling concurrent transactions.
Understanding ETag and If-Match
An Entity Tag, or ETag
, is used to identify a specific version of a resource uniquely. It is a hash or identifier associated with the state of a resource on the server. There are two types of Etag: weak and strong . The weak Etag allows for flexibility in representing semantically equivalent content. Strong Etag is suitable for the resource representation that is byte-for-byte identical for all users.
When a client requests a resource, the server attaches an ETag to the response. Subsequent requests from the client can include this ETag in the If-None-Match
header. The header is used to determine whether the resource has changed, the server responds with a “304 Not Modified
” status if the resource has not changed, reducing unnecessary data transfer and enhancing performance.
On the other hand, the If-Match
header is employed for optimistic concurrency control. It can ensure that an operation, such as an update or deletion, is only performed if the provided ETag
matches the current state of the resource on the server.
The significance of ETag
and If-Match
lies in their ability to prevent conflicting updates. In scenarios where multiple clients modify the same resource concurrently, these headers act as safeguards. ETag
enables efficient caching and reduces unnecessary data transfers while If-Match
ensuring that updates occur only when the client possesses the latest resource version. Together, they contribute to a more resilient interaction between clients and servers in RESTful APIs.
Generate and return an Etag
To generate an ETag
in NestJS using the [etag](https://github.com/jshttp/etag)
library, follow the below steps to install and import the library.
1 | // install it |
Then, we can generate an Etag
with a one-line call.
1 | const data = // your data here; |
Please note that the etag function only accepts string, buffer, or steams. We can’t pass objects or arrays directly, but a workaround exists below.
1 | const arrayData = [1, 2, 3]; |
Then, we can return the Etag
in the response header for a GET request.
1 | 'etag') ( |
Please note that we need to set the passthrough
option to true in the @Res({ passthrough: true })
decorator because injecting the @Res
will disable the default route handling by default.
Use Etag for Caching
One of the primary purposes of Etag
is caching. After calling the GET the first time, the client retrieves the Etag
in response, and then the subsequent requests can include this ETag
in the If-None-Match
header.
an example of if-none-match header in the request
If-None-Match: “bfc13a64729c4290ef5b2c2730249c88ca92d82d”
In the GET endpoint, add a check to compare the Etag
in If-None-Match
to determine whether a resource has been modified. We can return a 304 Not Modified response if the resource has not been changed.
1 | () |
Upon receiving the response with a 304 status, most modern browsers will fetch the resource from the local cache.
If-Match for Optimistic Concurrency Control
The If-Match
header is commonly used to facilitate optimistic concurrency control. What exactly is optimistic concurrency control?
Optimistic concurrency control is a strategy for managing multiple users attempting to modify the same piece of data simultaneously. Instead of locking the data and preventing others from making changes, optimistic concurrency assumes that conflicts are rare. Users can make changes independently, but before saving their modifications, the system checks if someone else has modified the data. If no changes conflict, the modifications are accepted; otherwise, the system prompts users to resolve the inconsistency.
Now, let’s see how to implement it in a PUT request.
1 | ':id') ( |
When a client sends a request to update a resource, the client includes the current ETag
of the resource in the If-Match
header. The server then checks if the provided ETag
matches the current state of the resource. If there’s a match, the update proceeds; otherwise, the server returns a “412 Precondition Failed
” status, indicating that another party has modified the resource.
The same approach can be used not only for PUT but also applicable to DELETE and PATCH requests.
If-Match vs If-None-Match
It is worth highlighting that If-Match
and If-None-Match
headers serve different purposes in the context of ETags
. Here’s a breakdown of their differences:
If-Match
Header
- It is used in requests to operate (e.g., update or delete) only if the provided
ETag
matches the current ETag of the resource on the server. - If the ETag matches, the operation is performed; otherwise, the server responds with a “412 Precondition Failed” status, indicating that another party modified the resource.
If-None-Match
Header
- It is used in requests to get a resource only if its ETag does not match the specified ETag(s).
- If the ETag matches, the server responds with a “304 Not Modified” status, indicating that the client’s cached version is still valid and there’s no need to transfer the resource again.
We discussed the usage of Etag and If-match/if-none-match in this post. In many cases, it is a good practice to use a combination of etag and if-match for optimistic concurrency control. I hope you have learned one thing or two in this post.
- Title: Exploring Etag and If-Match in NestJS
- Author: Sunny Sun
- Created at : 2024-03-05 00:00:00
- Updated at : 2024-07-15 19:37:14
- Link: http://coffeethinkcode.com/2024/03/05/exploring-etag/
- License: This work is licensed under CC BY-NC-SA 4.0.