Quadratic

Share this post

How Meta, Google, Github and others leverage HTTP conditional requests to build robust REST APIs

quadratic.fm

How Meta, Google, Github and others leverage HTTP conditional requests to build robust REST APIs

Deep-dive case studies in how tech giants, online retailers and one national railway company use HTTP conditional requests in their REST APIs.

Ilija Eftimov
Oct 26, 2022
2
Share this post

How Meta, Google, Github and others leverage HTTP conditional requests to build robust REST APIs

quadratic.fm

Hey folks, Ilija here! 👋 Welcome to another issue of Quadratic, published on quadratic.fm.

Every week I write advice on how to become a better product engineer, backed by my own experience of over a decade, my own research and learning from others’ experiences. Last week I wrote about:

Quadratic
How to support your Engineering team as a Product Manager when shipping APIs
Hey folks, Ilija here! 👋 Welcome to the 2nd issue of Quadratic, published on quadratic.fm. Every week I write advice on how to become a product engineer, backed by my own experience of over a decade, my own research and learning from others’ experiences. Last week I wrote about…
Read more
5 months ago · Ilija Eftimov

To receive all full articles and support Quadratic, consider subscribing 👇

Now, onto the latest issue…


In this issue, we will look at using HTTP conditional requests to improve our APIs’ performance and robustness. And we’ll do that by learning from eight massive organizations that use conditional requests for their HTTP APIs: Salesforce, Github, Firebase (Google), Microsoft, Zalando, Meta, and the Schweizerische Bundesbahnen (Swiss Federal Railways). These case studies will be enlightening - we'll review each organization's approach to conditional requests and their tradeoffs.

But before we go too deep, first, we'll skim over the basics - I want to give you a refresher on conditional requests so you can better enjoy the newsletter. If you are not familiar with conditional requests, I recommend reading my deep dive on the topic on my blog.

What are conditional requests in HTTP

Conditional requests are a mechanism native to HTTP, defined in RFC-7232. Conditional requests are a flexible mechanism because they enable different use cases, such as:

  • verifying the integrity of a document, like when resuming a download

  • preventing lost updates when uploading or modifying a document on the server

  • caching documents by avoiding sending additional bytes over the network.

Conditional requests include headers that define a precondition. An HTTP server will evaluate the request's precondition before executing the action against the target resource. Based on the precondition evaluation, the response can vary.

Validators and preconditions

Validators and preconditions are two types, or categories, of special HTTP headers:

  1. Timestamp of last modification of the resource - the Last-Modified header

  2. A unique string representing the resource version - the ETag (entity tag) header

The validators are attributes of the HTTP response that clients can use to build preconditions for subsequent requests to the same resource. For example, a combination of a validator with a precondition would be:

If-None-Match: W/"1117028508"

The possible precondition headers, provided by HTTP, are:

  • If-Match

  • If-None-Match

  • If-Modified-Since

  • If-Unmodified-Since

  • If-Range

If you’d like to go in-depth on the semantics of each of these headers, peruse section 3 of RFC-7232.

How conditional requests relate to APIs

REST API requests are nothing more than HTTP requests with additional semantics. API requests have all of HTTP’s mechanisms at their disposal, so why not put them into use?

By using conditional requests we can make the clients and the APIs more performant, and the user experience snappier. With conditional requests, clients can use the locally cached responses and APIs can avoid generating and sending those costly bytes over the network. Also, conditional requests can help APIs avoid mutation conflicts on high-throughput endpoints. Lots of potential benefits just by using a single HTTP header.

But unfortunately, in my experience, conditional requests are not widely used with APIs. So, I'd like us to change that. As you read through the different usage patterns and case studies, think about how you can apply conditional requests in your APIs.

Usage patterns

Conditional requests have two prominent roles: they can serve as a way to tell the client to use a cached response and to prevent race conditions while mutating data via an API. Of course, there are other ways to achieve the same result for complete transparency, but conditional requests are the HTTP-native approach.

Caching

The typical way you will see conditional requests used for caching is:

  1. The client sends a request with a validator and a precondition header

  2. The server accepts the request, uses the validator and precondition to validate the request

  3. Based on the precondition evaluation, the server decides whether the prerequisite is satisfied or not

  4. If the condition demands that the server must render a new response, the server obliges

  5. If not, then the server returns an HTTP 304 Not Modified response

