lenec ru

← все посты

Curl-примеры в документации API: чек-лист, чтобы они работали

19K

Документация API без работающих примеров — половина документации. Человек открывает страницу, видит curl-команду, копирует в терминал, получает ошибку. Поправил кавычки — снова ошибка. Поменял URL — снова. Через десять минут он закрывает доку и идёт читать чужой клиентский код, надеясь увидеть рабочий вызов.

Curl-примеры — самый универсальный способ показать запрос к API. Они работают везде: на Linux, macOS, Windows (с WSL или нативным curl), в CI, в трейнингах. Но писать их ленятся: копируют первое, что попалось, забывают экранировать, не проверяют. Разберу чек-лист, по которому пишутся curl-примеры в документации, чтобы они не разочаровывали через год после публикации.

Базовый шаблон, от которого отталкиваюсь

curl -X POST https://api.example.com/v1/users \
  -H 'Content-Type: application/json' \
  -H 'Authorization: Bearer YOUR_TOKEN_HERE' \
  -d '{
    "email": "alice@example.com",
    "name": "Alice"
  }'

Что в нём правильного:

  • Метод указан явно через -X, даже если он POST по умолчанию для -d.
  • URL — полный, https, с версией API.
  • Заголовки на отдельных строках через \ — копируется построчно при чтении.
  • JSON-тело отформатировано, не в одну строку.
  • Плейсхолдер YOUR_TOKEN_HERE — большими буквами, очевидно, что это плейсхолдер.

Пример короче, если запрос проще, но структура та же.

Кавычки: одинарные снаружи, двойные внутри

Самая частая ошибка в curl-примерах — путаница с кавычками. Bash, zsh, Windows cmd обрабатывают их по-разному.

Было (часто встречается):

curl -X POST https://api.example.com/v1/users \
  -H "Content-Type: application/json" \
  -d "{\"email\": \"alice@example.com\"}"

Двойные кавычки снаружи + экранированные двойные внутри. На bash работает, но читается ужасно, и при копировании в Windows cmd ломается.

Стало:

curl -X POST https://api.example.com/v1/users \
  -H 'Content-Type: application/json' \
  -d '{"email": "alice@example.com"}'

Одинарные снаружи — bash не интерпретирует содержимое, JSON остаётся как есть. Внутри JSON — двойные, как требует стандарт.

Правило: одинарные кавычки для всего, что не содержит переменных окружения. Если нужно подставить переменную — двойные снаружи и экранирование, но это исключение.

Подстановка переменных окружения

Когда в примере токен — стоит сразу показать, как его взять из переменной:

export API_TOKEN="your-actual-token"

curl -X GET https://api.example.com/v1/users \
  -H "Authorization: Bearer $API_TOKEN"

Двойные кавычки тут нужны, чтобы bash подставил $API_TOKEN. Это сразу учит читателя правильному паттерну: токен не лежит в shell history открытым текстом.

Альтернатива — --header "Authorization: Bearer ${API_TOKEN:?token not set}". Bash при пустом значении упадёт с понятной ошибкой, а не отправит запрос с Bearer . Полезно в скриптах, в документации обычно избыточно.

JSON в теле: -d vs --data-raw vs @file

Три способа передать тело, и у каждого свои нюансы.

-d со строкой:

curl -d '{"key": "value"}' ...

Подходит для коротких тел. -d по умолчанию ставит Content-Type: application/x-www-form-urlencoded — поэтому всегда явно указывай -H 'Content-Type: application/json', иначе сервер примет JSON за форму.

-d @file.json:

curl -d @body.json -H 'Content-Type: application/json' ...

Удобно для длинных тел. Но в документации работает плохо — у читателя нет файла body.json. Используй для скриптов, не для примеров в доке.

--data-raw:

curl --data-raw '{"key": "value"}' ...

Отличается от -d тем, что не интерпретирует @ в начале строки как файл. Полезно, когда тело начинается с @. В обычных примерах не нужен.

Экранирование специальных символов

Если в значении есть $, !, обратный слэш — bash может это интерпретировать.

Безопасно:

curl -d '{"password": "P@ssw0rd!"}' ...

Одинарные кавычки выключают подстановку. Если по какой-то причине нужны двойные:

curl -d "{\"password\": \"P\\@ssw0rd\\!\"}" ...

Это уже минное поле. Лучше переписать через одинарные.

URL-параметры и пробелы

Параметры в URL надо URL-кодировать. Пробел — %20, амперсанд внутри значения — %26, и так далее. Это часто забывают.

Было:

curl 'https://api.example.com/search?q=hello world&limit=10'

Может работать, может не работать (зависит от того, как прокси обрабатывает пробелы). Гарантированно работает:

curl 'https://api.example.com/search?q=hello%20world&limit=10'

Альтернатива — флаг -G + --data-urlencode:

curl -G https://api.example.com/search \
  --data-urlencode 'q=hello world' \
  --data-urlencode 'limit=10'

Curl сам соберёт URL и закодирует значения. В документации первый вариант понятнее, второй — для скриптов с пользовательским вводом.

Что показывать в ответе

Хороший пример показывает не только запрос, но и что ожидать в ответе:

curl -X GET https://api.example.com/v1/users/42 \
  -H 'Authorization: Bearer YOUR_TOKEN'

Ответ:

{
  "id": 42,
  "email": "alice@example.com",
  "name": "Alice",
  "created_at": "2026-05-15T10:30:00Z"
}

Без ответа читатель не знает, тот ли вызов. Тратит ещё минуту, парся документацию схемы. Покажи короткий пример — экономия очевидна.

