<?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 Steve Sun</title><link>https://sund.site/en/tags/api/</link><description>Recent content in Api on Steve Sun</description><generator>Hugo</generator><language>en</language><copyright>© 2013-2026, Steve Sun</copyright><lastBuildDate>Sat, 13 Jul 2024 16:12:34 +0800</lastBuildDate><follow_challenge><feedId>41397727810093074</feedId><userId>56666701051455488</userId></follow_challenge><atom:link href="https://sund.site/en/tags/api/index.xml" rel="self" type="application/rss+xml"/><item><title>Notes on the RESTful Web Services Cookbook</title><link>https://sund.site/en/posts/2024/restful-api-cookbook/</link><pubDate>Sat, 13 Jul 2024 16:12:34 +0800</pubDate><guid>https://sund.site/en/posts/2024/restful-api-cookbook/</guid><description>&lt;p&gt;&lt;a href="https://www.oreilly.com/library/view/restful-web-services/9780596809140/"&gt;RESTful Web Services Cookbook&lt;/a&gt; is a short, concise guide to designing RESTful APIs. This post (notes) records the key points from the book.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since RESTful conventions are second nature to most backend developers, I will skip the well-known parts and focus on the details in the book that many developers tend to overlook.&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2 id="http-methods"&gt;HTTP Methods&lt;/h2&gt;
&lt;h3 id="get"&gt;GET&lt;/h3&gt;
&lt;p&gt;Performs &lt;strong&gt;safe&lt;/strong&gt; and &lt;strong&gt;idempotent&lt;/strong&gt; retrieval of information.&lt;/p&gt;
&lt;h3 id="post"&gt;POST&lt;/h3&gt;
&lt;p&gt;The target of execution is a collection of resources (a factory), not a specific URI.&lt;/p&gt;
&lt;p&gt;Use cases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create a new resource by treating a resource as a factory.&lt;/li&gt;
&lt;li&gt;Modify one or more resources through a controller resource.&lt;/li&gt;
&lt;li&gt;Execute queries that require a large data input (many parameters).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When no other HTTP method seems appropriate, perform an unsafe or non-idempotent operation.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Designate an existing resource as the factory for creating new resources. Although any resource can be used as a factory, the common practice is to use a collection resource.&lt;/li&gt;
&lt;li&gt;Have the client submit a POST request to the factory resource, attaching a representation of the resource to be created. Through the optional &lt;strong&gt;Slug&lt;/strong&gt; header, the client can suggest a name to the server as part of the URI of the created resource.&lt;/li&gt;
&lt;li&gt;After the resource is created, return response code &lt;strong&gt;201 (Created)&lt;/strong&gt; and include the URI of the new resource in the &lt;strong&gt;Location&lt;/strong&gt; header.&lt;/li&gt;
&lt;li&gt;If the response body contains a full representation of the new resource, include the URI of the new resource in the &lt;strong&gt;Content-Location&lt;/strong&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="put"&gt;PUT&lt;/h3&gt;
&lt;p&gt;Only use PUT to create a new resource when the client controls the structure of the URI. &lt;strong&gt;In other words, PUT can also create a resource, but only when the client specifies the URI.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id="determining-the-granularity-of-resource-objects"&gt;Determining the Granularity of Resource Objects&lt;/h2&gt;
&lt;p&gt;Resources should be designed to match the client&amp;rsquo;s usage patterns, not based on existing database or object models.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cacheability&lt;/li&gt;
&lt;li&gt;Reduce modification frequency&lt;/li&gt;
&lt;li&gt;Mutability — separate mutable from immutable data&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="how-to-design-composite-resources"&gt;How to Design Composite Resources?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Composite resources&lt;/strong&gt; reduce the visibility of the uniform interface because their representations contain data that overlaps with other resources.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If composite resources are used &lt;strong&gt;infrequently&lt;/strong&gt;, consider using &lt;strong&gt;caching&lt;/strong&gt; instead.&lt;/li&gt;
&lt;li&gt;Consider the network overhead — would a composite resource reduce server throughput and increase latency?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="http-body"&gt;HTTP Body&lt;/h2&gt;
&lt;p&gt;Taking a JSON body as an example:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It is best to include a self-referential link.&lt;/li&gt;
&lt;li&gt;If the results are paginated, it is best to include a link to the next page.&lt;/li&gt;
&lt;li&gt;If the results are paginated, indicate the size of the collection (the total).&lt;/li&gt;
&lt;li&gt;If the queried object is localized, add a property to indicate the language of the localized content.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;John&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;urn:example:user:1234&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;link&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;self&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://www.example.org/person/john&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;address&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;urn:example:address:4567&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;link&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;rel&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;self&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;href&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://www.example.org/person/john/address&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="http-response"&gt;HTTP Response&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;For client errors, return a 4xx status code plus a Date (the time the error occurred).&lt;/li&gt;
&lt;li&gt;For server errors, return a 5xx status code plus a Date (the time the error occurred).&lt;/li&gt;
&lt;li&gt;The body should describe the error. If there are external documents and links for reference, provide a Link header or include the link directly in the body.&lt;/li&gt;
&lt;li&gt;To support later tracing and analysis, errors are logged on the server. Provide an identifier or link that can be used to locate the error.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="designing-the-query-structure"&gt;Designing the Query Structure&lt;/h2&gt;
&lt;h3 id="designing-query-requests"&gt;Designing Query Requests&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;To improve caching and performance, try to avoid range queries. Workarounds include:
&lt;ul&gt;
&lt;li&gt;Use predefined queries&lt;/li&gt;
&lt;li&gt;Alternatively, use the HTTP header: Range&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Avoid using general-purpose query languages (SQL, XPATH).&lt;/li&gt;
&lt;li&gt;Avoid tight coupling between the URI and the underlying data storage (treating the backend as a database on the front end).&lt;/li&gt;
&lt;li&gt;For requests with many parameters, consider using POST (since URIs have a maximum length)
&lt;ul&gt;
&lt;li&gt;The downside of a POST interface is that it loses caching ability&lt;/li&gt;
&lt;li&gt;POST requests are not cacheable, so the Cache-Control and Expires headers are useless&lt;/li&gt;
&lt;li&gt;To solve the caching problem, have the POST create a temporary resource, return the link to the client, and let the client use GET to fetch that resource next time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="designing-query-response-results"&gt;Designing Query Response Results&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Return a collection. Add appropriate cache expiration headers.&lt;/li&gt;
&lt;li&gt;If there are no results, return an &lt;strong&gt;empty collection&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;</description></item></channel></rss>