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:

  1. GET /countries retorna a lista de países

  2. POST /countries adiciona 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.