Interagindo com APIs REST a partir do Bash

1. Fundamentos: Ferramentas Essenciais para Requisições HTTP

O ecossistema Bash oferece duas ferramentas principais para comunicação HTTP: curl e wget. O curl é a escolha predominante para APIs REST devido ao seu suporte nativo a métodos HTTP, cabeçalhos customizados e tratamento granular de respostas.

# Sintaxe básica do curl
curl -X GET https://api.exemplo.com/usuarios

# Com opções comuns
curl -s -X POST https://api.exemplo.com/dados \
  -H "Content-Type: application/json" \
  -d '{"nome": "Maria"}'

# Salvando resposta em arquivo e extraindo tempo de resposta
curl -s -o resposta.json -w "Tempo: %{time_total}s\n" https://api.exemplo.com

O wget é mais adequado para downloads simples, mas não oferece controle fino sobre cabeçalhos e métodos. Antes de chamar APIs, teste conectividade com:

# Teste de conectividade
ping -c 1 api.exemplo.com > /dev/null 2>&1 && echo "OK" || echo "Falha"
nc -zv api.exemplo.com 443 2>&1 | grep -q "succeeded" && echo "Porta 443 aberta"

2. Estruturando Requisições: Cabeçalhos, Métodos e Corpo

Cada método HTTP tem finalidade específica. O curl utiliza a opção -X para especificar o método:

# GET - Consulta
curl -s https://api.exemplo.com/usuarios/123

# POST - Criação
curl -s -X POST https://api.exemplo.com/usuarios \
  -H "Content-Type: application/json" \
  -d '{"nome": "João", "email": "joao@email.com"}'

# PUT - Atualização completa
curl -s -X PUT https://api.exemplo.com/usuarios/123 \
  -H "Content-Type: application/json" \
  -d '{"nome": "João Silva", "email": "joao.silva@email.com"}'

# DELETE - Remoção
curl -s -X DELETE https://api.exemplo.com/usuarios/123

Cabeçalhos customizados são essenciais para autenticação e formatação:

# Autenticação Bearer
curl -s -H "Authorization: Bearer TOKEN_AQUI" https://api.exemplo.com/protegido

# Múltiplos cabeçalhos
curl -s -H "Content-Type: application/json" -H "Accept: application/json" \
  -H "X-API-Key: chave123" https://api.exemplo.com/dados

Para envio de dados complexos:

# JSON no corpo
curl -s -X POST -H "Content-Type: application/json" \
  -d '{"produto": "notebook", "quantidade": 2}' \
  https://api.exemplo.com/pedidos

# Dados de formulário
curl -s -X POST --data-urlencode "nome=João" \
  --data-urlencode "idade=30" \
  https://api.exemplo.com/cadastro

# Upload de arquivo
curl -s -X POST -F "arquivo=@/home/usuario/documento.pdf" \
  https://api.exemplo.com/upload

3. Autenticação e Tokens: Lidando com Segurança

Diferentes APIs exigem diferentes estratégias de autenticação. Veja as abordagens mais comuns:

# API Key via cabeçalho
API_KEY="sua_chave_secreta"
curl -s -H "X-API-Key: $API_KEY" https://api.exemplo.com/recurso

# API Key via query string (menos seguro)
curl -s "https://api.exemplo.com/recurso?api_key=$API_KEY"

# Autenticação Basic
curl -s --user "usuario:senha" https://api.exemplo.com/basico

# Codificação base64 manual
CREDENCIAIS=$(echo -n "usuario:senha" | base64)
curl -s -H "Authorization: Basic $CREDENCIAIS" https://api.exemplo.com/basico

Para tokens JWT, o fluxo típico envolve obter, armazenar e renovar:

# Obtenção do token
TOKEN=$(curl -s -X POST https://api.exemplo.com/auth \
  -H "Content-Type: application/json" \
  -d '{"usuario": "admin", "senha": "123456"}' | jq -r '.token')