Let’s look at a more practical example. Imagine an API that has the /v1/users resource. The client can use the GET /v1/users/:id endpoint to get the user with a particular ID. For example, to fetch the user with ID=1, the client will send a GET HTTP request to the /v1/users/1 endpoint.

Let’s look at an example response:

GET /v1/users/1

{
  "id": 1,
  "name": "Jane",
  "surname": "Doe",
  "age": 20,
  "location": "Oxford, England"
  "last_login": 1666374784
}

This JSON represents the user Jane Doe, with ID=1. Now, when we would hit the endpoint with curl (or another HTTP client), we’d see something like this:

$ curl -X GET /v1/users/1

HTTP/2 200
date: Fri, 21 Oct 2022 19:48:44 GMT
server: api-srv-12
cache-control: no-cache
last-modified: Wed, 19 Oct 2022 15:20:24 GMT
content-type: application/json
etag: W/"1117028508"
age: 0

{
  "id": 1,
  "name": "Jane",
  "surname": "Doe",
  "age": 20,
  "location": "Oxford, England"
  "last_login": 1666374784
}

If you look at the bolded response headers, you’ll notice that there are two: Last-modified and ETag.

The Last-Modified response header is a timestamp that indicates when the origin server believes the selected resource was last modified while handling the request. ETag is an opaque identifier whose exact value is implementation dependent.

Case study: Github

According to Github's API documentation, their API returns an ETag response header. In addition, for some resources, the API also returns a Last-modified header. They encourage using the values of these headers to make subsequent requests to those resources using the If-None-Match and If-Modified-Since headers, respectively. The server will return a 304 Not Modified if the resource has not changed.

For example, when the client fetches a user via the API:

$ curl -I https://api.github.com/user
> HTTP/2 200
> ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
> Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

The response might contain the ETag and Last-Modified response headers. The values of the headers allow the client to send a subsequent request to the same request using a precondition with the validator, e.g.:

$ curl -I https://api.github.com/user -H 'If-None-Match: "644b5b0155e6404a9cc4bd9d8b1ae730"'
> HTTP/2 304
> ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
> Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

If the resource hasn't changed, the ETag value will still be valid, and the server will return just an HTTP 304 Not Modified, signaling the client that it already has the freshest representation of the resource. Another alternative is to use the If-Modifed-Since precondition, with the timestamp from the Last-Modified header, e.g.:

$ curl -I https://api.github.com/user -H "If-Modified-Since: Thu, 05 Jul 2012 15:31:30 GMT"
> HTTP/2 304
> Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

If the resource hasn’t changed in the meantime, the server will also return an HTTP 304 Not Modified.

Github's approach to conditional requests is excellent because receiving an HTTP 304 response does not count against the client's rate limit. Therefore, Github encourages clients to use the precondition headers as much as possible. These HTTP 304s won't matter if there's a CDN between the client and the origin API servers. If there's a CDN in place, these requests never reach Github's origin servers, and the CDN returns the HTTP 304s.

You can read more in Github’s documentation.

Case study: Salesforce Lightning Platform REST API

To support response caching, Salesforce’s REST API allows conditional request headers that follow the standards defined by the HTTP 1.1 specification. Salesforce’s API is very flexible – it supports most HTTP precondition headers: If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since. If-Range is not permitted.

The documentation also discerns strong and weak validation, a nice little touch for the reader. Salesforce suggests using If-Match or If-None-Match for strong validation (via ETag) and If-Modified-Since and If-Unmodified-Since for weak validation (via the Last-Modified timestamp).

For example, using weak validation:

$ curl https://instance.salesforce.com/services/data/v52.0/limits/ -H "Authorization: Bearer token"
> HTTP/2 200
> ETag: "644b5b0155e6404a9cc4bd9d8b1ae730"
> Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

Then, you can use the value of the Last-Modified header in the subsequent request, similarly to Github above:

$ curl https://instance.salesforce.com/services/data/v52.0/limits/ -H "Authorization: Bearer token" -H "If-Modified-Since: Thu, 05 Jul 2012 15:31:30 GMT"
> HTTP/2 304
> Last-Modified: Thu, 05 Jul 2012 15:31:30 GMT

You can read more in the API documentation.

Case study: Meta Marketing API

I found the approach that Meta took in their Marketing API interesting. Like GitHub and Salesforce above, they use the HTTP mechanics of conditional requests for their APIs.

An example of strong validation using ETag:

$ curl -i "https://graph.beta.facebook.com/me/adaccounts?access_token=TOKEN"

