- Data Hackers Newsletter
- Posts
- REST APIs em Python: guia completo para consumir e criar web services
REST APIs em Python: guia completo para consumir e criar web services
Conheça as principais ferramentas de Python para consumir dados e construir APIs usando a arquitetura REST
Existe uma quantidade impressionante de dados disponíveis na Web. Muitos web services, como YouTube e GitHub, disponibilizam seus dados para aplicações de terceiros através de uma API (Application Programming Interface). Uma das formas mais populares de construir APIs é utilizando o estilo de arquitetura REST. Python oferece ferramentas excelentes não apenas para consumir dados de REST APIs, mas também para construir suas próprias APIs.
Neste guia completo, você vai aprender:
O que é arquitetura REST
Como REST APIs fornecem acesso a dados web
Como consumir dados de REST APIs usando a biblioteca requests
Quais passos seguir para construir uma REST API
Quais são as principais ferramentas Python para criar REST APIs
Ao usar Python e REST APIs, você pode recuperar, processar, atualizar e manipular dados fornecidos por qualquer serviço web de seu interesse.
O que é arquitetura REST
REST significa Representational State Transfer (transferência de estado representacional) e é um estilo de arquitetura de software que define um padrão para comunicações entre cliente e servidor através de uma rede. REST fornece um conjunto de restrições para arquitetura de software visando promover performance, escalabilidade, simplicidade e confiabilidade no sistema.
REST define as seguintes restrições arquiteturais:
Stateless (sem estado): O servidor não mantém nenhum estado entre requisições do cliente
Cliente-servidor: Cliente e servidor devem ser desacoplados um do outro, permitindo que cada um se desenvolva independentemente
Cacheável: Os dados recuperados do servidor devem ser cacheáveis pelo cliente ou pelo servidor
Interface uniforme: O servidor fornecerá uma interface uniforme para acessar recursos sem definir sua representação
Sistema em camadas: O cliente pode acessar os recursos no servidor indiretamente através de outras camadas, como proxy ou load balancer
Código sob demanda (opcional): O servidor pode transferir código para o cliente executar, como JavaScript para uma aplicação de página única
É importante notar que REST não é uma especificação, mas um conjunto de diretrizes sobre como arquitetar um sistema de software conectado em rede.
REST APIs e web services
Um REST web service é qualquer serviço web que adere às restrições da arquitetura REST. Esses serviços web expõem seus dados para o mundo externo através de uma API. REST APIs fornecem acesso aos dados do serviço web através de URLs públicas.
Por exemplo, aqui está uma das URLs da REST API do GitHub:
https://api.github.com/users/<username>
Esta URL permite acessar informações sobre um usuário específico do GitHub. Você acessa dados de uma REST API enviando uma requisição HTTP para uma URL específica e processando a resposta.
Métodos HTTP
REST APIs escutam métodos HTTP como GET, POST e DELETE para saber quais operações executar nos recursos do serviço web. Um recurso é qualquer dado disponível no serviço web que pode ser acessado e manipulado com requisições HTTP para a REST API. O método HTTP informa à API qual ação executar no recurso.
Embora existam muitos métodos HTTP, os cinco métodos listados abaixo são os mais comumente usados com REST APIs:
Método HTTP | Descrição |
|---|---|
GET | Recuperar um recurso existente |
POST | Criar um novo recurso |
PUT | Atualizar um recurso existente |
PATCH | Atualizar parcialmente um recurso existente |
DELETE | Deletar um recurso |
Uma aplicação cliente REST API pode usar esses cinco métodos HTTP para gerenciar o estado dos recursos no serviço web.
Status codes
Assim que uma REST API recebe e processa uma requisição HTTP, ela retornará uma resposta HTTP. Incluído nesta resposta está um código de status HTTP. Este código fornece informações sobre os resultados da requisição. Uma aplicação que envia requisições para a API pode verificar o código de status e executar ações baseadas no resultado.
Abaixo está uma lista dos códigos de status mais comuns retornados por REST APIs:
Código | Significado | Descrição |
|---|---|---|
200 | OK | A ação solicitada foi bem-sucedida |
201 | Created | Um novo recurso foi criado |
202 | Accepted | A requisição foi recebida, mas nenhuma modificação foi feita ainda |
204 | No Content | A requisição foi bem-sucedida, mas a resposta não tem conteúdo |
400 | Bad Request | A requisição estava malformada |
401 | Unauthorized | O cliente não está autorizado a executar a ação solicitada |
404 | Not Found | O recurso solicitado não foi encontrado |
415 | Unsupported Media Type | O formato dos dados da requisição não é suportado pelo servidor |
422 | Unprocessable Entity | Os dados da requisição estavam formatados corretamente mas continham dados inválidos |
500 | Internal Server Error | O servidor lançou um erro ao processar a requisição |
Os códigos de status são numerados baseados na categoria do resultado:
Faixa de código | Categoria |
|---|---|
2xx | Operação bem-sucedida |
3xx | Redirecionamento |
4xx | Erro do cliente |
5xx | Erro do servidor |
Os códigos de status HTTP são muito úteis ao trabalhar com REST APIs, pois você frequentemente precisará executar lógicas diferentes baseadas nos resultados da requisição.
API endpoints
Uma REST API expõe um conjunto de URLs públicas que aplicações cliente usam para acessar os recursos de um serviço web. Essas URLs, no contexto de uma API, são chamadas de endpoints.
Para ajudar a clarificar isso, veja a tabela abaixo com endpoints de API para um sistema hipotético de CRM. Esses endpoints são para um recurso de clientes (customers) que representa potenciais clientes no sistema:
Método HTTP | Endpoint da API | Descrição |
|---|---|---|
GET | /customers | Obter uma lista de clientes |
GET | /customers/<customer_id> | Obter um único cliente |
POST | /customers | Criar um novo cliente |
PUT | /customers/<customer_id> | Atualizar um cliente |
PATCH | /customers/<customer_id> | Atualizar parcialmente um cliente |
DELETE | /customers/<customer_id> | Deletar um cliente |
Cada um dos endpoints acima executa uma ação diferente baseada no método HTTP.
REST e Python: consumindo APIs
Para escrever código que interage com REST APIs, a maioria dos desenvolvedores Python recorre à biblioteca requests para enviar requisições HTTP. Esta biblioteca abstrai as complexidades de fazer requisições HTTP e é um dos poucos projetos que vale a pena tratar como se fosse parte da biblioteca padrão.
Para começar a usar requests, você precisa instalá-la primeiro:
$ python -m pip install requests
Agora você pode começar a enviar requisições HTTP.
GET
GET é um dos métodos HTTP mais comuns que você usará ao trabalhar com REST APIs. Este método permite recuperar recursos de uma API. GET é uma operação somente leitura, então você não deve usá-lo para modificar um recurso existente.
Para testar GET e os outros métodos desta seção, usaremos um serviço chamado JSONPlaceholder. Este serviço gratuito fornece endpoints de API falsos que enviam respostas que o requests pode processar.
Abra o REPL do Python e execute os seguintes comandos para enviar uma requisição GET para um endpoint do JSONPlaceholder:
>>> import requests
>>> api_url = "https://jsonplaceholder.typicode.com/todos/1"
>>> response = requests.get(api_url)
>>> response.json()
{'userId': 1, 'id': 1, 'title': 'delectus aut autem', 'completed': False}
Este código chama requests.get() para enviar uma requisição GET para /todos/1, que responde com o item todo com ID 1. Então você pode chamar .json() no objeto response para visualizar os dados que vieram da API.
Os dados da resposta são formatados como JSON, um formato chave-valor similar a um dicionário Python. É um formato de dados muito popular e o formato de troca de facto para a maioria das REST APIs.
Além de visualizar os dados JSON da API, você também pode ver outras informações sobre a response:
>>> response.status_code
200
>>> response.headers["Content-Type"]
'application/json; charset=utf-8'
Aqui, você acessa response.status_code para ver o código de status HTTP. Você também pode visualizar os headers HTTP da resposta com response.headers.
POST
Agora veja como usar requests para enviar dados via POST para uma REST API e criar um novo recurso. Usaremos o JSONPlaceholder novamente, mas desta vez incluiremos dados JSON na requisição:
>>> import requests
>>> api_url = "https://jsonplaceholder.typicode.com/todos"
>>> todo = {"userId": 1, "title": "Buy milk", "completed": False}
>>> response = requests.post(api_url, json=todo)
>>> response.json()
{'userId': 1, 'title': 'Buy milk', 'completed': False, 'id': 201}
>>> response.status_code
201
Aqui, você chama requests.post() para criar um novo todo no sistema. Quando você passa o dicionário para o argumento json, requests.post() automaticamente define o header HTTP Content-Type da requisição para application/json e serializa todo em uma string JSON, que é anexada ao corpo da requisição.
PUT
Além de GET e POST, requests fornece suporte para todos os outros métodos HTTP que você usaria com uma REST API. O código abaixo envia uma requisição PUT para atualizar um todo existente com novos dados:
>>> import requests
>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"
>>> response = requests.get(api_url)
>>> response.json()
{'userId': 1, 'id': 10, 'title': 'illo est ... aut', 'completed': True}
>>> todo = {"userId": 1, "title": "Wash car", "completed": True}
>>> response = requests.put(api_url, json=todo)
>>> response.json()
{'userId': 1, 'title': 'Wash car', 'completed': True, 'id': 10}
>>> response.status_code
200
Primeiro você chama requests.get() para visualizar o conteúdo do todo existente. Em seguida, chama requests.put() com novos dados JSON para substituir os valores existentes. Requisições PUT bem-sucedidas sempre retornarão 200 em vez de 201 porque você não está criando um novo recurso, apenas atualizando um existente.
PATCH
A seguir, você usará requests.patch() para modificar o valor de um campo específico em um todo existente. PATCH difere de PUT porque não substitui completamente o recurso existente. Ele modifica apenas os valores definidos no JSON enviado com a requisição:
>>> import requests
>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"
>>> todo = {"title": "Mow lawn"}
>>> response = requests.patch(api_url, json=todo)
>>> response.json()
{'userId': 1, 'id': 10, 'title': 'Mow lawn', 'completed': True}
>>> response.status_code
200
Quando você chama response.json(), pode ver que title foi atualizado para Mow lawn.
DELETE
Por último, se você quiser remover completamente um recurso, use DELETE:
>>> import requests
>>> api_url = "https://jsonplaceholder.typicode.com/todos/10"
>>> response = requests.delete(api_url)
>>> response.json()
{}
>>> response.status_code
200
Você chama requests.delete() com uma URL da API que contém o ID do todo que você gostaria de remover. Isso envia uma requisição DELETE para a REST API, que então remove o recurso correspondente. Após deletar o recurso, a API envia de volta um objeto JSON vazio indicando que o recurso foi deletado.
A biblioteca requests é uma ferramenta incrível para trabalhar com REST APIs e uma parte indispensável do seu conjunto de ferramentas Python.
REST e Python: construindo APIs
O design de REST API é um tópico enorme com muitas camadas. Como a maioria das coisas em tecnologia, há uma ampla gama de opiniões sobre a melhor abordagem para construir APIs. Nesta seção, você verá alguns passos recomendados a seguir ao construir uma API.
Identifique recursos
O primeiro passo ao construir uma REST API é identificar os recursos que a API gerenciará. É comum descrever esses recursos como substantivos plurais, como customers, events ou transactions. Ao identificar diferentes recursos em seu serviço web, você construirá uma lista de substantivos que descrevem os diferentes dados que os usuários podem gerenciar na API.
Ao fazer isso, certifique-se de considerar quaisquer recursos aninhados. Por exemplo, customers podem ter sales, ou events podem conter guests. Estabelecer essas hierarquias de recursos ajudará quando você definir os endpoints da API.
Defina seus endpoints
Uma vez que você identificou os recursos em seu serviço web, você vai querer usar esses recursos para definir os endpoints da API. Aqui estão alguns exemplos de endpoints para um recurso transactions que você pode encontrar em uma API para um serviço de processamento de pagamentos:
Método HTTP | Endpoint da API | Descrição |
|---|---|---|
GET | /transactions | Obter uma lista de transações |
GET | /transactions/<transaction_id> | Obter uma única transação |
POST | /transactions | Criar uma nova transação |
PUT | /transactions/<transaction_id> | Atualizar uma transação |
PATCH | /transactions/<transaction_id> | Atualizar parcialmente uma transação |
DELETE | /transactions/<transaction_id> | Deletar uma transação |
Esses seis endpoints cobrem todas as operações necessárias para criar, ler, atualizar e deletar transactions no serviço web.
Um endpoint não deve conter verbos. Em vez disso, você deve selecionar os métodos HTTP apropriados para transmitir a ação do endpoint. Por exemplo, o endpoint abaixo contém um verbo desnecessário:
GET /getTransactions
Aqui, get está incluído no endpoint quando não é necessário. O método HTTP GET já fornece o significado semântico para o endpoint, indicando a ação. Você pode remover get do endpoint:
GET /transactions
Este endpoint contém apenas um substantivo plural, e o método HTTP GET comunica a ação.
Escolha seu formato de intercâmbio de dados
Duas opções populares para formatar dados de serviços web são XML e JSON. Tradicionalmente, XML era muito popular com APIs SOAP, mas JSON é mais popular com REST APIs.
Aqui está um exemplo de um recurso book formatado como JSON:
{
"title": "Python Basics",
"page_count": 635,
"pub_date": "2021-03-16",
"authors": [
{"name": "David Amos"},
{"name": "Joanna Jablonski"},
{"name": "Dan Bader"},
{"name": "Fletcher Heisler"}
],
"isbn13": "978-1775093329",
"genre": "Education"
}
JSON armazena dados em pares chave-valor similar a um dicionário Python. Como XML, JSON suporta aninhamento de dados em qualquer nível, então você pode modelar dados complexos.
Nem JSON nem XML é inerentemente melhor que o outro, mas há uma preferência por JSON entre desenvolvedores de REST API, especialmente quando você combina uma REST API com um framework front-end como React ou Vue.
Projete respostas de sucesso
Uma vez que você escolheu um formato de dados, o próximo passo é decidir como você responderá às requisições HTTP. Todas as respostas de sua REST API devem ter um formato similar e incluir o código de status HTTP apropriado.
Vamos ver um exemplo de requisição GET para /cars, que retorna uma lista de carros:
GET /cars HTTP/1.1
Host: api.example.com
Agora veja a resposta. Esta API usa JSON como formato de intercâmbio de dados:
HTTP/1.1 200 OK
Content-Type: application/json
[
{
"id": 1,
"make": "GMC",
"model": "1500 Club Coupe",
"year": 1998,
"vin": "1D7RV1GTXAS806941",
"color": "Red"
},
{
"id": 2,
"make": "Lamborghini",
"model":"Gallardo",
"year":2006,
"vin":"JN1BY1PR0FM736887",
"color":"Mauve"
}
]
A API retorna uma resposta que contém uma lista de carros. Você sabe que a resposta foi bem-sucedida por causa do código de status 200 OK. A resposta também tem um header Content-Type definido como application/json, informando ao usuário para processar a resposta como JSON.
Para qualquer requisição GET bem-sucedida, você deve retornar 200 OK. Para requisições POST bem-sucedidas, você deve usar 201 Created:
HTTP/1.1 201 Created
Content-Type: application/json
{
"id": 4,
"make": "Nissan",
"model": "240SX",
"year": 1994,
"vin": "1N6AD0CU5AC961553",
"color": "Violet"
}
Esta resposta tem um código de status 201 Created para informar ao usuário que um novo recurso foi criado.
Projete respostas de erro
Sempre há uma chance de que requisições para sua REST API possam falhar. É uma boa ideia definir como será uma resposta de erro. Essas respostas devem incluir uma descrição do erro que ocorreu junto com o código de status apropriado.
Aqui está um exemplo de uma requisição para um recurso que não existe na API:
GET /motorcycles HTTP/1.1
Host: api.example.com
O usuário envia uma requisição GET para /motorcycles, que não existe. A API envia de volta a seguinte resposta:
HTTP/1.1 404 Not Found
Content-Type: application/json
{
"error": "The requested resource was not found."
}
Esta resposta inclui um código de status 404 Not Found. Junto com isso, a resposta contém um objeto JSON com uma mensagem de erro descritiva.
Aqui está outro exemplo quando o usuário envia uma requisição inválida:
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
"error": "This request was not properly formatted. Please send again."
}
Esta resposta inclui uma mensagem de erro descritiva junto com o código de status 400 Bad Request, informando ao usuário que ele precisa corrigir a requisição.
REST e Python: ferramentas do mercado
Nesta seção, você verá três frameworks populares para construir REST APIs em Python. Cada framework tem prós e contras, então você terá que avaliar qual funciona melhor para suas necessidades.
Flask
Flask é um microframework Python usado para construir aplicações web e REST APIs. Flask fornece uma base sólida para suas aplicações, deixando muitas escolhas de design para você. O principal trabalho do Flask é lidar com requisições HTTP e roteá-las para a função apropriada na aplicação.
Abaixo está um exemplo de aplicação Flask para uma REST API:
# app.py
from flask import Flask, request, jsonify
app = Flask(__name__)
countries = [
{"id": 1, "name": "Thailand", "capital": "Bangkok", "area": 513120},
{"id": 2, "name": "Australia", "capital": "Canberra", "area": 7617930},
{"id": 3, "name": "Egypt", "capital": "Cairo", "area": 1010408},
]
def _find_next_id():
return max(country["id"] for country in countries) + 1
@app.get("/countries")
def get_countries():
return jsonify(countries)
@app.post("/countries")
def add_country():
if request.is_json:
country = request.get_json()
country["id"] = _find_next_id()
countries.append(country)
return country, 201
return {"error": "Request must be JSON"}, 415
Esta aplicação define o endpoint da API /countries para gerenciar a lista de países. Ela lida com dois tipos diferentes de requisições:
GET /countriesretorna a lista de paísesPOST /countriesadiciona um novo país à lista
Você pode testar esta aplicação instalando o Flask:
$ python -m pip install flask
Salve o código em um arquivo chamado app.py e execute:
$ export FLASK_APP=app.py
$ flask run
O servidor estará rodando em http://127.0.0.1:5000/countries.
Django REST framework
Outra opção popular para construir REST APIs é o Django REST framework. Django REST framework é um plugin Django que adiciona funcionalidade de REST API em cima de um projeto Django existente.
Para usar o Django REST framework, você precisa instalar Django e djangorestframework:
$ python -m pip install Django djangorestframework
Agora você pode usar a ferramenta django-admin para criar um novo projeto Django:
$ django-admin startproject countryapi
Este comando cria uma nova pasta no seu diretório atual chamada countryapi. Django REST framework é especialmente poderoso quando combinado com modelos Django para acessar bancos de dados.
FastAPI
FastAPI é um framework Python web otimizado para construir APIs. Ele usa type hints do Python e tem suporte integrado para operações assíncronas. FastAPI é construído em cima de Starlette e Pydantic e é muito performático.
Abaixo está um exemplo da REST API construída com FastAPI:
# app.py
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
def _find_next_id():
return max(country.country_id for country in countries) + 1
class Country(BaseModel):
country_id: int = Field(default_factory=_find_next_id, alias="id")
name: str
capital: str
area: int
countries = [
Country(id=1, name="Thailand", capital="Bangkok", area=513120),
Country(id=2, name="Australia", capital="Canberra", area=7617930),
Country(id=3, name="Egypt", capital="Cairo", area=1010408),
]
@app.get("/countries")
async def get_countries():
return countries
@app.post("/countries", status_code=201)
async def add_country(country: Country):
countries.append(country)
return country
Você pode testar esta aplicação instalando FastAPI e uvicorn:
$ python -m pip install fastapi uvicorn[standard]
Execute o servidor:
$ uvicorn app:app --reload
FastAPI automaticamente valida os dados de entrada usando os modelos Pydantic e gera documentação interativa para sua API.
Conclusão
REST APIs estão em todo lugar. Saber como usar Python para consumir e construir APIs permite que você trabalhe com a vasta quantidade de dados que serviços web fornecem.
Neste tutorial, você aprendeu como:
Identificar o estilo de arquitetura REST
Trabalhar com métodos HTTP e códigos de status
Usar requests para obter e consumir dados de uma API externa
Definir endpoints, dados e respostas para uma REST API
Começar com ferramentas Python para construir uma REST API
Usando suas novas habilidades com REST APIs em Python, você será capaz não apenas de interagir com serviços web, mas também de construir REST APIs para suas próprias aplicações. Essas ferramentas abrem portas para uma ampla gama de aplicações e serviços interessantes orientados a dados.