<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: James Bowes</title>
    <description>The latest articles on DEV Community by James Bowes (@jbowes).</description>
    <link>https://dev.to/jbowes</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F60535%2Feb78d4f5-a76b-4ed1-ab56-ca7d06e020cb.jpeg</url>
      <title>DEV Community: James Bowes</title>
      <link>https://dev.to/jbowes</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jbowes"/>
    <language>en</language>
    <item>
      <title>Strategies for Asynchronous APIs</title>
      <dc:creator>James Bowes</dc:creator>
      <pubDate>Sat, 17 Apr 2021 20:57:24 +0000</pubDate>
      <link>https://dev.to/jbowes/strategies-for-asynchronous-apis-5924</link>
      <guid>https://dev.to/jbowes/strategies-for-asynchronous-apis-5924</guid>
      <description>&lt;p&gt;Most APIs will require some form of non-blocking or asynchronous mechanisms for specific endpoints. It could be an import task that takes minutes or hours, report generation, or even deleting a resource. In any case, whatever the caller is asking for can't be done immediately, but the caller would like to know when its done (for example, so a user can view the report) and may like to know about progress (for example, so they can show import task status in a UI). If you're lucky, you'll catch most of these at the start, though inevitably some change in requirements, features, or performance characteristics will require changing an API from blocking or non-blocking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Required machinery
&lt;/h2&gt;

&lt;p&gt;If a given API endpoint must be asynchronous, you can assume the work it has to do will take longer than a single HTTP request. Further, if the work will take longer than a single HTTP request, you can assume it will take longer than the lifetime of your server process.&lt;/p&gt;

&lt;p&gt;You'll need to decouple the work from HTTP requests (say, with a background worker process or thread), and make the work durable and resilient to process crashes / restarts (with task queues, etc). With care, you can make these changes before modifying existing public APIs or introducing new ones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pretend and defer
&lt;/h3&gt;

&lt;p&gt;Pretending that the API is still synchronous, and deferring expensive work, is often the best option for retrofitting existing APIs. If your API continues to pretend the action is synchronous, you can keep the same interface.&lt;/p&gt;

&lt;p&gt;This strategy works best for actions that can tolerate &lt;a href="https://en.wikipedia.org/wiki/Eventual_consistency"&gt;eventual consistency&lt;/a&gt; in some of their data, or for actions that can mitigate inconsistency via other means.&lt;/p&gt;

&lt;p&gt;Pretend and defer is often applied to user / account deletion. An account deletion may require cascading deletes to all owned data in the same service, calls to other services to delete owned data, calls to third party services to remove records of the account, and possibly some time-delayed actions to perform accounting at the end of a billing period.&lt;/p&gt;

&lt;p&gt;Instead of blocking and waiting for all this work to complete, an account deletion API can mark the account as disabled and revoke any authentication tokens, effectively making the account inaccessible. Any expensive work is then done later on, either via direct work scheduling from the API, or through bulk cleanup processes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Block and de-duplicate
&lt;/h3&gt;