# Uso do token em requisições subsequentes
curl -s -H "Authorization: Bearer $TOKEN" https://api.exemplo.com/dados

# Renovação automática (exemplo simplificado)
if curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $TOKEN" \
  https://api.exemplo.com/verificar | grep -q "401"; then
  echo "Token expirado. Renovando..."
  TOKEN=$(curl -s -X POST https://api.exemplo.com/refresh \
    -H "Authorization: Bearer $TOKEN" | jq -r '.token')
fi

4. Processamento de Respostas: JSON com jq e jo

O jq é indispensável para processar JSON no terminal. Com ele, você extrai campos específicos e formata saídas:

# Extrair campo específico
curl -s https://api.exemplo.com/usuarios/1 | jq '.nome'

# Selecionar múltiplos campos
curl -s https://api.exemplo.com/usuarios | jq '.[] | {nome, email}'

# Filtros condicionais
curl -s https://api.exemplo.com/usuarios | jq '.[] | select(.ativo == true)'

# Formatação personalizada
curl -s https://api.exemplo.com/usuarios | jq -r '.[] | "\(.id): \(.nome)"'

Para construir JSON dinamicamente, use jo ou printf:

# Construindo JSON com jo
DADOS=$(jo nome="Ana" idade=28 ativo=true)
curl -s -X POST -H "Content-Type: application/json" -d "$DADOS" \
  https://api.exemplo.com/usuarios

# Alternativa com printf e jq
JSON=$(printf '{"nome": "%s", "idade": %d}' "Carlos" 35)
curl -s -X POST -H "Content-Type: application/json" -d "$JSON" \
  https://api.exemplo.com/usuarios

Tratamento de erros com verificação de código HTTP:

HTTP_CODE=$(curl -s -o resposta.json -w "%{http_code}" https://api.exemplo.com/dados)
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
  echo "Sucesso! Dados salvos em resposta.json"
  cat resposta.json | jq '.'
else
  echo "Erro $HTTP_CODE"
  cat resposta.json | jq '.mensagem // "Sem detalhes"'
fi

5. Paginação e Iteração: Coletando Dados em Lotes

APIs frequentemente paginam resultados. Identifique o padrão e itere até esgotar:

# Paginação por página (GitHub Style)
PAGE=1
while true; do
  RESPOSTA=$(curl -s "https://api.exemplo.com/itens?page=$PAGE&per_page=100")
  QUANTIDADE=$(echo "$RESPOSTA" | jq '. | length')

  [ "$QUANTIDADE" -eq 0 ] && break

  echo "$RESPOSTA" | jq -c '.[]' >> todos_itens.json
  echo "Página $PAGE: $QUANTIDADE itens"
  ((PAGE++))
done

# Paginação por cursor
CURSOR=""
while true; do
  RESPOSTA=$(curl -s "https://api.exemplo.com/itens?cursor=$CURSOR")
  echo "$RESPOSTA" | jq -c '.dados[]' >> todos_itens.json
  CURSOR=$(echo "$RESPOSTA" | jq -r '.proximo_cursor // empty')
  [ -z "$CURSOR" ] && break
  sleep 0.5  # Controle de taxa
done

Respeite limites de taxa com --retry e sleeps:

# Requisição com retry automático
curl -s --retry 3 --retry-delay 5 --retry-max-time 30 \
  https://api.exemplo.com/recurso

# Controle manual de taxa
for i in {1..10}; do
  curl -s "https://api.exemplo.com/pagina/$i" >> resultados.json
  sleep 2  # 2 segundos entre requisições
done

6. Scripts Modulares: Funções e Reutilização

Organize seu código em funções reutilizáveis para cada endpoint:

#!/bin/bash

# Configurações globais
API_BASE="https://api.exemplo.com/v1"
TOKEN="seu_token_aqui"
TIMEOUT=10

# Função genérica para requisições
api_request() {
  local method=$1
  local endpoint=$2
  local data=$3

  curl -s -X "$method" "$API_BASE$endpoint" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    ${data:+-d "$data"} \
    --max-time "$TIMEOUT"
}

# Funções específicas
get_users() {
  api_request "GET" "/usuarios"
}

post_user() {
  local nome=$1
  local email=$2
  local data=$(jo nome="$nome" email="$email")
  api_request "POST" "/usuarios" "$data"
}

delete_user() {
  local id=$1
  api_request "DELETE" "/usuarios/$id"
}

# Uso
get_users | jq '.'
post_user "Maria" "maria@email.com"
delete_user 123

Para criar uma biblioteca pessoal, salve as funções em um arquivo separado:

# api_helper.sh
source api_helper.sh
get_users

7. Tratamento de Erros e Logging Robusto

Implemente verificações de erro e logging estruturado:

#!/bin/bash

LOG_DIR="./logs"
mkdir -p "$LOG_DIR"

log_request() {
  local method=$1
  local url=$2
  local status=$3
  local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
  echo "[$timestamp] $method $url -> $status" >> "$LOG_DIR/api.log"
}

api_call() {
  local method=$1
  local url=$2
  local data=$3
  local output=$(mktemp)

  # Executa requisição
  http_code=$(curl -s -o "$output" -w "%{http_code}" \
    -X "$method" "$url" \
    ${data:+-d "$data"} \
    -H "Content-Type: application/json")

  log_request "$method" "$url" "$http_code"

  # Tratamento de erro
  if [ "$http_code" -ge 400 ]; then
    echo "Erro $http_code:" >&2
    cat "$output" | jq '.' >&2
    rm "$output"
    return 1
  fi

  cat "$output"
  rm "$output"
}

# Estratégia de retry com backoff exponencial
retry_api_call() {
  local max_retries=5
  local attempt=1
  local wait=2

  while [ $attempt -le $max_retries ]; do
    if api_call "$@"; then
      return 0
    fi
    echo "Tentativa $attempt falhou. Aguardando ${wait}s..." >&2
    sleep $wait
    wait=$((wait * 2))
    ((attempt++))
  done

  echo "Falha após $max_retries tentativas" >&2
  return 1
}

8. Casos Práticos: Automação de Tarefas Reais

Monitoramento de status de serviço com GitHub API:

#!/bin/bash

check_github_status() {
  local status=$(curl -s https://www.githubstatus.com/api/v2/status.json | \
    jq -r '.status.indicator')

  case "$status" in
    "none") echo "✅ GitHub operacional" ;;
    "minor") echo "⚠️ GitHub com problemas menores" ;;
    "major") echo "🔴 GitHub com problemas graves" ;;
    *) echo "❌ Não foi possível verificar status" ;;
  esac
}

check_github_status

Envio de notificação para Slack via webhook:

#!/bin/bash

WEBHOOK_URL="https://hooks.slack.com/services/T123/B456/SEU_TOKEN"

send_slack_notification() {
  local message=$1
  local channel=${2:-#geral}

  local payload=$(jo text="$message" channel="$channel")

  curl -s -X POST "$WEBHOOK_URL" \
    -H "Content-Type: application/json" \
    -d "$payload" > /dev/null

  echo "Notificação enviada para $channel"
}

send_slack_notification "🚀 Deploy realizado com sucesso!" "#deploy"

Backup de dados de API para CSV:

#!/bin/bash

API_URL="https://api.exemplo.com/usuarios"
CSV_FILE="backup_usuarios_$(date +%Y%m%d).csv"

echo "ID,Nome,Email,DataCadastro" > "$CSV_FILE"

curl -s "$API_URL" | jq -r '.[] | [.id, .nome, .email, .data_cadastro] | @csv' \
  >> "$CSV_FILE"

echo "Backup concluído: $(wc -l < "$CSV_FILE") registros em $CSV_FILE"

Referências