mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-01-05 10:20:22 +02:00
clarifications & style fixes
This commit is contained in:
parent
913ba8d2e8
commit
0b2a4d945e
@ -174,11 +174,11 @@ It's considered good form to use globally unique strings as entity identifiers,
|
||||
|
||||
In general, we tend to advice using urn-like identifiers, e.g. `urn:order:<uuid>` (or just `order:<uuid>`). That helps a lot in dealing with legacy systems with different identifiers attached to the same entity. Namespaces in urns help to understand quickly which identifier is used, and is there a usage mistake.
|
||||
|
||||
One important implication: **never use increasing numbers as external identifiers**. Apart from abovementioned reasons, it allows counting how many entities of each types there are in the system. You competitors will be able to calculate a precise number of orders you have each day, for example.
|
||||
One important implication: **never use increasing numbers as external identifiers**. Apart from abovementioned reasons, it allows counting how many entities of each type there are in the system. You competitors will be able to calculate a precise number of orders you have each day, for example.
|
||||
|
||||
**NB**: this book often use short identifiers like "123" in code examples; that's for reading the book on small screens convenience, do not replicate this practice in a real-world API.
|
||||
|
||||
##### Clients must always know full system state
|
||||
##### System state must be observable by clients
|
||||
|
||||
This rule could be reformulated as ‘don't make clients guess’.
|
||||
|
||||
@ -372,7 +372,7 @@ Transferring only a subset of fields solves none of these problems, in the best
|
||||
* introducing pagination and field value length limits;
|
||||
* stopping saving bytes in all other cases.
|
||||
|
||||
**In second**, shortening response sizes will backfire exactly with sploiling collaborative editing: one client won't see the changes the other client have made. Generally speaking, in 9 cases out of 10 it is better to return a full entity state from any modifying operation, sharing the format with read access endpoint. Actually, you should always do this unless response size affects performance.
|
||||
**In second**, shortening response sizes will backfire exactly with sploiling collaborative editing: one client won't see the changes the other client has made. Generally speaking, in 9 cases out of 10 it is better to return a full entity state from any modifying operation, sharing the format with read access endpoint. Actually, you should always do this unless response size affects performance.
|
||||
|
||||
**In third**, this approach might work if you need to rewrite a field's value. But how to unset the field, return its value to the default state? For example, how to *remove* `client_phone_number_ext`?
|
||||
|
||||
@ -505,7 +505,7 @@ X-Idempotency-Token: <token>
|
||||
```
|
||||
— the server found out that a different token was used in creating revision 124, which means an access conflict.
|
||||
|
||||
Furthermore, adding idempotency tokens not only resolves the issue, but also makes possible to make an advanced optimization. If the server detects an access conflict, it could try to resolve it, ‘rebasing’ the update like modern version control systems do, and return `200 OK` instead of `409 Conflict`. This logics dramatically improves user experience, being fully backwards compatible and avoiding conflict resolving code fragmentation.
|
||||
Furthermore, adding idempotency tokens not only resolves the issue, but also makes advanced optimizations possible. If the server detects an access conflict, it could try to resolve it, ‘rebasing’ the update like modern version control systems do, and return `200 OK` instead of `409 Conflict`. This logics dramatically improves user experience, being fully backwards compatible and avoiding conflict resolving code fragmentation.
|
||||
|
||||
Also, be warned: clients are bad at implementing idempotency tokens. Two problems are common:
|
||||
* you can't really expect that clients generate truly random tokens — they may share the same seed or simply use weak algorithms or entropy sources; therefore you must put constraints on token checking: token must be unique to specific user and resource, not globally;
|
||||
@ -776,6 +776,12 @@ One advantage of this approach is the possibility to keep initial request parame
|
||||
|
||||
There are several approaches to implementing cursors (for example, making single endpoint for initial and follow-up requests, returning the first data portion in the first response). As usual, the crucial part is maintaining consistency across all such endpoints.
|
||||
|
||||
**NB**: some sources discourage this approach because in this case user can't see a list of all pages and can't choose an arbitrary one. We should note here that:
|
||||
* such a case (pages list and page selection) exists if we deal with user interfaces; we could hardly imagine a *program* interface which needs to provide an access to random data pages;
|
||||
* if we still talk about an API to some application, which has a ‘paging’ user control, then a proper approach would be to prepare ‘paging’ data on server, including generating links to pages;
|
||||
* cursor-based solution doesn't prohibit using `offset`/`limit`; nothing could stop us from creating a dual interface, which might serve both `GET /items?cursor=…` and `GET /items?offset=…&limit=…` requests;
|
||||
* finally, if there is a necessity to provide an access to arbitrary pages in user interface, we should ask ourselves a question, which problem is being solved that way; probably, users use this functionality to find something: a specific element on the list, or the position they ended while working with the list last time; probably, we should provide more convenient controls to solve those tasks than accessing data pages by their indexes.
|
||||
|
||||
**Bad**:
|
||||
```
|
||||
// Returns a limited number of records
|
||||
|
@ -21,7 +21,12 @@
|
||||
The book is dedicated to designing APIs: how to build the architecture
|
||||
properly, from a high-level planning down to final interfaces.
|
||||
</p>
|
||||
<p>Illustrations by Maria Konstantinova</p>
|
||||
<p>
|
||||
Illustrations by Maria Konstantinova<br /><a
|
||||
href="https://www.instagram.com/art.mari.ka/"
|
||||
>https://www.instagram.com/art.mari.ka/</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<img
|
||||
class="cc-by-nc-img"
|
||||
|
@ -173,7 +173,7 @@ str_replace(needle, replace, haystack)
|
||||
|
||||
**NB**: в этой книге часто используются короткие идентификаторы типа "123" в примерах — это для удобства чтения на маленьких экранах, повторять эту практику в реальном API не надо.
|
||||
|
||||
##### Клиент всегда должен знать полное состояние системы
|
||||
##### Состояние системы должно быть понятно клиенту
|
||||
|
||||
Правило можно ещё сформулировать так: не заставляйте клиент гадать.
|
||||
|
||||
@ -774,6 +774,12 @@ GET /v1/records?cursor=<значение курсора>
|
||||
|
||||
Вообще схему с курсором можно реализовать множеством способов (например, не разделять первый и последующие запросы данных), главное — выбрать какой-то один.
|
||||
|
||||
**NB**: в некоторых источниках такой подход, напротив, не рекомендуется, по следующей причине: пользователю невозможно показать список страниц и дать возможность выбрать произвольную. Здесь следует отметить, что:
|
||||
* подобный кейс — список страниц и выбор страниц — существует только для пользовательских интерфейсов; представить себе API, в котором действительно требуется доступ к случайным страницам данных мы можем с очень большим трудом;
|
||||
* если же мы всё-таки говорим об API приложения, которое содержит элемент управления с постраничной навигацией, то наиболее правильный подход — подготавливать данные для этого элемента управления на стороне сервера, в т.ч. генерировать ссылки на страницы;
|
||||
* подход с курсором не означает, что `limit`/`offset` использовать нельзя — ничто не мешает сделать двойной интерфейс, который будет отвечать и на запросы вида `GET /items?cursor=…`, и на запросы вида `GET /items?offset=…&limit=…`;
|
||||
* наконец, если возникает необходимость предоставлять доступ к произвольной странице в пользовательском интерфейсе, то следует задать себе вопрос, какая проблема тем самым решается; вероятнее всего с помощью этой функциональности пользователь что-то ищет — определенный элемент списка или может быть позицию, на которой он закончил работу со списком в прошлый раз; возможно, следует предоставить для этих задач более удобные элементы управления, нежели перебор страниц.
|
||||
|
||||
**Плохо**:
|
||||
```
|
||||
// Возвращает указанный limit записей,
|
||||
|
@ -22,7 +22,12 @@
|
||||
архитектуру, начиная с высокоуровневого планирования и заканчивая
|
||||
деталями реализации конкретных интерфейсов.
|
||||
</p>
|
||||
<p>Иллюстрации: Мария Константинова</p>
|
||||
<p>
|
||||
Иллюстрации: Мария Константинова<br /><a
|
||||
href="https://www.instagram.com/art.mari.ka/"
|
||||
>https://www.instagram.com/art.mari.ka/</a
|
||||
>
|
||||
</p>
|
||||
|
||||
<img
|
||||
class="cc-by-nc-img"
|
||||
|
Loading…
Reference in New Issue
Block a user