<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Api on Geoff Baskwill</title>
    <link>https://geoffbaskwill.ca/tags/api/</link>
    <description>Geoff Baskwill (Api)</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en-ca</language>
    <lastBuildDate>Wed, 09 Sep 2020 00:00:00 +0000</lastBuildDate>
    
    <atom:link href="https://geoffbaskwill.ca/tags/api/index.xml" rel="self" type="application/rss+xml" />
    
    
    <item>
      <title>API versioning</title>
      <link>https://geoffbaskwill.ca/posts/api-versioning/</link>
      <pubDate>Wed, 09 Sep 2020 00:00:00 +0000</pubDate>
      
      <guid>https://geoffbaskwill.ca/posts/api-versioning/</guid>
      <description>&lt;p&gt;There are lots of ways to implement API versioning. Picking one is an exercise in balance: what voices will you listen to?&lt;/p&gt;
&lt;p&gt;For us, the most important voice is the voice of the customer. There are lots of customers, though, and many of them just want to get the job done. They are expecting us to be the experts and make it easy for them to build integrations that Just Work™.&lt;/p&gt;
&lt;p&gt;People using our APIs don&amp;rsquo;t really care whether we&amp;rsquo;re 100% &lt;a href=&#34;https://en.wikipedia.org/wiki/Roy_Fielding&#34;&gt;Fielding&lt;/a&gt;-Certified REST™ (not a real thing). They want something that&amp;rsquo;s simple and predictable.&lt;/p&gt;
&lt;p&gt;How can we generally meet people where they&amp;rsquo;re at, and give everyone the benefit of versioning without creating work for them?&lt;/p&gt;
&lt;p&gt;I consider &lt;a href=&#34;https://stripe.com/blog/api-versioning&#34;&gt;Stripe&amp;rsquo;s API versioning strategy&lt;/a&gt; to be the gold standard. If you read their post, you&amp;rsquo;ll see how they carefully balance the ideal of &amp;ldquo;we will never break your integration with us&amp;rdquo; with &amp;ldquo;sometimes we need to make breaking changes&amp;rdquo;.&lt;/p&gt;
&lt;h2&gt;When do we need a new version?&lt;/h2&gt;&lt;p&gt;Most people agree that you need a new API version every time you have a breaking change.&lt;/p&gt;
&lt;p&gt;What&amp;rsquo;s a breaking change? It depends. Some people are really strict: if there is a new attribute, that&amp;rsquo;s a breaking change in their minds. I tend to be more lenient. If an attribute changes type (for example, from a simple type to an object) or if a required attribute in a response goes away, that&amp;rsquo;s a breaking change. It gets a little trickier when you modify an enumerated type, so you need to understand your audience and how they are using your API.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://imgs.xkcd.com/comics/workflow.png&#34; alt=&#34;XKCD 1162 - Workflow&#34; title=&#34;XKCD 1162 - Workflow&#34;&gt;&lt;/p&gt;
&lt;h2&gt;What&amp;rsquo;s a good version string look like?&lt;/h2&gt;&lt;p&gt;The Stripe folks use a date to represent an API version; it&amp;rsquo;s handy as people generally understand how to compare dates. Some people get confused when they see an &amp;ldquo;old&amp;rdquo; API version and think that your documentation is out of date, but those people are rare.&lt;/p&gt;
&lt;p&gt;Other folks prefer to use semantic versioning (&lt;code&gt;v1.0.3&lt;/code&gt;), or use a simple string like &lt;code&gt;v1&lt;/code&gt; and move on.&lt;/p&gt;
&lt;p&gt;I feel like &lt;code&gt;v1&lt;/code&gt; isn&amp;rsquo;t particularly granular, semantic versioning is a bit sketchy from an API perspective and too much like a release number, and prefer date strings.&lt;/p&gt;
&lt;h2&gt;Options&lt;/h2&gt;&lt;h3&gt;Just put a version in the URL&lt;/h3&gt;&lt;p&gt;&lt;em&gt;also known as &amp;ldquo;What&amp;rsquo;s wrong with &lt;code&gt;/v1/widgets/123456&lt;/code&gt;?&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Some people love putting the API version in the path portion of the URL. I don&amp;rsquo;t particularly like this myself. If I have one resource one day that has a breaking change, then I have to break out &lt;code&gt;/v2&lt;/code&gt;. The next day, another breaking change to a resource, &lt;code&gt;/v3&lt;/code&gt;. Where does it end? What do you do with the resources that didn&amp;rsquo;t change?&lt;/p&gt;
&lt;p&gt;From a dogmatic perspective, putting the API version in the URL path means that your resources at &lt;code&gt;/v1/*&lt;/code&gt; and at &lt;code&gt;/v2/*&lt;/code&gt; and at &lt;code&gt;/v3/*&lt;/code&gt; are &lt;em&gt;different things&lt;/em&gt;, not &lt;em&gt;different representations of the same things&lt;/em&gt;. People with a Job To Be Done won&amp;rsquo;t care, but it&amp;rsquo;s a bit irksome to have something so flagrantly against how the Web is supposed to work. It&amp;rsquo;s handy from a service developer&amp;rsquo;s perspective, as you can &amp;ldquo;just&amp;rdquo; put a new endpoint handler in place, but if only one resource has changed then it gets messy.&lt;/p&gt;
&lt;h3&gt;Use content negotiation like Fielding intended&lt;/h3&gt;&lt;p&gt;&lt;em&gt;also known as &amp;ldquo;What&amp;rsquo;s wrong with &lt;code&gt;Accept: application/x-widget+json;v=1&lt;/code&gt;? &lt;code&gt;Accept: application/x-widget.v1+json&lt;/code&gt;?&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For a long time I believed that content negotiation was Totally Awesome™. Imagine: something built into the HTTP standards that lets you not only discover what potential representation options exist for any given resource, but also transparently negotiate upgrades on either the client or the server side.&lt;/p&gt;
&lt;p&gt;I was so enamoured with this idea I did a conference talk on it, claiming that using content negotiation was level π of the &lt;a href=&#34;https://martinfowler.com/articles/richardsonMaturityModel.html&#34;&gt;Richardson Maturity Model&lt;/a&gt; (because it&amp;rsquo;s transcendent and slightly greater than 3).&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;ve mellowed since then. I still believe that content negotiation is an awesome mechanism, but it&amp;rsquo;s a bit too clever for some folks, and unfortunately framework support is rather sparse for parameterized content types on both the client and server side.&lt;/p&gt;
&lt;p&gt;Forcing people to learn about content negotiation before they can use your API also means that some people will just walk away.&lt;/p&gt;
&lt;h3&gt;Use an API version header&lt;/h3&gt;&lt;p&gt;&lt;em&gt;also known as &amp;ldquo;What&amp;rsquo;s wrong with &lt;code&gt;Api-Version: 2020-04-01&lt;/code&gt;?&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t managed to find any concrete reasons why you shouldn&amp;rsquo;t use a custom header for passing the API version. The best people have been able to give me is &amp;ldquo;it&amp;rsquo;s harder to set up header-based routing&amp;rdquo;, which is sometimes true and totally valid for people in that position.&lt;/p&gt;
&lt;p&gt;If you allow caching of API responses, you should include a &lt;code&gt;Vary: Api-Version&lt;/code&gt; header in your responses if you go this route; it will tell any caching infrastructure that the response may be different if the API version in the request is different.&lt;/p&gt;
&lt;p&gt;The Stripe folks have a really smart mechanism: if you don&amp;rsquo;t specify the API version in your request, they look at what API version your account is pinned at. If it&amp;rsquo;s your first time calling the API, they pin you to the current version. You can change your pinned API version through the console (and presumably through the account management API as well).&lt;/p&gt;
&lt;p&gt;I haven&amp;rsquo;t yet gone so far as to implement API version pinning. The best I&amp;rsquo;ve been able to do is default to the latest version when the header isn&amp;rsquo;t present &amp;ndash; this gives folks the most chance of seeing responses that match the current API docs, but it means that folks who don&amp;rsquo;t provide an API version header are more vulnerable to breaking changes.&lt;/p&gt;
</description>
    </item>
    
    <item>
      <title>Pagination and cursors</title>
      <link>https://geoffbaskwill.ca/posts/pagination-and-cursors/</link>
      <pubDate>Wed, 07 Mar 2018 00:00:00 +0000</pubDate>
      
      <guid>https://geoffbaskwill.ca/posts/pagination-and-cursors/</guid>
      <description>&lt;p&gt;Whenever you retrieve a list of things in an API I&amp;rsquo;ve managed to influence, unless the size of the list is internally capped at an incredibly small number, you&amp;rsquo;ll see that the API supports &lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;cursor&lt;/code&gt; query parameters to allow you to page through the results. This post will explore what those parameters do, how they work, and some of the limitations of the approach.&lt;/p&gt;
&lt;h2&gt;Why pagination?&lt;/h2&gt;&lt;p&gt;Before we get into the details of how the solution works, we should properly understand the problem. Most APIs start out very simple, with a trivial &lt;code&gt;GET&lt;/code&gt; operation on a container resource that returns a list of objects, and everyone is happy:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET /widgets HTTP/1.1
Accept: application/json
...

HTTP/1.1 200 OK
...
Content-Type: application/json

[ { widget 1 }, { widget 2 }, ... ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The happiness starts to go away when the server starts handling hundreds or thousands of widgets. API response times start getting long, payloads are huge, and memory usage on the server gets really spiky as it tries to serialize the entire contents of the database in response to every request.&lt;/p&gt;
&lt;p&gt;Most people come to the quick realization that it&amp;rsquo;s better to only ask for a smaller amount of data to start, and only load additional data on demand:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET /widgets?limit=10 HTTP/1.1
Accept: application/json
...

HTTP/1.1 200 OK
...
Content-Type: application/json

[ { widget 1, { widget 2 }, ... ]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;But wait! With this data structure, how is the client supposed to know whether there&amp;rsquo;s more data to load?&lt;/p&gt;
&lt;p&gt;Some APIs simply say &amp;ldquo;Give me the start point and page size, and I&amp;rsquo;ll figure out the rest.&amp;rdquo; OK, no problem. But what&amp;rsquo;s the start point? Primary key? What if you&amp;rsquo;re sorting on something else? Things get complicated quickly. Some folks try to use an &lt;code&gt;offset&lt;/code&gt; parameter, but that can also get complicated.&lt;/p&gt;
&lt;h2&gt;Cursors can help&lt;/h2&gt;&lt;p&gt;Inspired by &lt;a href=&#34;https://slack.engineering/evolving-api-pagination-at-slack&#34;&gt;a very detailed post by the kind folks at Slack&lt;/a&gt;, I tried implementing cursors in an API framework I was building. Briefly, in this context a cursor is a token that can be used to resume the query when more than one page of results is available.&lt;/p&gt;
&lt;p&gt;When someone makes a request to list resources, our API returns a structure that contains one attribute with the list of requested resources, and if there is more than one page of results, it will also include a &lt;code&gt;next&lt;/code&gt; attribute containing an encoded token that can be used in a subsequent request to get the next page of results.&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;GET /widgets?limit=10 HTTP/1.1
Accept: application/json
...

HTTP/1.1 200 OK
Content-Type: application/json
...

{ &amp;#34;widgets&amp;#34;: [...], &amp;#34;next&amp;#34;: &amp;#34;bmljZSB0cnkK&amp;#34; }
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If the &lt;code&gt;next&lt;/code&gt; attribute isn&amp;rsquo;t there, there is no more data to be had.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;next&lt;/code&gt; attribute is a base64-encoded token that the server knows how to decode into a database query that corresponds to the next page of data. It includes information about the sort order, starting point, and anything else that the server needs in order to do its job. The API consumer doesn&amp;rsquo;t need to know anything other than &amp;ldquo;as long as there&amp;rsquo;s a &lt;code&gt;next&lt;/code&gt; field, take the value out of the result and paste it into the &lt;code&gt;cursor&lt;/code&gt; query parameter in the next request&amp;rdquo;, and I&amp;rsquo;m free to make changes to the internal logic at any time without affecting consumers, which is pretty amazing from a service development perspective. Different resources can use different logic internally without exposing the details to the API user.&lt;/p&gt;
&lt;p&gt;Even better, the &lt;code&gt;next&lt;/code&gt; attribute is self-contained. The server doesn&amp;rsquo;t have to maintain any state about the query or the cursor, so no resources are consumed if the caller never comes back.&lt;/p&gt;
&lt;h2&gt;Other ways to communicate the cursor&lt;/h2&gt;&lt;p&gt;Some folks use a &lt;code&gt;Link&lt;/code&gt; header to carry the full URL for the next page; I think that is a fantastic idea and recommend implementing that as well:&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;Link: &amp;lt;/api/widgets?cursor=aGFwcHkgZWFzdGVyIQo&amp;gt;; rel=&amp;#34;next&amp;#34;
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Security considerations&lt;/h2&gt;&lt;p&gt;If someone weren&amp;rsquo;t careful, they could easily take the idea above and say &amp;ldquo;I&amp;rsquo;ll just base64-encode a JSON blob with a bunch of parameters, call it a cursor, and take the rest of the day off!&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Of course, we want to keep our attack surface to a minimum. To help with this, we encrypt the cursor with an algorithm that verifies the integrity of the protected content, and we salt the input to make it harder to attack with known plaintext. For added protection, we also rotate the encryption key regularly, so that if someone does manage to break the key they can only attempt further attacks for a limited period of time.&lt;/p&gt;
&lt;h2&gt;Limitations&lt;/h2&gt;&lt;p&gt;The main limitation in this scheme is that it&amp;rsquo;s pretty focused on unidirectional scrolling – the cursor only goes forward (or back, depending on the sort order). There are ways to deal with this, but they&amp;rsquo;re far more complicated than I wanted to get into, and more importantly they&amp;rsquo;re not relevant to our current use cases.&lt;/p&gt;
</description>
    </item>
    
  </channel>
</rss>
