Trabalhando com APIs REST diretamente do PowerShell
1. Fundamentos do Invoke-RestMethod e Invoke-WebRequest
O PowerShell oferece dois cmdlets principais para interagir com APIs REST: Invoke-RestMethod e Invoke-WebRequest. Ambos realizam requisições HTTP, mas diferem no tratamento da resposta.
Invoke-RestMethod automaticamente desserializa respostas JSON ou XML em objetos PowerShell. Invoke-WebRequest retorna um objeto BasicHtmlWebResponseObject com propriedades como Content, StatusCode e Headers.
# Invoke-RestMethod - retorna objetos diretamente
$resultado = Invoke-RestMethod -Uri "https://api.github.com/users/octocat"
$resultado.login
# Invoke-WebRequest - retorna resposta bruta
$resposta = Invoke-WebRequest -Uri "https://api.github.com/users/octocat"
$resposta.Content
$resposta.StatusCode
Parâmetros essenciais comuns a ambos:
- -Uri: URL do endpoint
- -Method: GET, POST, PUT, PATCH, DELETE
- -Headers: hashtable com cabeçalhos HTTP
- -Body: conteúdo da requisição (para POST/PUT)
2. Autenticação em APIs REST
Autenticação via Bearer Token
$headers = @{
"Authorization" = "Bearer seu_token_aqui"
"Content-Type" = "application/json"
}
$resposta = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/dados" `
-Headers $headers
Autenticação Básica (Basic Auth)
$usuario = "admin"
$senha = "minha_senha"
$credenciais = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${usuario}:${senha}"))
$headers = @{
"Authorization" = "Basic $credenciais"
}
Invoke-RestMethod -Uri "https://api.exemplo.com/v1/login" -Headers $headers
Gerenciamento de Tokens Expirados
$token = $null
$tokenExpiracao = Get-Date
function Get-Token {
if ($token -eq $null -or (Get-Date) -ge $tokenExpiracao) {
$body = @{
grant_type = "client_credentials"
client_id = "seu_id"
client_secret = "seu_secret"
}
$resposta = Invoke-RestMethod -Uri "https://auth.exemplo.com/token" `
-Method Post -Body $body
$token = $resposta.access_token
$tokenExpiracao = (Get-Date).AddSeconds($resposta.expires_in - 60)
}
return $token
}
3. Métodos HTTP e Operações CRUD
GET - Consultando dados
# Listar usuários
$usuarios = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/usuarios"
$usuarios | Select-Object id, nome, email
POST - Criando recursos
$novoUsuario = @{
nome = "João Silva"
email = "joao@exemplo.com"
cargo = "Desenvolvedor"
} | ConvertTo-Json
$criado = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/usuarios" `
-Method Post `
-Body $novoUsuario `
-ContentType "application/json"
PUT/PATCH - Atualizando recursos
$atualizacao = @{
cargo = "Desenvolvedor Sênior"
salario = 15000
} | ConvertTo-Json
Invoke-RestMethod -Uri "https://api.exemplo.com/v1/usuarios/123" `
-Method Patch `
-Body $atualizacao `
-ContentType "application/json"
DELETE - Removendo recursos
Invoke-RestMethod -Uri "https://api.exemplo.com/v1/usuarios/123" `
-Method Delete
4. Manipulação de Parâmetros e Query Strings
Construção dinâmica de URIs
function Get-Usuarios {
param(
[string]$Nome,
[int]$Limite = 10,
[int]$Pagina = 1
)
$parametros = @()
if ($Nome) { $parametros += "nome=$([System.Uri]::EscapeDataString($Nome))" }
if ($Limite) { $parametros += "limite=$Limite" }
if ($Pagina) { $parametros += "pagina=$Pagina" }
$queryString = $parametros -join "&"
$uri = "https://api.exemplo.com/v1/usuarios?$queryString"
Invoke-RestMethod -Uri $uri
}
Uso de hashtable para parâmetros
$parametros = @{
"filtro" = "ativo"
"ordem" = "nome_asc"
"campos" = "id,nome,email"
}
$uri = "https://api.exemplo.com/v1/usuarios"
$queryParams = $parametros.GetEnumerator() | ForEach-Object {
"$($_.Key)=$([System.Uri]::EscapeDataString($_.Value))"
}
$uriCompleta = "$uri?$($queryParams -join '&')"
Invoke-RestMethod -Uri $uriCompleta
Tratamento de paginação
function Get-TodosUsuarios {
$todos = @()
$pagina = 1
$limitePorPagina = 100
do {
$uri = "https://api.exemplo.com/v1/usuarios?pagina=$pagina&limite=$limitePorPagina"
$resultado = Invoke-RestMethod -Uri $uri
$todos += $resultado.dados
$pagina++
} while ($resultado.dados.Count -eq $limitePorPagina)
return $todos
}
5. Tratamento de Erros e Respostas HTTP
Verificação de status codes
try {
$resposta = Invoke-WebRequest -Uri "https://api.exemplo.com/v1/usuarios/9999" `
-ErrorAction Stop
switch ($resposta.StatusCode) {
200 { Write-Host "Sucesso" -ForegroundColor Green }
201 { Write-Host "Recurso criado" -ForegroundColor Green }
204 { Write-Host "Sem conteúdo" -ForegroundColor Yellow }
}
}
catch {
$statusCode = $_.Exception.Response.StatusCode.value__
switch ($statusCode) {
400 { Write-Warning "Requisição inválida: $($_.Exception.Message)" }
401 { Write-Error "Não autorizado - verifique suas credenciais" }
403 { Write-Error "Acesso proibido" }
404 { Write-Warning "Recurso não encontrado" }
429 { Write-Warning "Limite de requisições excedido" }
500 { Write-Error "Erro interno do servidor" }
default { Write-Error "Erro HTTP $statusCode: $($_.Exception.Message)" }
}
}
Retry automático com política de espera
function Invoke-ComRetry {
param(
[string]$Uri,
[int]$MaxTentativas = 3,
[int]$EsperaBase = 2
)
$tentativa = 0
do {
$tentativa++
try {
return Invoke-RestMethod -Uri $Uri -ErrorAction Stop
}
catch {
if ($tentativa -ge $MaxTentativas) {
throw
}
$statusCode = $_.Exception.Response.StatusCode.value__
if ($statusCode -eq 429 -or $statusCode -ge 500) {
$espera = $EsperaBase * $tentativa
Write-Warning "Tentativa $tentativa falhou. Aguardando ${espera}s..."
Start-Sleep -Seconds $espera
}
else {
throw
}
}
} while ($tentativa -lt $MaxTentativas)
}
6. Serialização e Desserialização de Dados
Conversão de objetos para JSON
$dados = @(
@{
id = 1
nome = "Produto A"
preco = 99.90
categorias = @("Eletrônicos", "Promoção")
},
@{
id = 2
nome = "Produto B"
preco = 149.90
categorias = @("Casa", "Cozinha")
}
)
# ConvertTo-Json com profundidade personalizada
$json = $dados | ConvertTo-Json -Depth 5
Write-Host $json
# Compressão para reduzir tamanho
$jsonCompacto = $dados | ConvertTo-Json -Depth 5 -Compress
Trabalhando com estruturas aninhadas
# Acessar propriedades aninhadas
$resposta = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/pedidos/100"
$resposta.cliente.nome
$resposta.itens | ForEach-Object {
"$($_.produto.nome) - Quantidade: $($_.quantidade) - Preço: $($_.preco)"
}
# Filtrar dados complexos
$resposta.itens | Where-Object { $_.quantidade -gt 2 } |
Select-Object @{Name="Produto";Expression={$_.produto.nome}}, quantidade, preco
7. Automação de Fluxos com APIs
Encadeamento de chamadas sequenciais
function New-PedidoCompleto {
param(
[int]$ClienteId,
[array]$Itens
)
# 1. Validar cliente
$cliente = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/clientes/$ClienteId"
if (-not $cliente.ativo) {
throw "Cliente inativo"
}
# 2. Verificar estoque
foreach ($item in $Itens) {
$estoque = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/produtos/$($item.produtoId)/estoque"
if ($estoque.disponivel -lt $item.quantidade) {
throw "Estoque insuficiente para produto $($item.produtoId)"
}
}
# 3. Criar pedido
$pedidoBody = @{
clienteId = $ClienteId
itens = $Itens
data = (Get-Date -Format "yyyy-MM-dd")
} | ConvertTo-Json -Depth 3
$pedido = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/pedidos" `
-Method Post -Body $pedidoBody -ContentType "application/json"
# 4. Processar pagamento
$pagamentoBody = @{
pedidoId = $pedido.id
valor = $pedido.total
metodo = "cartao_credito"
} | ConvertTo-Json
$pagamento = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/pagamentos" `
-Method Post -Body $pagamentoBody -ContentType "application/json"
return @{
Pedido = $pedido
Pagamento = $pagamento
}
}
Processamento em lote com loops
$idsProdutos = 1..100
$resultados = @()
foreach ($id in $idsProdutos) {
try {
$produto = Invoke-RestMethod -Uri "https://api.exemplo.com/v1/produtos/$id"
$resultados += $produto
Write-Progress -Activity "Processando produtos" `
-Status "Produto $id" `
-PercentComplete (($id / $idsProdutos.Count) * 100)
}
catch {
Write-Warning "Erro ao processar produto $id"
}
}
Criação de funções reutilizáveis
function Invoke-MinhaAPI {
param(
[Parameter(Mandatory)]
[string]$Endpoint,
[ValidateSet("GET","POST","PUT","PATCH","DELETE")]
[string]$Method = "GET",
[object]$Body,
[hashtable]$QueryParams
)
$headers = @{
"Authorization" = "Bearer $(Get-MeuToken)"
"Content-Type" = "application/json"
"Accept" = "application/json"
}
$uri = "https://api.exemplo.com/v1$Endpoint"
if ($QueryParams) {
$queryParts = $QueryParams.GetEnumerator() | ForEach-Object {
"$($_.Key)=$([System.Uri]::EscapeDataString($_.Value))"
}
$uri += "?$($queryParts -join '&')"
}
$params = @{
Uri = $uri
Method = $Method
Headers = $headers
}
if ($Body) {
$params.Body = $Body | ConvertTo-Json -Depth 5 -Compress
}
return Invoke-RestMethod @params
}
# Exemplo de uso
$usuarios = Invoke-MinhaAPI -Endpoint "/usuarios" -QueryParams @{limite=50;ativo=true}
$novoUsuario = Invoke-MinhaAPI -Endpoint "/usuarios" -Method POST -Body @{nome="Maria";email="maria@exemplo.com"}
Referências
- Documentação oficial do Invoke-RestMethod (Microsoft) — Documentação completa do cmdlet Invoke-RestMethod com exemplos e parâmetros
- Trabalhando com APIs REST no PowerShell (Microsoft Learn) — Guia oficial da Microsoft sobre consumo de APIs REST no PowerShell
- PowerShell e APIs REST: Guia Completo (Dev.to) — Artigo técnico detalhado sobre autenticação, paginação e tratamento de erros
- Autenticação em APIs com PowerShell (SS64.com) — Tutorial sobre diferentes métodos de autenticação em APIs usando PowerShell
- ConvertTo-Json e manipulação de dados JSON (PowerShell.org) — Guia prático sobre serialização e desserialização de JSON no PowerShell
- Políticas de retry e tratamento de erros (Stack Overflow) — Discussão técnica sobre implementação de retry automático em chamadas de API
- PowerShell Gallery: Módulo PSFramework para APIs — Módulo avançado para construção de funções reutilizáveis de API com logging e tratamento de erros