Если ответ длинный — обрежь до первых полей и поставь ... в массивах:

{
  "users": [
    { "id": 1, "email": "alice@example.com" },
    { "id": 2, "email": "bob@example.com" }
  ],
  "total": 247,
  "next_cursor": "eyJpZCI6Mn0="
}

Флаги, без которых не обойтись

  • -i — показать заголовки ответа. Полезно при дебаге X-Request-Id, X-RateLimit-*.
  • -v — verbose, видно весь обмен. В примерах не показываю, но в FAQ упоминаю как способ дебага.
  • -s — silent, убирает прогресс-бар. Незаменим в скриптах, в примерах необязателен.
  • -S — show errors даже в silent. Используется вместе с -s в скриптах: -sS.
  • -f — fail на HTTP-ошибках, curl возвращает ненулевой exit code. Для CI-скриптов в доке полезно.
  • -o file.json — записать ответ в файл.
  • -w '%{http_code}\n' — вывести HTTP-статус отдельно.

В простых примерах не нужны все. Покажи минимум для понимания, остальное — в отдельной странице «Troubleshooting».

Версии curl и совместимость

Современный curl 8.x поддерживает HTTP/2, HTTP/3, мощные опции для timeouts. Но в примерах ориентируйся на 7.x — он стоит на старых системах, в CI-образах, в дистрибутивах с длинной поддержкой.

Что точно работает в 7.x: -X, -H, -d, --data-urlencode, -G, -i, -o, -w, -f. Что не использовать в примерах: --json (появился в 7.82), сложные форматы -w '%{json}' (тоже свежий).

Если используешь свежие фичи — оговори это:

# Требует curl 7.82+
curl --json '{"key": "value"}' https://api.example.com/items

Windows: альтернативы и подводные камни

Windows-пользователи теперь с нативным curl, но с особенностями. Curl в PowerShell — это alias на Invoke-WebRequest в старых версиях, а не настоящий curl. В PowerShell 7+ alias убрали, но если читатель на старом — он копирует пример и получает странную ошибку.

Решения:

  • Упомянуть в FAQ: «На Windows используйте curl.exe явно, чтобы обойти alias».
  • Дать альтернативу для PowerShell для самых частых запросов.
  • Не использовать backslash-продолжение строк, оно работает по-разному. Пример в одну длинную строку всегда работает.

Я обычно даю «one-liner» вариант рядом с многострочным:

curl -X POST https://api.example.com/v1/users -H 'Content-Type: application/json' -H 'Authorization: Bearer YOUR_TOKEN' -d '{"email":"alice@example.com","name":"Alice"}'

Тестирование примеров в CI

Самая частая боль через год после релиза — примеры устарели. URL поменялся, поле переименовали, токен-схема другая. Без CI-проверки пример становится дезинформацией.

Простой подход: в репозитории доков лежат .bash-скрипты с примерами. CI запускает их против тестового API и проверяет, что HTTP-статус ожидаемый. Можно через bats, можно простым shell-скриптом.

#!/usr/bin/env bash
set -euo pipefail

response=$(curl -sS -o /tmp/response.json -w '%{http_code}' \
  -X POST "$API_URL/v1/users" \
  -H 'Content-Type: application/json' \
  -H "Authorization: Bearer $TEST_TOKEN" \
  -d '{"email":"test@example.com","name":"Test"}')

if [ "$response" != "201" ]; then
  echo "Expected 201, got $response"
  cat /tmp/response.json
  exit 1
fi

echo "OK"

Полное соответствие документации и кода вытащить сложно, но базовая проверка «команды из доки выполняются» — лучше, чем полностью «на честном слове».

Многоязычные примеры

Curl — стандарт, но не для каждого читателя. Хорошая практика — показать тот же запрос на двух-трёх языках через табы:

import requests

response = requests.post(
    'https://api.example.com/v1/users',
    headers={'Authorization': f'Bearer {API_TOKEN}'},
    json={'email': 'alice@example.com', 'name': 'Alice'},
)
print(response.json())
const response = await fetch('https://api.example.com/v1/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${apiToken}`,
  },
  body: JSON.stringify({
    email: 'alice@example.com',
    name: 'Alice',
  }),
});
const data = await response.json();

Не давай примеры на 8 языках — это шум. Curl + Python + TypeScript покрывают 80% аудитории. Остальное — через сгенерированный SDK.

Чек-лист curl-примера в доке

  • Метод указан явно через -X.
  • URL полный, с https и версией API.
  • Одинарные кавычки снаружи, двойные внутри JSON.
  • Все нужные заголовки указаны (Content-Type, Authorization, кастомные).
  • Плейсхолдеры в верхнем регистре (YOUR_TOKEN, не your-token).
  • JSON-тело отформатировано, не в одну строку.
  • Если есть параметры с пробелами — URL-encoded или через --data-urlencode.
  • Показан пример ответа, обрезанный если большой.
  • Пример работает после копирования в bash без правок (кроме плейсхолдеров).
  • В CI есть смоук-тест, который запускает пример раз в день/при релизе.

Curl-примеры — это первая точка контакта с API. Если они плохие, дальше документацию никто не читает: уже сложилось впечатление, что «у них всё кривое». Если хорошие — на их основе пишется первая интеграция, и доверие к остальной документации растёт. Времени на качественный пример уходит немного, окупается каждый раз, когда новый разработчик копирует и запускает без вопросов.

Комментарии 0

  • Будьте первым, кто оставит комментарий.

Войдите, чтобы оставить комментарий.