&lt;p&gt;For cases where an existing API endpoint's work can't be deferred, you can maintain the existing API (until it's been removed via a deprecation policy), run the work in a background task, and de-duplicate requests via &lt;a href="https://repl.ca/what-is-the-idempotency-key-header/"&gt;idempotency&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Treat each HTTP request as a request to enqueue background work, keeping the request open. Have the server poll the background worker on behalf of the client, returning a response when the background work is done. Use idempotency (likely based on the contents of the request) to de-duplicate in-flight requests, pointing them all at the same in-process background work.&lt;/p&gt;

&lt;p&gt;If the long-running work completes before the client times out (or the server crashes, etc), then from the client's perspective, the API has not changed. If the client times out, and retries, then idempotency assures that the client resumes tracking the same original work. If the client does not retry, then the work will complete, regardless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resources with status
&lt;/h3&gt;

&lt;p&gt;While the first two strategies are half-steps meant to support backwards compatibility, adding a &lt;code&gt;status&lt;/code&gt; field to a resource is a cleaner, more complete solution, that may also break backwards compatibility.&lt;/p&gt;

&lt;p&gt;In this strategy, any API request to create a resource creates it right away, filling in as many details as possible, with the addition of a status field, which records state transitions as the resource progresses from an accepted request, to a completed and created resource.&lt;/p&gt;

&lt;p&gt;For example, consider creating a new Cat Bonnet that is hand sewn on demand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/v1/cat-bonnets&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red bonnet"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt; &lt;span class="ne"&gt;Created&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/cat-bonnets/3f566245-754a-44af-82fd-b754d4b03fb6&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f566245-754a-44af-82fd-b754d4b03fb6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red bonnet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:22:03Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your bonnet has been accepted for processing."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;POST&lt;/code&gt; response returns the pending Cat Bonnet resource. The client can then poll the server, watching the state of the Cat Bonnet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/v1/cat-bonnets/3f566245-754a-44af-82fd-b754d4b03fb6&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f566245-754a-44af-82fd-b754d4b03fb6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red bonnet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sewing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:23:49Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expert craftspersons are sewing your new bonnet."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:22:03Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your bonnet has been accepted for processing."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eventually, the Cat Bonnet will be fully sewn and complete. Hooray!&lt;/p&gt;

&lt;p&gt;As in the above example, status may exist directly on a resource that has meaning for end users. It may also exist on a resource that is mostly meaningful for the long-running task itself, but also has use as a record or log, like an &lt;code&gt;Import&lt;/code&gt; type.&lt;/p&gt;

&lt;p&gt;For resource modification or &lt;code&gt;DELETE&lt;/code&gt;s, the resource proper can stay in its old state, until the status progresses to the point where the modification is complete.&lt;/p&gt;

&lt;p&gt;Note that this is similar to, but not the same as, &lt;a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/"&gt;Kubernetes' status field&lt;/a&gt;, which fully describes the current state of a resource &lt;em&gt;and optionally&lt;/em&gt; may include fields describing the resource's state transition history.&lt;/p&gt;

&lt;p&gt;For additional context, the &lt;a href="http://watson-developer-cloud.github.io/api-guidelines/#asynchronous-operations"&gt;IBM Watson REST API Guidelines&lt;/a&gt; have additional details on a similar style of asynchronous API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Job specific indirect resources
&lt;/h3&gt;

&lt;p&gt;A job specific indirect resource has a few differences from putting status on a resource:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The job specific resource is often a generic type, like &lt;code&gt;Job&lt;/code&gt;, &lt;code&gt;Operation&lt;/code&gt;, or similar.&lt;/li&gt;
&lt;li&gt;Its lifetime may be short, say only 24 hours after task completion or failure.&lt;/li&gt;
&lt;li&gt;They are often created &lt;strong&gt;indirectly&lt;/strong&gt; at the server's discretion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point can be powerful; it gives the server the option of responding immediately, or giving the client a URL to use for polling results, making this decision at runtime.&lt;/p&gt;

&lt;p&gt;Expanding on the Cat Bonnet creation example above, given the following request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/v1/cat-bonnets&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red bonnet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"fabric"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"purest unicorn mane"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server could reference warehouse stock for &lt;code&gt;purest unicorn mane&lt;/code&gt; to determine if the Cat Bonnet could be fabricated in a reasonable amount of time, or if there is no stock, and fabricating the bonnet may require an asynchronous response.&lt;/p&gt;

&lt;h4&gt;
  
  
  Direct response
&lt;/h4&gt;

&lt;p&gt;If there is stock, the server can create the bonnet and respond immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt; &lt;span class="ne"&gt;Created&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/cat-bonnets/3f566245-754a-44af-82fd-b754d4b03fb6&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f566245-754a-44af-82fd-b754d4b03fb6"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red bonnet"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Indirect response
&lt;/h4&gt;

&lt;p&gt;If, on the other hand, there is no stock, the server can respond with a &lt;code&gt;202 Accepted&lt;/code&gt; and point the client at an endpoint for polling status:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;202&lt;/span&gt; &lt;span class="ne"&gt;Accepted&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/jobs/27440252-c84a-40aa-8a17-8c3532eb8aca&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cat Bonnet creation accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27440252-c84a-40aa-8a17-8c3532eb8aca"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202"&gt;&lt;code&gt;202 Accepted&lt;/code&gt;&lt;/a&gt; is intentionally non-committal and vaguely defined. Further, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location"&gt;&lt;code&gt;Location&lt;/code&gt;&lt;/a&gt; has no defined meaning when used with it. However, convention in APIs has landed on using the two together for non-blocking APIs.&lt;/p&gt;

&lt;p&gt;The client can then make repeated requests to the &lt;code&gt;/v1/jobs&lt;/code&gt; endpoint returned in the &lt;code&gt;Location&lt;/code&gt; header to get the status of the Cat Bonnet creation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/v1/jobs/27440252-c84a-40aa-8a17-8c3532eb8aca&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27440252-c84a-40aa-8a17-8c3532eb8aca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sewing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T09:15:49Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expert craftspersons are sewing your new bonnet."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:22:03Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your bonnet has been accepted for processing."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once done creating the Cat Bonnet, the server can reply with an updated &lt;code&gt;status&lt;/code&gt; including information on where to find the created Cat Bonnet. It's also acceptable and common to return a &lt;code&gt;201 Created&lt;/code&gt; response with a &lt;code&gt;Location&lt;/code&gt; header:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;201&lt;/span&gt; &lt;span class="ne"&gt;Created&lt;/span&gt;
&lt;span class="na"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/v1/cat-bonnets/3f566245-754a-44af-82fd-b754d4b03fb6&lt;/span&gt;
&lt;span class="na"&gt;Content-Language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;en-CA&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"27440252-c84a-40aa-8a17-8c3532eb8aca"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T09:15:49Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your new bonnet is ready."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bonnet_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"3f566245-754a-44af-82fd-b754d4b03fb6"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sewing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T09:15:49Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Expert craftspersons are sewing your new bonnet."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:22:03Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your bonnet has been accepted for processing."&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's OK to reply with a &lt;code&gt;201 Created&lt;/code&gt; after the Cat Bonnet is created, but it isn't OK to respond with a &lt;code&gt;4XX&lt;/code&gt; series status code on error. A &lt;code&gt;4XX&lt;/code&gt; series code should be served in response to the direct request for the job status only (for example, if the client asked for a job ID that didn't exist).&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexibility is complexity
&lt;/h3&gt;

&lt;p&gt;Every client will have to know how to handle a direct response and an indirect non-blocking response. Take this into account; it may be better to always return a &lt;code&gt;202 Accepted&lt;/code&gt; response, even for fast replies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Picking a style
&lt;/h2&gt;

&lt;p&gt;Most often the style of asynchronous API you choose is influenced by existing APIs, data, schema, etc. If you're mostly free from those constraints, your choices are likely between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; on end-user meaningful resources (for example directly on Cat Bonnets)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt; on job-specific indirect resources (for example creating a &lt;code&gt;Job&lt;/code&gt; to track sewing a new Cat Bonnet)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both of these styles can handle asynchronous resource creation, modification, or deletion, whereas direct creation of a job-specific resource like an &lt;code&gt;Import&lt;/code&gt; may complicate your API for modification or deletion. An import resource can be useful to track when a single entity was imported into a system, but pairing it with a &lt;code&gt;Deletion&lt;/code&gt; resource, or placing long-running delete status directly on the imported entity makes less sense.&lt;/p&gt;

&lt;p&gt;Given the two options then, your choice should be influenced by how useful the &lt;code&gt;status&lt;/code&gt; data is, and how badly a client can break their application by ignoring the data.&lt;/p&gt;

&lt;p&gt;At Manifold, our API had both styles of API for creating (buying) a resource (SaaS DB) over the years.&lt;/p&gt;

&lt;p&gt;We began with job-specific resources. These were problematic for display in the frontend UI; the client was responsible for merging the list of &lt;em&gt;real&lt;/em&gt; resources with the ones that were in the middle of creation, plus modifying the real ones for any in-flight change or delete jobs. Performing this merge was extra work for the frontend client, and error prone. We would end up with out of order lists, or missing in-creation resources, or duplicate entries for the real resource plus its modification job.&lt;/p&gt;

&lt;p&gt;Changing the API to have &lt;code&gt;status&lt;/code&gt; directly on resources made the client simpler. Even if the client ignored the &lt;code&gt;status&lt;/code&gt; field, the list of resources would always be correct, only misidentifying in-creation resources as live ones. If that bug ever occurred, further user interaction might fail, but it was far better to show the resource than miss displaying it.&lt;/p&gt;

&lt;p&gt;So again, the best style for your API will depend on how you expect clients to consume it.&lt;/p&gt;

&lt;h3&gt;
  
  
  What goes in &lt;code&gt;status&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The most useful &lt;code&gt;status&lt;/code&gt; fields are arrays of objects like those shown in the examples above, sorted in newest to oldest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"time"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-04-08T07:22:03Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your bonnet has been accepted for processing."&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A machine readable state field (which could terminate on &lt;code&gt;completed&lt;/code&gt; or &lt;code&gt;errored&lt;/code&gt;), coupled with a human-readable description field should give enough data for meaningful progress feedback. If you have it, you could add estimated time to completion, as well.&lt;/p&gt;

&lt;p&gt;Be careful of too many possible state transitions; you don't want this array to grow to hundreds (or even tens) of entries. If that's possible, you may wish to truncate to the last &lt;code&gt;N&lt;/code&gt; entries, instead of resorting to pagination.&lt;/p&gt;

&lt;p&gt;If your data doesn't have meaningful state progressions, a &lt;code&gt;percent_complete&lt;/code&gt; field can work just as well. Add it in addition to the &lt;code&gt;state&lt;/code&gt; field, so &lt;code&gt;state&lt;/code&gt; can indicate &lt;code&gt;running&lt;/code&gt;, &lt;code&gt;completed&lt;/code&gt;, or &lt;code&gt;errored&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Variants
&lt;/h2&gt;

&lt;p&gt;The above strategies require the client to make repeated requests to the server (polling) to get the latest state of a request. Polling is excellent for client simplicity and compatibility; if a client can make an HTTP request to start an asynchronous action, they can make further requests to get the status. However, polling adds additional work for your server to do, and will add some latency from when work is finished to when the client knows (depending on the polling interval). You may wish to support additional features to reduce latency for the client or load on your server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Callback URLs
&lt;/h3&gt;

&lt;p&gt;Callbacks are like targetted one-off &lt;a href="https://en.wikipedia.org/wiki/Webhook"&gt;webhooks&lt;/a&gt;. When a caller creates an asynchronous operation, they supply a callback url parameter that the server will call on completion. The caller then has less state to track, and no polling to do; it can wait for the callback to arrive on completion or error (though practically speaking it should not trust the server to callback, and implement its own timeout if required).&lt;/p&gt;

&lt;p&gt;As the caller is providing an arbitrary URL, care should be taken to ensure the caller has some proof of ownership of that URL, and you may wish to &lt;a href="https://repl.ca/modern-webhook-signatures/"&gt;sign&lt;/a&gt; the callback payloads to help protect the client from arbitrary callers or replay attacks.&lt;/p&gt;

&lt;p&gt;As the callback needs a server to call back to, this option is best suited for APIs that will be called by other server-side processes. Not only will the calling server not have to maintain a long-running session to poll for status (tricky when we expect systems to crash and go away all the time), but the calling server may not have to maintain much additional state, provided the callback response contains enough context.&lt;/p&gt;

&lt;h3&gt;
  
  
  Push on state change
&lt;/h3&gt;

&lt;p&gt;Instead of requiring the client to make new requests for status periodically, they can instead open a long-lived connection with &lt;a href="https://en.wikipedia.org/wiki/WebSocket"&gt;WebSockets&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Server-sent_events"&gt;Server-Sent Events&lt;/a&gt;, allowing the server to push state changes to them, as they happen. Push-based APIs such as these require your infrastructure to take on an additional level of state, tracking who has subscriptions to what, across your fleet of servers. It can be deceptively difficult to set up, and support may vary across different infrastructure providers and technologies.&lt;/p&gt;

&lt;p&gt;Pushing on state change is most applicable to clients that maintain sessions and state, like web browsers or mobile apps.&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>async</category>
    </item>
    <item>
      <title>One-Sided Idempotence</title>
      <dc:creator>James Bowes</dc:creator>
      <pubDate>Sun, 04 Apr 2021 13:09:57 +0000</pubDate>
      <link>https://dev.to/jbowes/one-sided-idempotence-447g</link>
      <guid>https://dev.to/jbowes/one-sided-idempotence-447g</guid>
      <description>&lt;p&gt;In the last post, we &lt;a href="https://repl.ca/what-is-the-idempotency-key-header/"&gt;discussed the idempotency-key header&lt;/a&gt;, and how this header can be used to add idempotence to otherwise non-idempotent HTTP methods (like &lt;code&gt;POST&lt;/code&gt;) in REST APIs. We also touched on how many HTTP methods are inherently idempotent, like &lt;code&gt;DELETE&lt;/code&gt;. But just how idempotent is it, in practice?&lt;/p&gt;

&lt;p&gt;To recap, &lt;code&gt;DELETE&lt;/code&gt; is an idempotent HTTP method because after the first request to delete a resource, a client can make any number of subsequent duplicate &lt;code&gt;DELETE&lt;/code&gt; requests, and the resource will stay deleted. The end state on the server side stays the same, no matter how many &lt;code&gt;DELETE&lt;/code&gt; requests it sees. Idempotent requests like &lt;code&gt;DELETE&lt;/code&gt; have another interesting property: After that first request, failures, errors, and malformed or misinterpreted requests maintain the same state on the server side. &lt;em&gt;Side note: If you've ever seen a bug that caused a resource to be re-created on a failed delete, &lt;a href="https://twitter.com/jrbowes"&gt;let me know on twitter&lt;/a&gt;. I'd love to hear about it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;"server side" is important here. Discussion of idempotence usually focuses on the server (or whoever holds the canonical state), glossing over its impact on the client; it's a one-sided view. The client is important too, particularly in distributed systems, where "the client" might be one of many hops between the server and a user. &lt;/p&gt;

&lt;p&gt;Idempotence, when paired with retries, provides resilience against failures in the network, etc. If a wire is tripped over when the server is responding &lt;code&gt;2XX&lt;/code&gt; to a &lt;code&gt;DELETE&lt;/code&gt;, but before the client reads that response (and the client eventually times out), then at some point, the client will retry the request. How the server responds to that second request will impact the complexity of the client's logic for handling replies, and may ultimately end up impacting what an end user sees.&lt;/p&gt;

&lt;p&gt;For a real-world example of how responses from idempotent requests can impact clients, let's look at an example using &lt;a href="https://stripe.com"&gt;Stripe&lt;/a&gt;. Stripe's API makes ample use of idempotence, and is typical in how it handles &lt;code&gt;DELETE&lt;/code&gt; requests. First, assume we have the following product defined:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/v1/products/prod_JEbKPQJxRVglrR&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.stripe.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;430&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prod_JEbKPQJxRVglrR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"active"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1617451149&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A premium hobby-grade cat bonnet."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"images"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"livemode"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cat Bonnet"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"package_dimensions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"shippable"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"statement_descriptor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"service"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"unit_label"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"updated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1617451217&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This product is a hobby-grade cat bonnet for sale in our online cat bonnet marketplace, listed by an independent cat bonnet artisan. Imagine the artisan decides to stop selling this particular cat bonnet. Their pressing of a delete button in the UI triggers the following call in the cat bonnet marketplace:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;DELETE&lt;/span&gt; &lt;span class="nn"&gt;/v1/products/prod_JEbKPQJxRVglrR&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.stripe.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;76&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"prod_JEbKPQJxRVglrR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"product"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;200&lt;/code&gt; response indicates success, and the response is a sparse representation of the deleted product. Note that while the &lt;a href="https://stripe.com/docs/api/products/delete"&gt;Stripe docs on products&lt;/a&gt; say the product is returned, the only data we get is values we could infer from the request itself.&lt;/p&gt;

&lt;p&gt;Now, imagine if the backend server never saw that &lt;code&gt;200&lt;/code&gt; response, and retried its request to delete. This is the response it would get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;DELETE&lt;/span&gt; &lt;span class="nn"&gt;/v1/products/prod_JEbKPQJxRVglrR&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.stripe.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;404&lt;/span&gt; &lt;span class="ne"&gt;Not Found&lt;/span&gt;
&lt;span class="na"&gt;Content-Length&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;236&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;application/json&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"resource_missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"doc_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://stripe.com/docs/error-codes/resource-missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"No such product: 'prod_JEbKPQJxRVglrR'"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"param"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invalid_request_error"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;404&lt;/code&gt;, indicating (since it's a &lt;code&gt;4XX&lt;/code&gt; series response) that the client made a mistake, and this resource doesn't exist. The end result from Stripe's perspective is still that the product is deleted, but now, unless the cat bonnet marketplace backend accounts for a &lt;code&gt;404&lt;/code&gt; response status, and the difference in the response bodies, the end user may see an error message instead of success.&lt;/p&gt;

&lt;p&gt;In the case of Stripe, the API is &lt;em&gt;responding to to the requested change&lt;/em&gt; and not &lt;em&gt;responding to the desired state&lt;/em&gt; (this is similar to [edge vs level triggering &lt;a href="https://link.medium.com/zWGns5U69eb"&gt;edgevlevel&lt;/a&gt;). If the API treats a &lt;code&gt;DELETE&lt;/code&gt; as "ensure this resource does not exist" instead of "delete this existing resource", then so long as the resource does not exist at the end of a &lt;code&gt;DELETE&lt;/code&gt; request, the server can respond with a &lt;code&gt;200 OK&lt;/code&gt;, the client will know that the resource doesn't exist (as it desired), and the client doesn't require any additional logic for treating &lt;code&gt;404&lt;/code&gt; responses as successes.&lt;/p&gt;

&lt;p&gt;Back to Stripe's original &lt;code&gt;200&lt;/code&gt; response: It's documented as returning the deleted object, but only returns the object type and id (both of which are included in the &lt;code&gt;DELETE&lt;/code&gt; request). Many APIs do return the full deleted object. Be careful with this when designing APIs; you'd make extra work for yourself trying to return the deleted object for multiple delete requests.&lt;/p&gt;

&lt;p&gt;Sometimes, it matters to the client if the resource being deleted actually was deleted by them or not. In those cases, you could either encourage the client to &lt;code&gt;GET&lt;/code&gt; the resource first, and see if it exists (ignoring a possible race condition), or include an additional response header or field on the body indicating if that request caused a delete.&lt;/p&gt;

&lt;p&gt;Designing APIs requires balancing tradeoffs, predicting common usage patterns, and aiming for simplicity. Next time you implement &lt;code&gt;DELETE&lt;/code&gt;, consider if always returning &lt;code&gt;200&lt;/code&gt; may be best. But be careful to not introduce inconsistencies with other &lt;code&gt;DELETE&lt;/code&gt; endpoints in an existing API.&lt;/p&gt;

</description>
      <category>rest</category>
      <category>api</category>
    </item>
    <item>
      <title>What is the idempotency-key header?</title>
      <dc:creator>James Bowes</dc:creator>
      <pubDate>Sat, 27 Mar 2021 10:12:21 +0000</pubDate>
      <link>https://dev.to/jbowes/what-is-the-idempotency-key-header-447i</link>
      <guid>https://dev.to/jbowes/what-is-the-idempotency-key-header-447i</guid>
      <description>&lt;p&gt;In Computer Science (and in its cooler older sibling, Mathematics) &lt;a href="https://en.wikipedia.org/wiki/Idempotence"&gt;idempotence&lt;/a&gt; is a property of an action (e.g. an API call) such that doing the action again with the same inputs produces the same result. In the context of HTTP, a &lt;code&gt;GET&lt;/code&gt; request is usually idempotent; making a request to the same URL should return the same response, and not change any relevant data in the backing data store. A &lt;code&gt;DELETE&lt;/code&gt; request is likewise typically idempotent; no matter how many times you &lt;code&gt;DELETE&lt;/code&gt; a given URL, the resource stays deleted after the first call, no other resources are deleted, etc.&lt;/p&gt;

&lt;p&gt;Idempotence is a great property for APIs to have. Idempotence coupled with retries can mitigate many problems related to the unreliability of networks, computers, and software. If any request along the chain between the client and your data store fails, the downstream initiator can safely retry. Even impatient users frantically clicking buttons and refreshing pages are no match for idempotence!&lt;/p&gt;

&lt;p&gt;Looking at the HTTP methods used in REST APIs, some are inherently idempotent, and others are not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;GET&lt;/code&gt;: Show me the thing I asked for. &lt;strong&gt;IDEMPOTENT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DELETE&lt;/code&gt;: Ensure the thing I referenced does not exist. &lt;strong&gt;IDEMPOTENT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PUT&lt;/code&gt;: Ensure the thing I gave you exists exactly as I gave it to you. &lt;strong&gt;IDEMPOTENT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PATCH&lt;/code&gt;: Apply the provided set of changes to the current state of the thing I referenced. &lt;strong&gt;NOT IDEMPOTENT&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;POST&lt;/code&gt;: Create this thing I gave you (usually at a different URL), or modify a thing based on inputs and its current state. &lt;strong&gt;NOT IDEMPOTENT&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; and &lt;code&gt;PATCH&lt;/code&gt; are not idempotent. &lt;code&gt;PATCH&lt;/code&gt; is especially resistant to being made idempotent, as a &lt;code&gt;PATCH&lt;/code&gt; operation is meant to apply context-aware changes to a resource in its current state. Applying the same change multiple times, as the resource's state is updated, will at best result in an error, and at worst result in a new state that the caller didn't intend. Strategies like &lt;a href="https://en.wikipedia.org/wiki/Optimistic_concurrency_control"&gt;optimistic concurrency control&lt;/a&gt; are often best for &lt;code&gt;PATCH&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt;, on the other hand, can be made idempotent - particularly for uses meant to create a resource. If we're building an API to sell Cat Bonnets, &lt;code&gt;POST&lt;/code&gt; requests to create new store listings could include the bonnet &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt; as the most important and meaningful fields. A content-aware idempotence scheme would treat any &lt;code&gt;POST&lt;/code&gt;s with the same &lt;code&gt;size&lt;/code&gt; and &lt;code&gt;color&lt;/code&gt; as intention to ensure such a listing exists. As long as one already does, the &lt;code&gt;POST&lt;/code&gt; won't create a new one; &lt;code&gt;N &amp;gt; 1&lt;/code&gt; requests to create a large blue bonnet always result in only 1 large blue bonnet existing.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://tools.ietf.org/id/draft-idempotency-header-01.html"&gt;&lt;code&gt;idempotency-key&lt;/code&gt;&lt;/a&gt; header is another way to bring idempotence to non-idempotent HTTP methods (particularly &lt;code&gt;POST&lt;/code&gt;). The caller can supply a unique value for the header. If the header is supplied, then the server returns the same response for any requests with the same value for &lt;code&gt;idempotency-key&lt;/code&gt; (usually within a 24 hour window; we can't expect infinite storage). But if we can make a &lt;code&gt;POST&lt;/code&gt; idempotent based on the request contents, what's the use of this header?&lt;/p&gt;

&lt;p&gt;A hint lies in companies that implement it: [Stripe &lt;a href="https://stripe.com"&gt;stripe&lt;/a&gt;, &lt;a href="https://squareup.com"&gt;Square&lt;/a&gt;, and &lt;a href="https://twilio.com"&gt;Twilio&lt;/a&gt; (to name a few).&lt;/p&gt;

&lt;p&gt;These are companies that provide APIs interacting with the outside world, where each action is expensive (sometimes in real money), and the expense compounds with duplicates (e.g. charging a user twice for the same good also damages reputation).&lt;/p&gt;

&lt;p&gt;An SMS API wants to let its clients send a message to user X with message body "you have a new notification" more than once, maybe within minutes or seconds of each other, or even at the same time. Further, they also want to allow the clients to have a means to prevent duplicate sends if from the client's point of view, there is only one message. Doing this with content-based idempotence would require additional significant fields in the request body, ultimately boiling down to a unique key - essentially identical to the &lt;code&gt;idempotency-key&lt;/code&gt; header. Putting the field in an HTTP header allows for easier reuse between API endpoints, and keeps values out of the schema that aren't directly meaningful for the problem space.&lt;/p&gt;

&lt;p&gt;Ignoring any imposed expiration on &lt;code&gt;idempotency-key&lt;/code&gt; values, adding the header to &lt;code&gt;POST&lt;/code&gt; requests makes them look a lot like &lt;code&gt;PUT&lt;/code&gt;s: The unique value a client uses for &lt;code&gt;idempotency-key&lt;/code&gt; maps to the URL (typically including a unique ID in the path) that they would call &lt;code&gt;PUT&lt;/code&gt; on. There are a few reasons why a &lt;code&gt;PUT&lt;/code&gt; may not make sense for an API:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You may want full control over your resource IDs and their URLS; letting a client provide them in &lt;code&gt;PUT&lt;/code&gt;s would give that control up.&lt;/li&gt;
&lt;li&gt;The scheme for your IDs is complex enough that expecting clients to create them is unreasonable; letting them provide arbitrary &lt;code&gt;idempotency-key&lt;/code&gt; values is easier.&lt;/li&gt;
&lt;li&gt;You don't want to persist any of the request resources. &lt;code&gt;PUT&lt;/code&gt;ing wouldn't make sense, as the resource won't exist at the URL after the operation is finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a final note, &lt;em&gt;this&lt;/em&gt; post is not idempotent. Repeated readings may further enhance your understanding of idempotence for APIs.&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
    </item>
    <item>
      <title>Modern webhook signatures in 2021</title>
      <dc:creator>James Bowes</dc:creator>
      <pubDate>Fri, 26 Mar 2021 13:37:10 +0000</pubDate>
      <link>https://dev.to/jbowes/modern-webhook-signatures-in-2021-i0i</link>
      <guid>https://dev.to/jbowes/modern-webhook-signatures-in-2021-i0i</guid>
      <description>&lt;p&gt;&lt;em&gt;This post is a follow-up to the previous post on &lt;a href="https://repl.ca/what-is-x-hub-signature/"&gt;&lt;code&gt;x-hub-signature&lt;/code&gt;&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It's 2021, and you work for Cat Bonnets Online. The bigwigs up on the top floor just finished telling you over Zoom that the company is modernizing its strategy. While half the company is converting the bonnets to &lt;a href="https://en.wikipedia.org/wiki/Non-fungible_token"&gt;NFTs&lt;/a&gt; (Nice Feline Toppings), and the rest is working on a &lt;a href="https://www.joinclubhouse.com/"&gt;Clubhouse&lt;/a&gt; strategy, your team is adding &lt;a href="https://en.wikipedia.org/wiki/Webhook"&gt;webhooks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You want to sign your webhooks, so receivers can verify you sent them. Don't use &lt;code&gt;x-hub-signature&lt;/code&gt; for this. It's not good enough, and there's a better option in 2021: [The "Signing HTTP Messages" draft specification &lt;a href="https://datatracker.ietf.org/doc/draft-ietf-httpbis-message-signatures/"&gt;httpsig&lt;/a&gt;. This new specification  addresses three shortcomings in &lt;code&gt;x-hub-signature&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Headers can be included in the signature&lt;/strong&gt;: If your webhook includes important information in headers (for example, event or account identifiers), it should be signed, so the receiver can ensure they haven't been tampered with.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;There's a facility for expiration&lt;/strong&gt;: Sent requests can have an expiration time, mitigating replay attacks. If your events are not &lt;a href="https://en.wikipedia.org/wiki/Idempotence"&gt;idempotent&lt;/a&gt; (or the receiver doesn't treat them as such) a bad actor could intercept a request, and  repeatedly send it to the reciever. This could lead to resource exhaustion, lost data, or inconsistent state.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asymmetric keys are supported&lt;/strong&gt;: &lt;code&gt;x-hub-signature&lt;/code&gt; implementations use symmetric keys for signing and verification. While this is faster than asymmetric forms, it means a bad actor could steal the key from your system, from the receiver, or while it's in transit between the two during setup. With asymmetric keys, you only need worry about having the key stolen from your own system.&lt;/p&gt;

&lt;p&gt;The first two issues with &lt;code&gt;x-hub-signature&lt;/code&gt; (unsigned headers and no expiration) can be solved in a layer above the signature, by including &lt;strong&gt;all&lt;/strong&gt; details only in the message body, and having an application defined expiration. It's too easy to let important data slip into the headers over time, or for a receiver to not implement the extra step of checking the expiration time. Why not let a specification (and hopefully a standard library) handle it for you and your receivers?&lt;/p&gt;

&lt;p&gt;At Manifold, we implemented request signing based on an earlier version of the spec, but modified to address some shortcomings in the spec at the time (notably that while header contents were included in the signature, the header names were not). You can read about that implementation, and see code samples, on Manifold's &lt;a href="https://docs.manifold.co/enterprise/sell-in-marketplaces/development-guide/request-signing"&gt;request signing documentation&lt;/a&gt;. We deviated from the spec at the time, but now, you don't have to.&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>security</category>
    </item>
    <item>
      <title>What is x-hub-signature?</title>
      <dc:creator>James Bowes</dc:creator>
      <pubDate>Fri, 26 Mar 2021 10:30:42 +0000</pubDate>
      <link>https://dev.to/jbowes/what-is-x-hub-signature-4n6j</link>
      <guid>https://dev.to/jbowes/what-is-x-hub-signature-4n6j</guid>
      <description>&lt;p&gt;&lt;em&gt;TL;DR&lt;/em&gt; &lt;code&gt;x-hub-signature&lt;/code&gt; is an HTTP &lt;strong&gt;body only&lt;/strong&gt; signing scheme from the &lt;a href="https://www.w3.org/TR/websub/"&gt;WebSub standard&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Webhook"&gt;Webhooks&lt;/a&gt; are a common way on the modern web for one application to send events to another, in a way that doesn't tightly couple the first application to the second. For example: Application one (henceforth "Cat Bonnets Online") is configured by a customer at runtime to send events to application two (henceforce "Chat App"). Cat Bonnets Online sends events in its own format to the configured endpoint of Chat App. Chat App knows it gets webhook events from Cat Bonnets Online, and knows how to parse and display them. Cat Bonnets Online knows nothing about Chat App, other than that it is another endpoint that receives webhook events.&lt;/p&gt;

&lt;p&gt;Chat App now has a public-facing URL that, when called, displays information from the received event to all users of a given Chat App instance. That's great when those users want to know if someone bought them the latest argyle cat bonnet. It's not so great when a bad actor discovers and calls the endpoint with hateful and abusive content.&lt;/p&gt;

&lt;p&gt;Webhooks need &lt;strong&gt;SECURITY&lt;/strong&gt;! &lt;code&gt;x-hub-signature&lt;/code&gt; is a header containing an HMAC signature of the body of the webhook request. During webhook configuration, Cat Bonnets Online and Chat App share a secret. This secret is used in the HMAC, allowing Chat App to verify that all webhooks it receives have this header, and that the HMAC is correct, assuring webhooks come from Cat Bonnets Online.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;x-hub-signature&lt;/code&gt; is used by lots of applications that send webhooks (&lt;a href="https://docs.github.com/en/developers/webhooks-and-events/securing-your-webhooks"&gt;GitHub&lt;/a&gt;, &lt;a href="https://developers.facebook.com/docs/graph-api/webhooks/getting-started/"&gt;Facebook&lt;/a&gt;, etc), but without reference to where it comes from: the &lt;a href="https://www.w3.org/TR/websub/"&gt;WebSub standard&lt;/a&gt; (Formerly known as PubSubHubBub). Knowing the connection can help answer questions like "Why does Facebook do a verification request with all these hub.XXX parameters?" or "Is hub short for GitHub?", and help find the subtle differences between each implementation.&lt;/p&gt;

&lt;p&gt;It's too difficult to find the connection between &lt;code&gt;x-hub signature&lt;/code&gt; and &lt;a href="https://www.w3.org/TR/websub/"&gt;WebSub&lt;/a&gt;. Hopefully this post will help the search rankings of WebSub, even just a tiny bit. Spread the word!&lt;/p&gt;

&lt;p&gt;Now that you know what &lt;code&gt;x-hub-signature&lt;/code&gt; is, you should never use it for a new webhook implementation in 2021. Read this &lt;a href="https://dev.to/modern-webhook-signatures/"&gt;follow-up post&lt;/a&gt; to learn about what you should use instead.&lt;/p&gt;

</description>
      <category>api</category>
      <category>rest</category>
      <category>security</category>
    </item>
  </channel>
</rss>
