Formulário de Avaliação com API Gateway e DynamoDB

Desde que comecei a usar mais Python no meu dia a dia, tenho tido algumas ideias de pequenos projetos, seja para aprender algo novo, seja para reforçar conceitos, utilizando a linguagem. Neste laboratório, tive a ideia de fazer algo simples, mas que me permitisse usar o API Gateway da AWS e o Flask para manter o frontend.

O frontend consiste em um formulário e uma lista de avaliações enviadas, contando apenas com 3 campos a serem preenchidos: título da avaliação, nome e a avaliação em si. Não estava no escopo fazer uma verificação do tamanho da mensagem ou verificar se os campos foram realmente preenchidos (apesar de ter uma verificação simples do lado do frontend), por exemplo.

Optei por salvar as avaliações no DynamoDB, um banco de dados NoSQL oferecido pela AWS, já que tem sido um dos temas pertinentes nos meus estudos. Tanto o registro quanto a busca pelas avaliações foram realizadas via Lambda com funções em Python, ativadas por métodos HTTP diferentes configuradas no API Gateway.

Desenho da Arquitetura
Desenho da Arquitetura
Screenshot do frontend com o formulário e lista de avaliações.
Frontend com o formulário e lista de avaliações

Por ser uma aplicação bem simples, optei for fazer apenas com HTML e CSS junto do Flask. A única responsabilidade dela é de passar as informações do formulário e a data para a API e requisitar as registradas.

Partindo para a infraestrutura na nuvem, optei por utilizar Terraform na criação dos recursos. Para AWS, geralmente utilizo mais o Serverless Framework, então foi a primeira vez em um tempo que utilizei o Terraform para tal.

Lidando com o banco de dados

Foi a primeira vez que criei uma tabela no DynamoDB, apesar de utilizá-lo com frequência. Foi uma oportunidade boa para dar uma lida melhor na documentação e sobre boas práticas principalmente no que tange à definição da partition key (PK) e sort key (SK).

Como as funções de leitura e escrita foram feitas em Python, precisei utilizar o Boto3 para fazer a comunicação das Lambdas com a tabela. Existem duas formas de fazer isso: utilizando o client ou o resource. Enquanto o client é mais baixo nível, o resource é mais alto nível e possui uma sintaxe mais simples de lidar com o banco, que foi o caminho que decidi seguir.

Para buscar pelas avaliações, optei pelo uso de query. O uso dele no lugar de scan para retornar todos os registros requer que os itens tenham uma PK de mesmo valor. Inicialmente, por estar fazendo uma tabela bem simples, os registros ficariam com a seguinte estrutura:

PK: "REVIEW",
username: "Nome do usuário",
title: "Título da avaliação",
date: "Data do envio",
message: "Texto da avaliação"

O que satisfazia a necessidade da query, pois todos teriam a mesma PK, mas e na hora de salvar? Estava sobrescrevendo, é claro! O PK precisa ser um identificador único quando ele é a única chave presente, pois todo item precisa de uma primary key. Tudo que eu enviava era sobrescrito ao usar put_item para salvar, pois o item já existia devido a ter a mesma chave. Para resolver, precisaria usar uma SK para tornar a primary key em uma composite key, ou seja, uma chave composta (por PK + SK). Dessa forma, apenas a SK precisa ser diferente entre os itens.

A estrutura então passou a ser:

PK: "REVIEW",
SK: "REVIEW_ID#UUID,
username: "Nome do usuário",
title: "Título da avaliação",
date: "Data do envio",
message: "Texto da avaliação"

Depois, eu percebi que gerar apenas um UUID seria suficiente, sem necessidade do uso de REVIEW_ID#, mas deixei assim mesmo 😅.

Permissionamento

Na grande maioria das vezes que lido com serviços na AWS (ou em qualquer outra cloud), eles já possuem suas políticas de IAM definidas. Como estava criando tudo do zero, me esqueci de uma parte fundamental quando se trata de comunicação entre os serviços: eles precisam de permissão pra isso.

Após seguir a documentação do Terraform, configurar o CORS, meu API Gateway estava no ar com a rota /reviews, permitindo GET e POST.

Abri o Postman, realizei um GET e... Internal Server Error! 🥹

Revisei todo o código no Terraform, verifiquei se as Lambdas não estavam gerando um log, se a URL não estava errada, nada parecia ser o problema. Até que depois de um tempo passou pela minha cabeça: "se eu precisei dar permissão para as Lambdas mexerem com o DynamoDB, então... o API Gateway deve precisar de permissão para invocar as funções". Dito e feito, era isso mesmo que faltava.


No final, deu tudo certo e senti que pude ler bastante sobre diversos assuntos que uso no dia a dia. A parte mais "novidade" foi sobre proxy no API Gateway, que devo pegar com mais calma para ler depois. O projeto pode ser acessado nesse repositório.

Deixo aqui alguns links que foram muito importantes e que serviram de referência:

Last updated