> HTTP/1.1 200 OK
> Content-Type: text/javascript; charset=UTF-8
> ETag: "7776cdb01f44354af8bfa4db0c56eebcb1378975"

{"data":[{"id":"act.......

Using the ETag from the first request in combination with If-None-Match:

$ curl -i -H "If-None-Match: \"7776cdb01f44354af8bfa4db0c56eebcb1378975\"" "https://graph.beta.facebook.com/me/adaccounts?access_token=TOKEN"

> HTTP/1.1 304 Not Modified
> Content-Length: 0

As expected, the resource has remained unchanged so the API returned HTTP 304.

The intriguing part is batch calls: the Meta Marketing API supports batch API calls using a single HTTP call. In other words, the API multiplexes the single API call, yet it returns a single response.

For example:

$ curl -i "curl -F 'access_token=TOKEN' -F 'batch=[ 
  {"method":"GET", "relative_url": "?ids=6003356308839,6004164369439" }, 
  {"method":"GET", "relative_url": "act_12345678/ads?campaign_ids=[6003356307839, 6004164259439]"}]'
 https://graph.facebook.com"

The above request will result in two different requests, whose responses will be combined, and each will return a separate ETag:

...{"name":"ETag","value":"\"21d371640127490b2ed0387e8af3f0f8c9eff012\""}...      
...{"name":"ETag","value":"\"410e53bb257f116e8716e4ebcc76df1c567b87f4\""}...

A client can use the above ETags in subsequent batch requests, just like with single requests:

$ curl -F 'access_token=TOKEN' -F 'batch=[
  {"method":"GET", "headers":["If-None-Match: \"21d371640127490b2ed0387e8af3f0f8c9eff012\""], "relative_url": "?ids=6003356308839,6004164369439" },
  {"method":"GET",  "headers":["If-None-Match: \"410e53bb257f116e8716e4ebcc76df1c567b87f4\""], "relative_url": "act_12345678/ads?campaign_ids=[6003356307839, 6004164259439]"}]' 
https://graph.facebook.com

Which can result in one or multiple responses returning an HTTP 304:

[{
    "code": 304,
    "body": null
},
{
    "code": 304,
    "body": null
}]

I found the curl syntax above a tad weird, as it's injecting JSON payload in a multipart/form-data Content-type format.

Meta suggests that the formatting might affect the ETag value, unlike the other case studies above. They've added this callout because the API generates the ETag using the complete response from the API call, including its formatting. The user agent string may impact the response format; therefore, Meta recommends "keeping your user agent consistent between calls made from the same client."

In contrast to Github's rate-limiting policies, Meta's policy states that while conditional requests using ETag help reduce data traffic, If-None-Match GET requests still count against the rate limits of the client. It’d be a nice touch if it didn’t, but I am sure they have a good reason to keep things as they are.

See more in their API documentation.

Optimistic Locking

We should always look out for race conditions in computing, and HTTP APIs are no exception to the rule. For example, if two clients try to modify the same resource, the client's in-memory resource might differ from the one on the server, so in case of an update, the client might overwrite prior updates done by different clients. For example, consider this flow:

In the sequence diagram above, client B overrides earlier changes done by client A. A typical race condition scenario. Luckily, HTTP conditional requests are here to save the day.

Conditional requests are a mechanism to avoid race conditions when updating a resource. HTTP already provides precondition headers that, when supplied, can stop two clients from overwriting each other's changes. Let's update the sequence diagram above to use conditional requests:

The difference between the two diagrams is the usage of preconditions and ETags to validate the resource's state before changing it. Both clients use ETags with the If-Match header. If-Match's semantics allow updating the resource only if the resource's state matches the provided ETag value.

In the diagram, client A modifies the user's state with ID=1, and the API returns a different ETag value. So, when client B tries to alter the user with the old ETag value, the API returns a client error stating that the precondition has failed. The precondition headers and ETags combo are powerful for optimistic locking and avoiding race conditions.

Case study: Zalando

Photo by Claudio Schwarz on Unsplash

I love how in-depth Zalando went in this rabbit hole. It's lovely to see an organization making an opinionated but educated technical choice. Their guidelines are not the most satisfactory, but I am sure they're the best within their context and constraints. I hope, at least.

Zalando has looked at four alternatives for optimistic locking:

  1. ETags with If-Match

  2. ETags in result entities, with If-Match

  3. Version numbers

  4. Last-Modified with If-Unmodified-Since

ETag + If-Match

As an approach, this is identical to the sequence diagram example we saw above. For example:

$ curl -X GET /orders/BO0000042

> HTTP/1.1 200 OK
> ETag: abc123
> { "id": "BO0000042", ... }

$ curl -X PUT /orders/O0000042 -H "If-Match: abc123" -d '{ "id": "O0000042", ... }'

> HTTP/1.1 204 No Content

Or, if there was an update since the GET and the entity’s ETag has changed:

> HTTP/1.1 412 Precondition failed 

ETag in result entities, with If-Match

Zalando provides this pattern similar to the previous one, with the core difference being that the resource result embeds the ETags. For example:

$ curl -X GET /orders

> HTTP/1.1 200 OK
> {
>   "items": [
>     { "id": "O0000042", "etag": "osjnfkjbnkq3jlnksjnvkjlsbf", "foo": 42, "bar": true },
>     { "id": "O0000043", "etag": "kjshdfknjqlowjdsljdnfkjbkn", "foo": 24, "bar": false }
>   ]
> }

$ curl -X PUT /orders/O0000042 -H 'If-Match: osjnfkjbnkq3jlnksjnvkjlsbf' -d '{ "id": "O0000042", "foo": 43, "bar": true }'

> HTTP/1.1 204 No Content

The ETag for every entity is an additional attribute. In a list response (like the one above), every entity contains a distinct ETag that users can use in subsequent PUT requests. The ETag is read-only, and the API doesn't expect its presence in the PUT request payload.

While this works, it leaks HTTP mechanics into the resource representations. This leaking is why Zalando has to call out that ETag is not an actual attribute that every resource has but a "special" one. It also mixes concerns - a performance optimization aspect of the API is blended into the resource representation. Lastly, how could they scale this out to Last-Modified? Would Last-Modified also become a response attribute? For example:

$ curl -X GET /orders

> HTTP/1.1 200 OK
> {
>   "items": [
>     { "id": "O0000042", "etag": "osjnfkjbnkq3jlnksjnvkjlsbf", "foo": 42, "bar": true, "last-modified": "Sat, 22 Oct 2022 19:22:16 GMT" },
>     { "id": "O0000043", "etag": "kjshdfknjqlowjdsljdnfkjbkn", "foo": 24, "bar": false, "last-modified": "Fri, 21 Oct 2022 20:12:55 GMT" }
>   ]
> }

Version numbers

Each entity contains a version property. It's what it says on the lid - a version number. Think of it as version control for the entity. After an update, the API returns the entity's version in the response. The API validates the version number – if the version in the request is old, the API will reject the request. If it's still fresh - it will perform the update. And increment the version number.

Zalando makes an interesting semantics point here: an update of a resource with a version implies a modification of the resource by the service itself (i.e., incrementing the version); Zalando expects such endpoints to use the POST HTTP method. Practically, instead of using PUT /orders/0000042 to update the Order, their guidelines suggest using POST /orders/0000042.

For example:

$ curl -X GET /orders

> HTTP/1.1 200 OK
> {
>   "items": [
>     { "id": "O0000042", "version": 1,  "foo": 42, "bar": true },
>     { "id": "O0000043", "version": 42, "foo": 24, "bar": false }
>   ]
> }

$ curl -X POST /orders/O0000042 -d '{ "id": "O0000042", "version": 1, "foo": 43, "bar": true }'

> HTTP/1.1 204 No Content

If the resource is updated and the version number in the database is higher than the one given in the request body, the API returns a client error:

> HTTP/1.1 409 Conflict

Last-Modified with If-Unmodified-Since

The combination of these headers is an approach that predates ETag. Every response contains a Last-Modified header with an HTTP date. When requesting an update using a PUT request, the client has to provide this value via the header If-Unmodified-Since. If the Last-Modified date of the entity is bigger than the header value, the server will reject the request.

For example:

$ curl -X GET /orders

> HTTP/1.1 200 OK
> Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT

{
  "items": [
    { "id": "O0000042", ... },
    { "id": "O0000043", ... }
  ]
}

$ curl -X PUT /block/O0000042 -H "If-Unmodified-Since: Wed, 22 Jul 2009 19:15:56 GMT" -d '{ "id": "O0000042", ... }'

> HTTP/1.1 204 No Content

Or, if there was an update of the resource or the provided dates are earlier than the last modified date:

> HTTP/1.1 412 Precondition failed

Zalando considers the ETag in result entities, with If-Match and the Last-Modified with If-Unmodified-Since superior to the rest. The ETag + If-Match option is the simplest, as ETags are more flexible. They don't require building custom versioning of resources, nor do they pollute the response entities themselves. Even though my preference differs from Zalando’s, I was elated to learn their REST API guidelines - I recommend reading them here.

Case study: Swiss Federal Railways

Riding on a train through Switzerland is on my bucket list. It's a lovely experience if I can judge it by all the Instagram Reels, TikToks, and photos I've seen. Breathtaking nature. Modern and tidy trains. Wonderful routes. But beyond that, the Swiss Federal Railways have another thing going well: well-documented REST API principles.

Photo by Chris Henry on Unsplash

In their API principles, the Schweizerische Bundesbahnen (SBB) states that when their engineering teams build APIs they should consider supporting ETag + If-Match/If-None-Match. By supporting the ETag validator plus the precondition headers, they “expose conflicts and prevent the ‘lost update’ or ‘initially created’ problem.”

I found their approach interesting because, unlike Zalando, they don't advise supporting Last-Modified or the If-Modified-Since/If-Unmodified-Since preconditions. But they do recommend that the ETag is either:

  • a hash of the response body

  • a hash of the last modified field of the entity, or

  • a version number or identifier of the entity version

SBB doesn't make compromises like Zalando in terms of exposing versions or ETags in the response entities. But they still suggest that API should disclose this data as the ETag on the response. The suggestion is good - SBB wants to have flexible ETag values while still generating ETags opaquely.

The first and the last option are reasonable: using the entity's version number or just hashing the response body as ETag will do fine. The alternatives might appear with similar effectiveness, but they're not. Per RFC-7232, ETags should change when the observable representation of the resource changes.

Often APIs expose a limited number of entity attributes in the API. Or some fields in the API response might not exist on the entity - the backend can create them dynamically. If such a non-exposed attribute is updated, the backend will increment the version number resulting in a changed ETag. But the ETag shouldn't change. The ETag must change only when the observable state of the resource changes. Sure, if the API exposes all fields, my counter-argument is moot.

SBB's backend can keep an allowlist with eligible attributes to bump the version number to avoid bumping the version (and changing the ETag) on each update. But this is error-prone: the allowlist must be maintained and tested. Not that this characteristic makes version numbers a bad option, but it does have this pitfall. SBB decided to live with it. Good for them.

Hashing the Last-modified datetime (option two) is the least favorable. Still, both Zalando and SBB prefer it.The problem with hashing Last-modified is in its resolution: a second. In other words, if a single entity gets updated multiple times within a second, the response will not reflect every change in the ETag. Sure, only some of us can boast with such high-scale traffic for this to be a problem. And maybe it's not a problem for SBB, too.

I recommend reading the rest of the Schweizerische Bundesbahnen’s API principles here.

Case study: Firebase

Firebase uses ETag and Last-modified to do optimistic locking on writes as well. We won't go deeper as it's similar to the other examples above. Yet, they do something the other companies don't discuss: optimistic locking on deletion.

Firebase is a set of hosting services for applications. It offers NoSQL and real-time hosting of databases, content, social authentication, notifications, or services, such as a real-time communication server. Google's investment in Firebase began as a simple NoSQL database in the cloud into a full suite of tools for hosting data for apps.

Firebase's context is essential to what we are going to discuss. Because it stores data (which is very important), Firebase provides its API clients with confidence mechanisms. Confidence is essential - clients must be sure they won't delete data whose state they're unfamiliar with. The confidence aspect is where optimistic locking on deletion comes into play: the client can delete only the data they think they're deleting.

For example, to fetch data from a location, the client can request an ETag with the X-Firebase-ETag request header:

$ curl -i 'https://test.firebaseio.com/posts/12345/upvotes.json' -H 'X-Firebase-ETag: true'

This will return the object with an ETag, as requested:

HTTP/1.1 200 OK
Content-Length: 6
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
ETag: number-ten-etag
Cache-Control: no-cache

10

If the client wants to delete the data at the location, it must send a DELETE request. As with any race condition, the state of that location was 10 when the client requested it. But it can change soon. When the client wants to delete the 10 another client might've already changed it to 11. To avoid deleting changed data, the client has to supply the ETag in the If-Match precondition on the DELETE request:

$ curl -X DELETE 'https://test.firebaseio.com/posts/12345/upvotes.json' -H 'if-match: number-ten-etag'

If the precondition is satisfied, the 10 will be gone. If it’s not satisfied, Firebase will reply with an HTTP 412 Precondition Failed:

HTTP/1.1 412 Precondition Failed
Content-Length: 6
Content-Type: application/json; charset=utf-8
Access-Control-Allow-Origin: *
ETag: number-twelve-etag
Cache-Control: no-cache

12 // New value of the data at the specified location

You can read more about this in Firebase’s documentation.

Limit upsert operations

For those that haven't met the term before, "upsert" is a short-hand for update+insert. In other words: update the resource if it's present; otherwise, insert it. It might be an over-generalization, but PUT methods in REST APIs should support upserting. I'll admit that this can vary per domain, so adopting the pattern is not equally spread.

Clients can use conditional requests in conjunction with upsert operations on HTTP APIs: an upsert ordinarily operates by creating an entity if it doesn't exist; otherwise, it updates an existing entity. However, ETags can be used to constrain upserts further to either prevent creates or to prevent updates.

Imagine a scenario where another client has deleted a resource, and our client tries to upsert the same resource again. Should the resource remain deleted? Or should we let it get recreated? Again, this complexity is manageable via ETags.

Case study: Microsoft’s Dataverse Web API

The Dataverse API allows control of the upserting behavior by using the If-Match precondition header. Using the precondition, the client can ask endpoints with a default upsert behavior to work as update-only endpoints. In other words, by supplying a special If-Match header, the API won’t create a new entry if the requested entity doesn’t exist.

For example, to turn an upsert endpoint into an update-only endpoint, you can add an If-Match header to the request with a value of *:

$ curl -X PATCH /api/data/v9.0/accounts(00000000-0000-0000-0000-000000000001) -H 'If-Match: *' -d '{  
    "name": "Updated Sample Account ",  
    "creditonhold": true,  
    "address1_latitude": 47.639583,  
    "description": "This is the updated description of the sample account",  
    "revenue": 6000000,  
    "accountcategorycode": 2  
}'

If the account with ID 00000000-0000-0000-0000-000000000001 already exists, the API will return an HTTP 404:

HTTP/1.1 404 Not Found  
OData-Version: 4.0  
Content-Type: application/json; odata.metadata=minimal  
  
{  
 "error": {  
  "code": "",  
  "message": "account With Id = 00000000-0000-0000-0000-000000000001 Does Not Exist"
 }  
}  

Another example is preventing an upsert endpoint from updating when the entity is present. In other words, using the If-None-Match precondition header, we can change the upsert endpoint only to create the entity if it doesn't exist. This behavior is useful when there is the possibility that a record with the same ID value already exists in the system, and you may not want to update it.

For example:

$ curl -X PATCH /api/data/v9.0/accounts(00000000-0000-0000-0000-000000000001) -H 'Content-Type: application/json' -H 'If-None-Match: *' -d '{  
    "name": "Updated Sample Account ",  
    "creditonhold": true,  
    "address1_latitude": 47.639583,  
    "description": "This is the updated description of the sample account",  
    "revenue": 6000000,  
    "accountcategorycode": 2  
}'

The API will return a normal response with status 204 No Content if the entity doesn't exist. However, when the entity exists, the API will return the following response with status 412 Precondition Failed:

HTTP/1.1 412 Precondition Failed
  
{  
  "error":{  
   "code":"",  
   "message":"A record with matching key values already exists."
  }  
}

For complete transparency: I haven't used APIs with such mechanisms before, but I found the use case worth pointing out. You can read more about this in the Dataverse Web API documentation.


And that’s it for this week! If you liked this, consider doing any of these:

1) ❤️ Share it — Quadratic lives thanks to word of mouth. Share the article with your team or with someone to whom it might be useful!

Share

2) ✉️ Subscribe — if you aren’t already, consider becoming a subscriber. Seeing folks read the newsletter energizes me and gives me ideas to write.

3) 🧠 Let’s chat — I am always on the lookout for questions to answer, to help out with challenges, or topic ideas to cover. Let me know what’s on your mind!

Share topic idea 💡

Until next time,
Ilija

2
Share this post

How Meta, Google, Github and others leverage HTTP conditional requests to build robust REST APIs

quadratic.fm
Previous
2 Comments
Vladimir
Nov 1, 2022Liked by Ilija Eftimov

It's great article, thank you!

Expand full comment
Reply
1 reply by Ilija Eftimov
1 more comment…
TopNewCommunity

No posts

Ready for more?

© 2023 Ilija Eftimov
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing