Categorias
API Desenvolvimento Python Tutorial

Construindo uma API em Python

Fui programador Java por 8 anos e este tutorial é para te dar uma dica: Use Python! Veja os motivos do porque eu indico esta linguagem e como fazer uma simples API com acesso ao banco de dados PostgreSQL

Olá amiguinho, como você está? Se tu chegou até aqui é porque ficou naquela dúvida para escolher em qual linguagem vai fazer teu próximo projeto. Eu acertei? Ou então é porque tá ouvindo alguém falar que Java é lento demais e ta querendo provas disso… acertei não é mesmo?

Pois bem, eu também tive essas dúvidas e nas minhas buscas (na época) não encontrei nada que fosse fácil de entender que me motivasse a sair do Java e ir para o Python, única coisa que eu consegui encontrar era: Python é melhor… e nada mais. Algum tempo depois comecei a trabalhar com dados, e nesta empresa desenvolvi como MVP um API totalmente em Python, e só então fui entender o porque Python é tão melhor que Java em muitos aspectos, e como uma linguagem conquistou o coração de um programador.

Porque não então trazer uma tutorial simples de como construir uma API em Python de maneira muito fácil, rápida e sem precisar (para começar) ler rios de documentação… Vamos lá então?

Dividindo o assunto

  1. Iniciando um projeto
    1. Definição do projeto
  2. Usando Python
    1. Ambiente de desenvolvimento
    2. Escolhendo o escopo inicial
    3. Gerenciando dependências
    4. Criando o projeto
    5. Plugins do VSCode
    6. Criando o arquivo de dependências
    7. Criando o arquivo principal
  3. Construindo rotas
    1. Decorators do Flask
    2. Parâmetros de entrada
      1. Corpo na requisição
      2. Variável na requisição
      3. Parâmetro na requisição
    3. Adicionando rota criada
  4. Testando API
  5. Manipulando dados SQL
    1. Criando módulo para conexão com PostgreSQL
    2. Criando nossa tabela no PostgreSQL
    3. Trabalhando com SELECT
    4. Trabalhando com INSERT
    5. Trabalhando com SELECT com WHERE
    6. Trabalhando com DELETE
    7. Reajustando nosso módulo de Rotas
    8. Testando as alterações
  6. Porque escolher o Python?

Iniciando um projeto

Se você é como eu, com certeza em todo início de projeto fica pensando como fazer isso, lembro que meu tempo de Java, se fosse uma API, eu já partia direto pro site do Spring Initializr, lá eu pensava no que podia ter de dependências para meu projeto. Muitas vezes acabava ainda recorrendo ao MvnRepository para adicionar uma biblioteca ou outra. Nada muito trabalhoso, já que o Spring Initializr nos ajuda muito já trazendo um arquétipo (modelo ou padrão passível de ser reproduzido em simulacros ou objetos semelhantes).

Porém, à medida que vamos construindo nosso projeto, percebemos que precisamos de muitos arquivos de configuração para fazer apenas uma API “burra”. Vou até deixar um link pro meu GitHub de uma API simples para analisarem quantos arquivos de configuração e de regras de negócio precisam ser feitos para construirmos algo “aceitável”.

E é agora que vamos ver o quanto o Python pode nos ajudar na questão produtividade. Vamos para um Hands On?

Definição do projeto

Vamos criar uma API simples que terá 4 end-points (Um endpoint de um web service é a URL onde seu serviço pode ser acessado por uma aplicação cliente).

  1. Criação de usuário [PUT]
  2. Listagem de todos os usuários [GET]
  3. Listagem de usuários filtrando por nome [GET]
  4. Remoção de usuário [DELETE]

Ambiente de Desenvolvimento

Existem várias IDEs que suportam Python como VsCode, Pycharm, Wing Python… Eu uso o VSCode porque me acostumei muito com alguma facilidades que ele traz, mas você pode até usar um editor de texto, já que Python tem a sintaxe muito fácil e enxuta. Para compararmos com o Java, veja como é um Hello World feito em Java:

package com.myhelloworld.demo;

class Simple {  
   public static void main(String args[]) {  
      System.out.println("Hello Java");     
   }  
}  

Se você já está vindo do Java, não está vendo nada muito complexo, não é mesmo? Mas veja como fica o mesmo “Hello World” feito com o Python:

print("Hello Python")

Entendeu porque o Python ajudar muito? Uma sintaxe enxuta, fácil de compreender e de programar.

Escolhendo o escopo inicial

Para meu escopo inicial vou escolher usar o Python 3.8, Flask e PsycoPg2. Vou me atentar neste post a tratar sobre o quanto o Python se torna mais produtivo.

Então você vai precisar instalar o Python 3.8 e o PIP. Se você usar Windows, procure algo no google sobre a instalação do Python e PIP (e também faça o download do Ubuntu); Se você usar Ubuntu, basta executar:

sudo apt update
sudo apt install python3 python3-pip

Gerenciando dependências

Assim como no Java podemos contar com o Maven, Gradle, Ant… no Python podemos também contar com alguns gerenciadores de dependências, em nosso exemplo vou usar o PIP, e fácil e precisamos apenas de 1 arquivo para configurar nossas dependências.

Criando o projeto

Vamos então criar uma pasta para o nosso projeto, após criado nossa pasta, iremos abrir essa pasta no VSCode indo em: File > Open Folder... e escolher a pasta criada…. você então terá a tela do VSCode assim:

Plugins do VSCode

“Se você já conhece o VSCode e não precisa de tutorial para instalar os plugins Python e Pylance, clique aqui e avance para o próximo tópico.”

William Shakespeare, 2020

Aqui no Visual Studio Code nós vemos então no lado esquerdo da tela a guia chamada Explorer. Nessa aba ficará a árvore de arquivos do nosso projeto. E do lado fica os ícones de acesso para algumas funcionalidades.

Entre elas encontramos o ícone para instalar alguns plugins pro VSCode e nós vamos utilizá-lo para instalar os plugins de Python necessários.

1. Você irá digitar Python na caixa de texto
2. Irá instalar esse plugin de Python para o VSCode
1. Você irá digitar Pylance na caixa de texto
2. Irá instalar esse plugin de suporte à linguagem para o VSCode

Criando o arquivo de dependências

Voltando para o Explorer, clique em File > New File ou utilize as teclas de atalha Ctrl+N e salve o arquivo com o nome requirements.txt e então adicionaremos nossas dependências como abaixo:

#Vamos instalar o PsycoPG2 em uma versão maior ou igual (>=) que a versão 2.8.6
psycopg2-binary>="2.8.6"
#Vamos instalar o Flask em uma versão maior ou igual (>=) que a versão 1.1.2
flask>="1.1.2"

Agora basta executar o comando pip3 install -r requirements.txt para que o PIP instale nossas dependências.

Criando o arquivo principal

Novamente, voltando para o Explorer,vamos clicar em File > New File ou utilize as teclas de atalha Ctrl+N e salvar o arquivo com o nome main.py e então começaremos nosso arquivo inicial em Python:

from flask import Flask, jsonify

app = Flask(__name__)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Iniciamos então importando o que precisamos, no caso estamos importando do pacote flask os módulos Flask e jsonify. Na segunda linha já temos a declaração da nossa primeira variável, a variável que recebe o Flask. Verificamos se estamos então executando o o arquivo main, pois, se caso este arquivo seja importado como módulo, o flask não será executado, como na linha abaixo, que rodamos definindo o host e a porta.

Assim já temos nosso arquivo funcional, se executarmos o comando python3 main.py, mas ainda vamos criar nossos primeiros endpoints, que chamaremos de rotas.

Aliás, antes de ir para o próximo tópico, que tal lembrar que para o Java temos que ter uma pasta com pelo menos uma estrutura de 4 pastas ou mais com o pom.xml para quem usa Maven, veja quanta complexidadde para apenas uma classe:

Enfim já começamos a limar a complexidade e entender do porque escolher Python pode ser uma ótima idéia! Agora sim, vamos para nossas rotas?

Construindo rotas

Vamos agora criar uma nova pasta. Com o botão direito do mouse, clique no espacio vazio da janela do Explorer e depois clique em New Folder e chamaremos esta pasta de routes. Repetindo o processo, clique com o botão direito em cima da pasta criada e em seguida clique em New File, nomeie este arquivo como user_route.py.

1. Crie uma nova pasta
2. Nomeie como routes
3. Adicione novo arquivo à esta pasta e o nomeie como user_route.py

Após realizar estes passos vamos agora trabalhar com nosso arquivo user_route.py, nele vamos criar nosso Endpoint referente à usuario, começaremos então importando os módulos necessários, do pacote flask importaremos:
Blueprint: do qual falaremos abaixo
jsonify: para transformar nossos objetos em JSON para retornar pela API
request: que nada mais é que o request feito para a API, através dele poderemos capturar os parâmetros de entrada, do qual também falaremos mais detalhadamente abaixo.
Do pactote uuid importaremos o uuid4 que é um criador de UUID (Universally Unique IDentifier) para setar nossos IDs. Assim garantimos IDs únicos.

Em seguida declararemos a variável que será a Blueprint (para facilitar, quem vem do mundo do Java, seria equivalente ao nosso @RestController e @RequestMapping), o Blueprint definirá que agrupe essas chamadas dentro deste recurso.

Também incluiremos uma variável chamada users_mock para funcionar por enquanto como nossa base de dados, nada mais é que uma lista em JSON com alguns itens, logo em seguida escreveremos quatro métodos de rotas, serão eles:

  • Listar usuário (list_user) e é chamado pelo método GET. Retornaremos uma lista com dois elementos, e também
  • Criar usuário (create_user) e é chamado pelo método PUT. Que por enquanto iremos apenas atribuiremos mais um parâmetro, o id, e nele setaremos uma UUID (Universally Unique IDentifier) , adicionaremos na nossa lista JSON e retornaremos com o código HTTP 201 (veja uma lista completa aqui)
  • Procurar usuário (find_user) que filtrará nossa lista users_mock pelo nome e que será enviado via variável HTTP. Este filtro usaremos o lambda do Python e veremos o quão simples é usar o lambda.
  • Remover usuario (delete_user) nós usaremos o o filter e o lambda para encontrar o usuário com o parametro que iremos fornecer, no caso o ID (UUID) e então removeremos ele de nossa lista.
from flask import Blueprint, jsonify, request
from uuid import uuid4

user_route = Blueprint('user', __name__, url_prefix='/user')

users_mock = [
        {
            'id': str(uuid4()),
            'name': 'Obi-Wan Kenobi',
            'email': 'obiwan@jedi.sw',
            'active': 1,
        },
        {
            'id': str(uuid4()),
            'name': 'Anakin Skywalker',
            'email': 'darth@imperial.sw',
            'active': 1
        }, 
        {
            'id': str(uuid4()),
            'name': 'Padmé Amidala',
            'email': 'padme@mother.sw',
            'active': 0
        }
    ]

@user_route.route('/list/', methods=['GET'])
def list_user():
    return jsonify(users_mock), 200

@user_route.route('/create/', methods=['PUT'])
def create_user():
    user = request.json
    user['id'] = str(uuid4())
    users_mock.append(user)
    return jsonify(user), 201

@user_route.route('/find/', methods=['GET'])
def find_user():
    name = request.args.get('name')    
    founded = list(filter(lambda x: name.lower() in x['name'].lower(), users_mock))
    return jsonify(founded), 200

@user_route.route('/<uuid>/', methods=['DELETE'])
def list_user_active(uuid):
    user = next(filter(lambda x: x['id'] == uuid, users_mock))
    users_mock.remove(user)
    return jsonify({'message': 'User removed'}), 200
Decorators do Flask

Para quem vem do mundo do Java e Springboot deve ter se acostumado com as Annotations (@ResponseEntity(), @SpringBootConfiguration), no Python isso é chamado de decorator. E também usamos para definir nossos endpoints. Em nosso exemplo acima temos o nosso decorator @user_route.route(...) neste decorator, definimos que este método é uma rota para nosse recurso de API.

Parâmetros de entrada

Bom, em nosso exemplo, temos 3 métodos que recebe parâmetros, variáveis e json, quis fazer dois diferentes justamente para mostrar mais formas de como capturar dados através da requisição (é por isso que importamos o request).

Corpo na requisição

Vamos então começar falando de como capturar um JSON enviado pelo corpo da requição através de métodos como POST, PUT, PATCH:

request.json

Só com isso, sem precisar declarar o parametro do método, capturamos do request direto de dentro do método que estamos trabalhando. Se você não está acostumado com Python, saiba que o Python trata isso como dicionário, ou seja para um JSON enviado assim:

{
    'name': 'Anakin Skywalker',
    'email': 'darth@imperial.sw',
    'active': 1
}

No Python você tratará diferentemente de um objeto no Java, por exemplo, se fosse para capturar o nome, no Java você faria: user.getName() ou user.name. No Python tratamos como dicionário, ou seja, pegamos este atributo desta forma: user['name']. No começo isso foi um problema para mim, mas não demorou para que eu me acostumasse e entendesse.

Variável na requisição

Temos então o método find_user() que recebe uma variável na requisição, dessa forma:

http://localhost:5000/user/find/?name=Obi

Quando ao final da URL colocamos ‘?’ significa que começaremos a declarar variáveis, e quando precisamos colocar mais de uma variável separamos elas por um ‘&’ dessa forma:

http://localhost:500/user/find/?name=Obi&active=1

E como capturamos essas variáveis com o Python e Flask? Claro que é pelo nosso request que importamos, iremos capturar dessa forma:

name = request.args.get('name')
active = request.args.get('active')

Caso não for passado a variável na URL, o valor retornará como None.

Parâmetro na requisição

Temos então o método delete_user(uuid) que agora recebe um parametro na declaração do método e este mesmo parâmetro é também declarado na definição do decorator, vamos rever:

@user_route.route('/<uuid>/', methods=['DELETE'])
def delete_user(uuid):
    user = next(filter(lambda x: x['id'] == uuid, users_mock))
    users_mock.remove(user)
    return jsonify({'message': 'User removed'}), 200

Então percebemos que no decorator declaramos na rota receber /<uuid>/ e o mesmo nome também é anunciado na declaração do método delete_user(uuid) e então já usamos esta variável que fora recebido por parametro no método.

Você pode declarar múltiplos parâmetros, desde que separados por barra / dessa forma:

@user_route.route('/get-actives/<active>/<name>/', methods=['GET'])
def test_multiple_params(active, name):
    active = int(active)
    return jsonify({'active': active, 'name': name}), 200

Adicionando rota criada

Agora que já temos nossa rota criada, vamos então adicionar ela para que possamos usá-la e testá-la. Voltaremos então ao nosso arquivo main.py e adicionaremos as seguintes linhas:

#importação do nosso Blueprint criado
from routes.user_route import user_route

#Registro do blueprint no app do Flask
app.register_blueprint(user_route)

Nosso arquivo main ficará assim:

from flask import Flask, jsonify
from routes.user_route import user_route

app = Flask(__name__)

app.register_blueprint(user_route)

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Agora sim… vamos testar?

Estou utilizando o software Insomnia Core mas você pode usar o Postman ou qualquer outro que seja amigável para você. Vamos começar testando a nossa listagem, que deve trazer uma lista com 3 items.

Vamos agora testar nosso metodo para adicionar um novo usuario:

Vamos testar agora o nosso metodo de listagem denovo para saber se a inclusão teve sucesso?

Olha que legal, nosso método funcionou direitinho. Que tal agora testar nosso método que busca pelo nome? Vamos primeiro testar com um nome que existe e depois com um nome que não existe… bora?

Legal, testamos e deu certo, como minha lista não tinha nenhum usuário com nome Sheila ele não retornou nada…

E agora, vamos remover um usuário? Eu escolhi um UUID para remover e vou executar o comando e depois novamente listar denovo e este usuário deverá não aparecer mais. Veremos a sequência dos 3 prints abaixo.

Eu escolhi o id do usuário Anakin Skywalker
Coloquei o UUID do usuário escolhido para remoção e adicionei como parametro da minha URL
Vemos agora na nossa nova listagem que o usuário removido já não consta em nossa lista.

Tudo funcionando perfeitamente, que orgulho da nossa API hein!?

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *