Нескучный API


    image

    Как создать АПИ для умных? Такое апи, чтобы создание клиента для него было не скучным механическим процессом, а настоящим приключением с элементами детектива, хоррора и мистики? Такое апи, о котором пользователи будут взахлёб рассказываете коллегам? Апи взрывающее мозг, заставляющее смеяться, кричать и плакать? Я постарался отобрать лучшие практики, с которыми пришлось столкнуться.




    Делай так


    1. Избегай очевидных названий. checkInDate лучше записать как start_date, еще лучше как sd, еще лучше как d. Тогда длинное и некрасивое checkInDate/checkOutDate превратится в лаконичное d1/d2. Еще больше отличных идей по именованию можно почерпнуть в книге “Совершенный код” в главе “Сила имен переменных”
    2. Не используй стандарты. ISO 8601? Чтобы преобразовывать дату с использованием стандартных библиотек, имеющихся в любом языке программирования и не написав ни одной регулярки? Скукота.
    3. Не используй в названиях термины предметной области. Лучше придумай что-нибудь максимально абстрактное.
    4. Придумай свою систему обозначения состояний. К примеру: 1,2,3 — есть, нет, под заказ. Не вздумай писать состояния напрямую и уж тем более, никогда не используй справочник состояний.
    5. Передавай вложенные структуры как список плоских связанных через внутренние айдишники, сгенерированные специально под этот ответ.
      {
      Jacket: \[{MaterialsIds=”m1,m2”, Pockets: “p1”, Price: 12000}\], 
      Materials: \[{Name: “Stormscale”, Color: “Tear of Elune”,  InnerId=”m1”}, 
                   {Name: “Feather of Valor”, Color: “unpredictable”, InnerId=”m2”}\], 
      Pockets:\[{Type: “Invisible”, Contents: “Soap”, InnerId="p1"}\]}
      }
    6. Не повторяйся.


      a. Придумай разные системы для обозначения состояний разных объектов. К цифровой, описанной выше, можно добавить цветовую: green, red, yellow.


      b. Меняй не только формат данных, но и формат записи названий полей — srart_date: 25/12/05, endDay: 2012.11.23.


      c. Не забудь про огромный выбор разделителей: запятая, точка, точка с запятой. Добавь в него что-нибудь нестандартное — знак процента или решетку.


      j. Список можно продолжать, будь креативен.


    7. Замени True/False на 1/0, а ещё лучше на 0/1. Превратив простое “has_goods: True” в четыре вопроса — айдишник? количество? просто ‘да’? или, таки, “нет”?
    8. Используй битовые флаги. Так ты продемонстрируешь свою исключительную образованность. Переведи их в десятичную форму для меньшей очевидности. Не передавай маску вместе с флагами, или даже в отдельном словаре. “flgs: 16” в структуре и список флагов в документации — прекрасный вариант.
    9. Старайся засунуть в каждое поле как можно больше смыслов.
      has_goods может принимать значения [-2, -1, 0, любое положительное число], 
      что значит: [уточните, нет в продаже, нет в наличии, количество товара в наличии]
    10. Добавьте в запрос обязательный параметр, у которого может быть только одно значение.


    11. Добавь в запрос параметр, смысл которого будет менять в зависимости от значения другого, необязательного, параметра.
      http://example.com/myService?cloth_type=silk&and_better=1
      Где  "cloth_type"  - обозначает конкретный тип ткани (тут, естественно, требуется название ткани строкой, даже если есть справочник тканей, в котором указаны их айдишники), а "and_better" делает его началом диапазона с константным концом. Для “большей гибкости” можно дать параметру "and_better" второе значение (-1, 0, false, not) или добавить необязательный параметр "and_worst" и обрабатывать тот, который стоит первым/последним, либо сначала проверять наличие первого, а при его отсутствии - второго.
    12. Задавай диапазоны на перечислимых данных последовательным списком, а на данных, порядок которых определен только в твоём приложении, началом и концом диапазона, а еще лучше через дефис или другой разделитель по вкусу.
      http://example.com/myService?d=11.12.05&d=12.12.05&d=13.12.05&colors=green;yellow
    13. Используй однотипные названия для полей с разными типами данных.
      http://example.com/myService?cats=5&dogs=4&hamsters=0 где cats и dogs интовые поля обозначающие количество, а hamsters битовое поле обозначающее наличие.
    14. Не обрабатывай ошибки. Переданы несовместимые параметры? Пустой ответ должен стать достаточной подсказкой. Или отсутствие ответа. Или отправь эксепшн сгенерированный твоим приложением.
    15. Документация не нужна.


      a. Если пришлось документировать, пропусти наиболее “очевидные” места.


      b. Не усложняй документацию примерами.


      c. Задокументируй отсутствующий функционал, который ты обязательно допишешь потом.


      d. Раздели документацию на несколько частей и каждую отдавай только тем пользователям, которые догадаются про неё спросить.



    Этот список основан на реальных событиях, имена изменены в интересах конфиденциальности, все совпадения не случайны.


    P.S. Напишите о своих героях и их былинных деяниях в комментариях.

    Поделиться публикацией
    Похожие публикации
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 67
      +5
      Soap, вы забыли про soap!
        +2
        Soap который возвращает строку c JSON :)
          0
          В двух кодировках.
            +3

            Просто JSON в Soap — это скучно, такое почти в любой системе с достаточной историей можно найти. А вот когда строки в JSON — это сериализованные Java-объекты, закодированные в Base64, тут уже становится интереснее.

              +5

              Из собственного опыта:
              Soap, в нем json, в нем имя файла и base64 строка, в ней zip архив, а в нем xml файл

                +1
                Сказка на новый лад — иголка в яйце, яйцо в утке, утка в зайце, заяц в архиве, архив в Base64…
                  +2
                  этот xml способен убить Кощея?
                    0
                    В нём отчёт для налоговой.
                    Да.
            0

            Если явно не указать, что это "вредные советы", кто-то вполне может воспринять их как руководство к действию.-)

              0

              Пока не прочитал первое предложение первого пункта, я так и думал)

              0

              Спасибо! Но не хватает практических примеров, как эти советы использовать.

                +8
                И вовсе не обязательно присылать ответ в том же запрос! Это так скучно и банально. Выкладывайте ответы в виде файлов на FTP, которые нужно стягивать после запроса. Даже если там две строчки ответа. Так интереснее.
                • НЛО прилетело и опубликовало эту надпись здесь
                  +4
                  Из своей практики работы со сторонними API:
                  — сообщения об ошибках в нескольких разных форматах — plaintext, строка в json и массив в json. А в документации чёрным по белому формат описан и он один. Чтобы было совсем не скучно — у сообщения об ошибке нет машиночитаемого кода, только описание — которое для некоторых ошибок может возвращаться на разных языках.
                  — разные размерности одних и тех же данных. Здесь amount — сумма в валюте клиента, а тут (той же самой сущности рекламного объявления!) amount — всегда в центах USD. О, или ещё лучше: если у клиента валюта «рубли» — то сумма в рублях, если USD — то сумма передаётся… — в центах! Для красоты саму валюту клиента через API не узнать никак.
                  — в документации в принципе отсутствуют допустимые значения для строк-перечислений, при том как для параметров так и для возвращаемых значений. status => string. Ура! Пишем логирование всех приходящих статусов и разбираемся что есть что по ходу пьесы, т.к. список возможных получить так и не смогли.
                  — запрос к сервису через API скидывает авторизацию пользователя, с ключом которого запрос выполнили — было крайне нескучно постоянно авторизовываться.
                  — неожиданные ограничения API — например, у пользователя есть логин и email, в API надо передать email. И вдруг получаем от части пользователей багрепорт, где выясняется что по email только некоторых пользователей авторизовать нельзя. А по логину — нет возможности в API.
                  — ошибиться в документации с написанием имени параметра. Ну то есть написать checkOutDate вместо реально используемого check_out_date
                  — в ответе на ошибочный запрос с полусотней параметров сказать «some error occured» без указания, а что именно не так или хотя бы в каком параметре
                    0

                    Когда тебе в ответе приходят поля вида Nom_bil_kn, cod_t или cod_h — разобраться бывает непросто как при наличии документации, так и при её отсутствии.-/

                    +5
                    • Обновляй API как можно чаще: беззастенчиво удаляй лишние запросы, но добавляй новые (обязательные!) параметры в уже существующие. Уведомлять пользователей нет необходимости — сами увидят, когда все сломается.
                    • Если ты так щедр, что предоставляешь тестовый сервис, то пусть он будет отставать от боевого. И работает раз в неделю. Хотя нет, работать ему необязательно.
                      0

                      А ещё можно тестить измененные методы на боевом сервере, отправлять фейковые ответы, а переданные данные не сохранять.

                      0
                      Забудь про версионирование API. Пускай каждую неделю/месяц разработчики сами подстраиваются и переписывают приложение под десятки breaking changes. Зато у них всегда будут твои новые фичи.
                        0
                        Если необязательный параметр решили сделать обязательным, или ввести для него ограничения — для этого тоже что ли версию API менять?
                          0
                          Не уверен, что компетентен ответить на этот вопрос. Моя боль с клиентской стороны состояла в постоянном изменении структуры запроса/ответа, их полей и типов.
                            0
                            Обычно принято мажорную версию менять при любом breaking change. Ибо для клиента это может стать сюрпризом.
                            Если бы обязательный сделали необязательным — ОК, обратная совместимость поддерживается.
                          +6
                          Тот случай, когда стыдно признаться
                          про меня на хабре написали
                            0
                            В ответ на запрос отправлять «запрос обработан», а чтобы получить ответ, надо вызывать отдельный метод «дай_ответ», который будет отправлять ответ на какой-нибудь запрос (маппить запрос-ответ будет клиент API по айдишникам).
                              +1

                              Вообще, если ваш запрос был заявкой на длительную операцию по обработке, то это вполне разумный организации асинхронных запросов.

                                0
                                вот это уже стандарт — есть HTTP код 202 accepted, который означает, что запрос принят на обработку
                                и в лучших сценариях в ответе есть url, или иной способ получить статус действия
                                0
                                Если у автора фамилия не «Остер», то он явно его родственник
                                  –2
                                  checkInDate/checkOutDate

                                  Смотрел на эту конструкцию, как среда на пятницу.
                                    0
                                    Очень похоже на описание внутренней структуры SAP'a.
                                      +10

                                      Если нужно вернуть код ответа, отличный от дефолтного, посылайте
                                      { code: 403, message: "У вас нет прав" }
                                      с HTTP кодом 200

                                        +1
                                        Что-то знакомое. Кажется для флеша надо было любой ценой возвращать ответ 200, даже если внутри по факту ошибка.
                                          0

                                          Ага. Некоторые еще не видят разницы между POST, PUT/PATCH, DELETE. Типа «а нафига оно надо, если есть POST?».

                                          0
                                          Вот буквально в час ночи от сегодня в одном API сменились лимиты и вот хоть стой хоть падай, пока тех поддержка отвечала, выяснил с внезапно изменившейся документацией без версий и логов, что лимиты так прилично зарезали. Очень классный кипеш был с утра в паре интернет магазинов. И это я уже научен горьким опытом от греха хранить полный ответ сервера апи.
                                            +3
                                            Пфф. Все гораздо проще. Возьмем API ВКонтакте для примера:

                                            1. Введите маленький лимит на число запросов для замедления работы всех приложений.
                                            2. Возвращайте каптчу на запросы от серверного API и предлагайте показать ее пользователям, которые оффлайн.
                                            3. Когда лень показывать каптчу, просто возвращайте ошибку «Flood control», т.к. запросы к API посылаются, внимание, слишком однотипные (ведь в API нужно использовать как минимум 99% из доступных методов, хотя они вам не нужны).

                                            P.S. Извините, накипело.
                                              –1
                                              example.com/myService?d=11.12.05&d=12.12.05&d=13.12.05&colors=green;yellow

                                              На сервер передастся d=13.12.05
                                              example.com/myService?d[1]=11.12.05&d[2]=12.12.05&d[3]=13.12.05&colors=green;yellow

                                              — так понятнее будет
                                                0
                                                Зависит от обработчика, вот обсуждение на SO.
                                                  0
                                                  если делать в духе статьи то вообще лучше так
                                                  example.com/myService?d_1=11.12.05&d_2=12.12.05&d_3=13.12.05&colors=green;yellow
                                                  0

                                                  Отнюдь, нужно использовать первый вариант, а потом вручную парсить строку запроса на сервере, даже если бэкенд написан на PHP. Пусть программисты, которым поддерживать этот код после вас, поломают головы :-)


                                                  Кроме шуток, встречал такое разок.

                                                    0
                                                    Поддержу. Тоже как-то встретил, прям офигел. Всю жизнь думал, что так делать нельзя, а оказалось это даже обрабатывается некоторыми веб-серверами.

                                                    Но это те еще страдания: 99.9% библиотек не поддерживают такой формат :)
                                                    • НЛО прилетело и опубликовало эту надпись здесь
                                                  0

                                                  Черт, пара пунктов исполнена, но это из-за легаси и обратной совместимости, честно!

                                                    +4
                                                    бесят эти ендпоинты и гэт-параметры,

                                                    лучше эндпоинт писать в хедер

                                                    MY-HEADER-ENDPOINT="/users"


                                                    дада… именно в верхнем регистре, это подчеркнет властность над глупыми машинами!

                                                    и гэт параметры:

                                                    MY-HEADER-GET=«key1=value1<ITS-MY-SEPARATOR>key2=value2»


                                                    так будет урл красивый! И данные спрятаны под капотом.

                                                    А чтобы совсем не скучно было:

                                                    1. эндпоинт хешируем: md5(base64(..))
                                                    2. бекэнд смотрит таблицу эндпоинтов в md5(base64(..))
                                                    3. если мд5-баса64 не найдет — статус писать 200-ОК, респонс писать {BAD} — и так чтобы было похоже на ясон, но не получилось прочитать как ясон, пусть знают как неправильные ендпоинты отправлять!!!
                                                    4. обязательно нужно обернуть хедер гетов в какойнибуть base64, можно даже несколько раз, или свою функцию написать в свободное время

                                                    ох, самое важное — отовсюду в коде стирать свои копирайты, емейлы, телефоны!…
                                                    А еще лучше — вписать кругом контактные данные одноклассника, который у тебя булочки отбирал, будет знать!!!
                                                      +1

                                                      Я бы посоветовал вам ещё использовать функцию crypt в php или ее аналог, если используете другой язык, чтобы для одинаковых строк хеш всегда был разным. Ну и на мой взгляд, здесь явно не хватает XML.

                                                        0

                                                        crypt — слишком медленно)


                                                        v = 0
                                                        function superHash(anything) {
                                                            return v++ 
                                                        }
                                                      0
                                                      В POST-запрос всегда одну половину параметров передавай в body запроса и другую как в query-параметры в URL.
                                                        0

                                                        та лаадно, там все равно на сервере все операции проводятся над $_REQUEST

                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                        +2
                                                        Единицы измерения: очень хорошо, когда одно и то же значение должно в разных местах задаваться разными единицами измерения. Или чтобы прочитать можно было только в одной единице, а записывать — только в другой.

                                                        Требуйте обязательного заполнения как можно большего количества полей. Особенно, если бизнес-процесс предполагает, что некоторые данные иногда сразу получить невозможно.

                                                        Требуйте наличия поля «дата документа», которое должно совпадать с текущей датой. Особенно, если данные поступают круглосуточно. Особенно, если принимаются данные из другого часового пояса.

                                                        Ещё — очень хорошо требовать определённый порядок полей в XML.
                                                          0
                                                          А вообще, какой-такой API, давайте мы будем присылать экселевские файлики по почте. Или на FTP.
                                                            +1
                                                            Хе, на почту. На 1.44 дискетку в дочерном офисе во Владивостоке с паспортом и бумажным заявлением с мокрой печатью. Если ексельник 100-метровый — архивируется и каждый архив кидается на дискету. Для самой смакотки вместо 43-й части можно повторно кинуть 42-у.
                                                              0
                                                              Кстати с экселем, отсылаемым на почту вполне себе работоспособный вариант — это один из возможных способов для оператора связи отчитаться перед министерством связи по различным статистическим показателям.
                                                                0
                                                                Только если отправлять экселевский файл буду я, и если получателем будет какое-нибудь министерство.
                                                              0

                                                              Надо было скомбинировать данные стороннего api и сервиса для создания api для нужд сайта. Сделал франкенштейна из этих данных и это угнетает. Как было бы сделать правильно?

                                                                0
                                                                Зачем нужны коды ошибок?
                                                                Только подробное описание, которое, может быть, разработчик поймет
                                                                {
                                                                   errorMessage: "DB: uniqueConstraint error for asdf@email.ru"
                                                                }
                                                                  +2
                                                                  Вы слишком детально расписываете проблему!

                                                                  {
                                                                     errorMessage: "DB: uniqueConstraint error"
                                                                  }

                                                                  Так более чем достаточно! Сам несколько раз встреча подобное!
                                                                    +1
                                                                    Так и вы переборщили!

                                                                    {
                                                                         success: false,
                                                                         errorCode: "24672b"
                                                                    }
                                                                    

                                                                    Всё, достаточно! А таблицу кодов прячем на 237-ую страницу мануала, ссылку на который нужно найти во втором мануале. Который высылают только по требованию, после оплаты dev-аккаунта. Через 4 дня.
                                                                      +3
                                                                      Только этого кода там нет.
                                                                        +1

                                                                        потому что errorCode это md5(real_error_code + timestamp)

                                                                  0
                                                                  ISO 8601 — это еще тот стандарт для любителей веселухи.
                                                                  Из практики, почти всегда можно найти вариант, который будет полностью соответствовать стандарту, но не поддерживаться или неправильно интерпретироваться конкретной библиотекой.
                                                                  Так что, да, указывайте просто ISO 8601, вместо например RFC 3339. Тестерам это реально доставит :-).
                                                                    0
                                                                    Это всё конечно хорошо, но вот в пункте 8 у меня появился вопрос: где же хранить сами флаги, если не в документации и в коде, использующем их?
                                                                    У меня вот иногда под флаги отдельный файл сделан, чтобы в нём хранить их… Чяднт?
                                                                      0
                                                                      Да, тут пункты, по-моему, несоизмеримы.
                                                                      Часть пунктов — реально жесть, но некоторые — скорее отображают неприятие автором некоторых вещей (которые, если их правильно готовить, в принципе неплохи (а если готовить неправильно, так что угодно сломать можно)).
                                                                      Хотя это юмор, так что требовать точности — вряд ли уместно.
                                                                      0
                                                                      Я лично таких API не писал, но, по-моему, некоторые из пунктов (или, точнее, некоторые из частей некоторых пунктов) — например: 5 («передавай вложенные структуры как список плоских»), 7 («замени True/False на 1/0» — только тогда, конечно, точные названия выбирать надо), 8 («используй битовые флаги»), 9 («[уточните, нет в продаже, нет в наличии, количество товара в наличии]» — если эти значения действительно взаимоисключаемые по логике приложения), 10 («обязательный параметр, у которого может быть только одно значение» — например, номер версии) — вполне приемлемые в определённых сферах или, по крайней мере, пусть и плохи, но на порядок лучше других (то есть степень вреда разных пунктов несоизмерима; например, если нет вменяемого отчёта об ошибке, отличимого от успешного ответа — это бардак; а если возвращается битовая маска, но она хорошо задокументирована, то это ок).
                                                                        0
                                                                        Из моего, недавнего:
                                                                        1) в разных методах апи возвращайте разные объекты(в документации сделайте копипаст)
                                                                        2) внутри апи сделайте разные методы, которые возвращают объекты и счётчик по ним, а потом исправьте ошибки только в одном из этих методов
                                                                          0

                                                                          По 7 пункту я сразу вспомнил фьюзы у микроконтроллеров AVR. Где 0 — это установлен. И можно было бы привыкнуть, но некоторые программы для прошивки инвертируют их. Что запутывает окончательно, особенно когда используешь несколько таких программ.

                                                                            +1
                                                                            Это не от балды так сделано. В дофлешовые времена фьюзы реально были плавкими перемычками, которые нужно было пережигать импульсом тока. И вполне логично, что целая перемычка — это 1, а пережженная — 0.
                                                                            +1

                                                                            Документация? Конечно, должна быть! Такая, ммм, по всем правилам из поста. Без примером и ограничений. И при любой проблеме мы будем отправлять пользователя перечитывать ее. Не зря ж писали.

                                                                              0

                                                                              Жалкая пародия на этот фундаментальный труд:


                                                                              https://learn.javascript.ru/write-unmain-code

                                                                                0
                                                                                За ссылку спасибо, но называя чей-то высер труд пародией будьте любезны потрудиться привести аргументы.

                                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                              Самое читаемое