1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2024-11-16 07:10:21 +02:00

fresh build

This commit is contained in:
Sergey Konstantinov 2024-08-21 22:45:53 +03:00
parent 3cd1399fbf
commit c0582a057b
6 changed files with 10 additions and 12 deletions

Binary file not shown.

View File

@ -868,8 +868,7 @@ h5 {
<p>Most of the examples of APIs will be provided in the form of JSON-over-HTTP endpoints. This is some sort of notation that, as we see it, helps to describe concepts in the most comprehensible manner. A <code>GET /v1/orders</code> endpoint call could easily be replaced with an <code>orders.get()</code> method call, local or remote; JSON could easily be replaced with any other data format. The semantics of statements shouldn't change.</p>
<p>Let's take a look at the following example:</p>
<pre><code class="language-json"><span class="hljs-comment">// Method description</span>
<span class="hljs-keyword">POST</span> /v1/bucket/<span class="hljs-substitution">{id}</span>/some-resource↵
/<span class="hljs-substitution">{resource_id}</span>
<span class="hljs-keyword">POST</span> /v1/buckets/<span class="hljs-substitution">{id}</span>/operation
X-Idempotency-Token: <span class="hljs-substitution">&#x3C;idempotency token></span>
{
@ -891,7 +890,7 @@ Cache-Control: no-cache
</code></pre>
<p>It should be read like this:</p>
<ul>
<li>A client performs a <code>POST</code> request to a <code>/v1/bucket/{id}/some-resource</code> resource, where <code>{id}</code> is to be replaced with some <code>bucket</code>'s identifier (<code>{something}</code> notation refers to the nearest term from the left unless explicitly specified otherwise).</li>
<li>A client performs a <code>POST</code> request to a <code>/v1/buckets/{id}/operation</code> resource, where <code>{id}</code> is to be replaced with some <code>bucket</code>'s identifier (<code>{something}</code> notation refers to the nearest term from the left unless explicitly specified otherwise).</li>
<li>A specific <code>X-Idempotency-Token</code> header is added to the request alongside standard headers (which we omit).</li>
<li>Terms in angle brackets (<code>&#x3C;idempotency token></code>) describe the semantics of an entity value (field, header, parameter).</li>
<li>A specific JSON, containing a <code>some_parameter</code> field and some other unspecified fields (indicated by ellipsis) is being sent as a request body payload.</li>
@ -902,8 +901,8 @@ Cache-Control: no-cache
</ul>
<p>The term “client” here stands for an application being executed on a user's device, either a native or a web one. The terms “agent” and “user agent” are synonymous with “client.”</p>
<p>Some request and response parts might be omitted if they are irrelevant to the topic being discussed.</p>
<p>Simplified notation might be used to avoid redundancies, like <code>POST /some-resource</code> <code>{…, "some_parameter", …}</code><code>{ "operation_id" }</code>; request and response bodies might also be omitted.</p>
<p>We will use sentences like “<code>POST /v1/bucket/{id}/some-resource</code> method” (or simply “<code>bucket/some-resource</code> method,” “<code>some-resource</code>” method — if there are no other <code>some-resource</code>s in the chapter, so there is no ambiguity) to refer to such endpoint definitions.</p>
<p>Simplified notation might be used to avoid redundancies, like <code>POST /operation</code> <code>{…, "some_parameter", …}</code><code>{ "operation_id" }</code>; request and response bodies might also be omitted.</p>
<p>We will use sentences like “<code>POST /v1/buckets/{id}/operation</code> method” (or simply “<code>buckets/operation</code> method,” “<code>operation</code>” method — if there are no other <code>operation</code>s in the chapter, so there is no ambiguity) to refer to such endpoint definitions.</p>
<p>Apart from HTTP API notation, we will employ C-style pseudocode, or, to be more precise, JavaScript-like or Python-like one since types are omitted. We assume such imperative structures are readable enough to skip detailed grammar explanations. HTTP API-like samples intend to illustrate the <em>contract</em>, i.e., how we would design an API. Samples in pseudocode are intended to illustrate how developers might work with the API in their code, or how we would implement SDKs based on the contract.</p><div class="page-break"></div><h2><a href="#section-2" class="anchor" id="section-2">Section I. The API Design</a></h2><h3><a href="#api-design-context-pyramid" class="anchor" id="api-design-context-pyramid">Chapter 9. The API Contexts Pyramid</a><a href="#chapter-9" class="secondary-anchor" id="chapter-9"> </a></h3>
<p>The approach we use to design APIs comprises four steps:</p>
<ul>
@ -3946,8 +3945,8 @@ X-Idempotency-Token: <span class="hljs-substitution">&#x3C;token></span>
<p>The most challenging aspect here is that not only does incremental progress, in the form of new platforms and protocols, necessitate changes to the API, but also the vulgar influence of trends. Several years ago realistic 3D icons were popular, but since then, public taste has changed in favor of flat and abstract ones. UI component developers had to follow the fashion, rebuilding their libraries by either shipping new icons or replacing the old ones. Similarly, the current trend of integrating the “night mode” feature has become widespread, demanding changes in a wide range of APIs.</p>
<h4>Backward-Compatible Specifications</h4>
<p>In the case of the API-first approach, the backward compatibility problem adds another dimension: the specification and code generation based on it. It becomes possible to break backward compatibility without breaking the spec (for example, by introducing eventual consistency instead of strict consistency) — and vice versa, modify the spec in a backward-incompatible manner without changing anything in the protocol and therefore not affecting existing integrations at all (for example, by replacing <code>additionalProperties: false</code> with <code>true</code> in OpenAPI).</p>
<p>The question of whether two specification versions are backward-compatible or not belongs to a gray zone, as specification standards themselves do not define this. Generally speaking, the statement “specification change is backward-compatible” is equivalent to “any client code written or generated based on the previous version of the spec continues to work correctly after the API vendor releases the new API version implementing the new version of the spec.” Practically speaking, following this definition seems quite unrealistic for two reasons: it is impossible to learn the behavior of every piece of code-generating software out there (for instance, it's rather hard to say whether code generated based on a specification that includes the parameter <code>additionalProperties: false</code> will still function properly if the server starts returning additional fields).</p>
<p>Thus, using IDLs to describe APIs with all the advantages they undeniably bring to the field, leads to having one aspect of the technology drift problem: the IDL version and, more importantly, versions of helper software based on it, are constantly and sometimes unpredictably evolving. If an API vendor employs the “code-first” approach, meaning that the spec is generated based on the actual API code, the occurrence of backward-incompatible changes in the server code — spec — code-generated SDK — client app chain is only a matter of time.</p>
<p>The question of whether two specification versions are backward-compatible or not belongs to a gray zone, as specification standards themselves do not define this. Generally speaking, the statement “specification change is backward-compatible” is equivalent to “any client code written or generated based on the previous version of the spec continues to work correctly after the API vendor releases the new API version implementing the new version of the spec.” Practically speaking, following this definition seems quite unrealistic as it is impossible to learn the behavior of every piece of code-generating software out there (for instance, it's rather hard to say whether code generated based on a specification that includes the parameter <code>additionalProperties: false</code> will still function properly if the server starts returning additional fields).</p>
<p>Thus, using IDLs to describe APIs with all the advantages they undeniably bring to the field, leads to having <em>one more</em> aspect of the technology drift problem: the IDL version and, more importantly, versions of helper software based on it, are constantly and sometimes unpredictably evolving. If an API vendor employs the “code-first” approach, meaning that the spec is generated based on the actual API code, the occurrence of backward-incompatible changes in the server code — spec — code-generated SDK — client app chain is only a matter of time.</p>
<p><strong>NB</strong>: We recommend sticking to reasonable practices such as not using functionality that is controversial from a backward compatibility point of view (including the above-mentioned <code>additionalProperties: false</code>) and when evaluating the safety of changes, considering spec-generated code behaves just like manually written code. If you find yourself in a situation of unresolvable doubts, your only option is to manually check every code generator to determine whether its output continues to work with the new version of the API.</p>
<h4>Backward Compatibility Policy</h4>
<p>To summarize the points discussed above:</p>

Binary file not shown.

Binary file not shown.

View File

@ -868,8 +868,7 @@ h5 {
<p>Большинство примеров API в общих разделах будут даны в виде абстрактных обращений по HTTP-протоколу к некоторой специфической именованной функции API («эндпойнту») с передачей данных в формате JSON. Это некоторая условность, которая помогает описать концепции, как нам кажется, максимально понятно. Вместо <code>GET /v1/orders</code> вполне может быть вызов метода <code>orders.get()</code>, локальный или удалённый; вместо JSON может быть любой другой формат данных. Смысл утверждений от этого не меняется.</p>
<p>Рассмотрим следующую запись:</p>
<pre><code class="language-json"><span class="hljs-comment">// Описание метода</span>
<span class="hljs-keyword">POST</span> /v1/bucket/<span class="hljs-substitution">{id}</span>/some-resource↵
/<span class="hljs-substitution">{resource_id}</span>
<span class="hljs-keyword">POST</span> /v1/buckets/<span class="hljs-substitution">{id}</span>/operation
X-Idempotency-Token: &#x3C;токен идемпотентности>
{
@ -890,7 +889,7 @@ Cache-Control: no-cache
</code></pre>
<p>Её следует читать так:</p>
<ul>
<li>клиент выполняет POST-запрос к ресурсу <code>/v1/bucket/{id}/some-resource</code>, где <code>{id}</code> заменяется на некоторый идентификатор <code>bucket</code>-а (при отсутствии уточнений подстановки вида <code>{something}</code> следует относить к ближайшему термину слева);</li>
<li>клиент выполняет POST-запрос к ресурсу <code>/v1/buckets/{id}/operation</code>, где <code>{id}</code> заменяется на некоторый идентификатор <code>bucket</code>-а (при отсутствии уточнений подстановки вида <code>{something}</code> следует относить к ближайшему термину слева);</li>
<li>запрос сопровождается (помимо стандартных заголовков, которые мы опускаем) дополнительным заголовком <code>X-Idempotency-Token</code>;</li>
<li>фразы в угловых скобках (<code>&#x3C;токен идемпотентности></code>) описывают семантику значения сущности (поля, заголовка, параметра);</li>
<li>в качестве тела запроса передаётся JSON, содержащий поле <code>some_parameter</code> со значением <code>value</code> и ещё какие-то поля, которые для краткости опущены (что показано многоточием);</li>
@ -901,8 +900,8 @@ Cache-Control: no-cache
</ul>
<p>Здесь термин «клиент» означает «приложение, установленное на устройстве пользователя, использующее рассматриваемый API». Приложение может быть как нативным, так и веб-приложением. Термины «агент» и «юзер-агент» являются синонимами термина «клиент».</p>
<p>Ответ (частично или целиком) и тело запроса могут быть опущены, если в контексте обсуждаемого вопроса их содержание не имеет значения.</p>
<p>Возможна сокращённая запись вида: <code>POST /some-resource</code> <code>{…,"some_parameter",…}</code><code>{ "operation_id" }</code>; тело запроса и/или ответа может опускаться аналогично полной записи.</p>
<p>Чтобы сослаться на это описание будут использоваться выражения типа «метод <code>POST /v1/bucket/{id}/some-resource</code>» или, для простоты, «метод <code>some-resource</code>» или «метод <code>bucket/some-resource</code>» (если никаких других <code>some-resource</code> в контексте главы не упоминается и перепутать не с чем).</p>
<p>Возможна сокращённая запись вида: <code>POST /operation</code> <code>{…,"some_parameter",…}</code><code>{ "operation_id" }</code>; тело запроса и/или ответа может опускаться аналогично полной записи.</p>
<p>Чтобы сослаться на это описание будут использоваться выражения типа «метод <code>POST /v1/buckets/{id}/operation</code>» или, для простоты, «метод <code>operation</code>» или «метод <code>buckets/operation</code>» (если никаких других <code>operation</code> в контексте главы не упоминается и перепутать не с чем).</p>
<p>Помимо HTTP API-нотации мы будем активно использовать C-подобный псевдокод — точнее будет сказать, JavaScript или Python-подобный, поскольку нотации типов мы будем опускать. Мы предполагаем, что подобного рода императивные конструкции достаточно читабельны, и не будем здесь описывать грамматику подробно. Примеры в виде HTTP API-нотации призваны иллюстрировать дизайн <em>контрактов</em>, т.е. показывать, как мы разрабатываем API. Примеры в псевдокоде обычно отражают, какой код напишут разработчики для работы с API, или как бы мы сами написали SDK на основе такого контракта.</p><div class="page-break"></div><h2><a href="#section-2" class="anchor" id="section-2">Раздел I. Проектирование API</a></h2><h3><a href="#api-design-context-pyramid" class="anchor" id="api-design-context-pyramid">Глава 9. Пирамида контекстов API</a><a href="#chapter-9" class="secondary-anchor" id="chapter-9"> </a></h3>
<p>Подход, который мы используем для проектирования, состоит из четырёх шагов:</p>
<ul>

Binary file not shown.