Kloto: Publique sua API com banco de dados na AWS/Azure/Google Cloud em 5 minutos!
Olá! Venho aqui mais uma vez compartilhar uma ferramenta interessante que descobri recentemente.
Introdução
Principalmente quando se está começando, é comum você desenvolver um projeto, por exemplo, uma API com express com um banco de dados, e ficar com dúvida sobre como publicar-lo para torna-lo acessível pela internet.
Quando de tratá de frontend, já existem ferramentas como Netlify e Vercel, onde você conecta o repositório do projeto e com alguns cliques você tem o seu site no ar com certificado SSL automático e atualizado sempre que você faz alguma modificação.
Quando se trata de backend, existem sim plataformas que facilitam bastante o trabalho. Vou citar aqui o Heroku e o Deta. São ótimas alternativas e bastantes fáceis de se utilizar.
Mas hoje eu vou falar sobre o Kloto, uma ferramenta recente, que utiliza o conceito de IfC (Infrastructure from code), semelhante à IaC (Infrastructure as code) (conceito utilizado em ferramentas como Terraform e Pulimi). Posso fazer um post futuramente explicando mas detalhadamente oque é isso e o que essas ferramentas fazem. Mas de maneira resumida, no IaC você define o provisionamento e configuração da sua infraestrutura (servidores, bancos de dados, etc) em arquivos de configuração ou scripts. Já no IfC (embora tenha algumas semelhaças com o IaC) você não define explicitamente os recursos. O código da sua aplicação é inspecionando e a parti dele a sua infrastrutura é provisionada.
Com o Kloto, você consegue publicar seus aplicativos nos maiores provedores de servidores do mundo: AWS, Azure e Googe Cloud. Você também consegue provissionar banco de dados relacionais e não relacionais, armazenamento de arquivos, etc. Tudo isso de maneira simples. Você também facilmente trocar de provedor.
Vamos ver como fazer isso com um exemplo prático! (Vou pressumir que você já tenha o Nodejs instalado). Neste exemplo, vamos utilizar a AWS como provedor, então você irá precisar de uma conta lá, (Você vai precisar de um cartão de crédito, quando eu fiz utilizei um cartão pré-pago, mas não sei se hoje em dia ainda é possível fazer dessa forma). E não se procupe, se a sua aplicação não for ter muito acesso, os custos serão bem baixos. Talvez até de graça, pois a AWS oferece alguns recursos gratuitamente até certo limite.
Código do projeto
Para este exemplo, vou criar uma API bastante simples com Nodejs e Express (atualmente, Python e Go também são suportados, mas se você entende o minimo de denvolvimento de API's em qualquer linguagem deve entender o exemplo). Essa API vai apenas possuir uma rota para cadastrar um usuário e salvar em um banco de dados.
// importação das bibliotecas
const express = require("express")
const bcrypt = require("bcrypt")
const { randomUUID } = require("crypto")
const app = express() // Cria a API no Express
const database = new Map()
// Cria uma rota chamada "/registrar", que utiliza o metódo POST
app.post("/registrar", async (req, res) => {
const { email, senha } = req.body // Obtem o email e a senha que o usuário inseriu
const usuarioId = randomUUID() // Gera um identificador único para o usuário
// Salva o usuário no banco de dados
await database.set(usuarioId, {
email,
password: await bcrypt.hash(password, 10) // criptografa a senha utilizando o algoritmo Bcrypt, com o salt 10 (caso você não saiba, esse é um procedimento básico de segurança)
})
res.json({ status: "sucesso", usuarioId }) // Retorna uma mensagem informando que a ação foi concluida com sucesso e o identificador do usuário
})
app.listen(8000, () => console.log("API rodando em http://localhost:8000")) // Inicia a API na porta 8000
Este é o nosso é exemplo, é bem simples. Mas se for minimamente atenciso, notará que eu não utulizei nenhum banco de dados, eu apenas salvei os dados do usuário em um Map, que é um tipo de váriável no Javascript que é utilizada para armazenar dados no formato chave/valor (caso queira entender melhor, sugiro ler este artigo. Nesse caso a chava é o identificador do usuário e o valor os dados dele. Isso significa que todos os dados ficarão armazenados na memória, o que tem duas grandes desvantagens:
- Os dados não serão salvos de maneira persistente, quando o programa for encerrado todos os dados serão perdidos;
- Não é nada escalável, pode rápidamente ocasionar em alto consumo de memória.
Outro detalhe é que é esta sendo utilizado um await
na frente do database.set
. Mas esse metodo não retorna uma Promise
, logo, isto não é necesário, esse await
tem o mesmo efeito do que não ter nada nessa parte código (caso não saibá o que significa await
e Promise
, sugiro ler este artigo.
Você deve está se perguntando o porquê disso, mas calma que vai fazer sentido ;)
Instalação do Klhoto
Agora, vamos à instalação do Klotho. Como a documentação indica, não existe uma versão para Windows, apenas para Linux e MacOS. Mas você pode utilizar o WSL.
A instalação é bem simples:
Linux
curl -fsSL "https://github.com/klothoplatform/klotho/releases/latest/download/klotho_linux_amd64" -o klotho # Baixa o executável
chmod +x klotho # Dá permissão para executar o executável
sudo mv klotho /usr/bin # Move o executável para a pasta "/usr/bin", esse passo é opicional, mas eu recomento pois assim você consegue utiliza-lo em qualquer pasta
Mac OS
brew install klothoplatform/tap/klotho
Feito isso, você pode verificar se a instalação foi concluida com sucesso com o seguinte comando:
klotho --login <seu email>
Inserção das anotações do Klhoto no código
Com isso feito, agora temos que fazer algumas moficações no código:
const express = require("express")
const bcrypt = require("bcrypt")
const { randomUUID } = require("crypto")
const app = express()
/* @klotho::persist {
* id = "database"
* }
*/
const database = new Map()
app.post("/registrar", async (req, res) => {
const { email, senha } = req.body
const usuarioId = randomUUID()
// Salva o usuário no banco de dados
await database.set(usuarioId, {
email,
password: await bcrypt.hash(password, 10)
})
res.json({ status: "sucesso", usuarioId })
})
/* @klotho::expose {
* id = "api"
* target = "public"
* }
*/
app.listen(8000, () => console.log("API rodando em http://localhost:8000"))
Como você deve ter percebido, foram adicionados alguns comentários no código. Assim como qualquer comentário, eles não vão interferir na execução do seu programa. Mas quando o Klotho estiver inspacionando o seu código, esses comentários serão processados, e com base neles, o que você precisa será provisionado.
O primeiro comentário foi feito encima de do Map
:
/* @klotho::persist {
* id = "database"
* }
*/
const database = new Map()
A anotação @klotho::persist
vai funcionar de maneira diferente de acordo de com o que a utilizamos. Nesse caso, como ela está sendo utilizado com um Map
, ela vai gerar um banco de dados DynamoDB (no caso na AWS) com o nome "database" (que voi passado no parâmetro "id"). O DynamoDB é um banco de dados não relacional (posso fazer um post sobre ele fururamente). Então sem modificar nada no código, quando implantarmos esse API ele irá substituir todos os lugares que utilizamos os metodos desse Map
para chamadas para o banco de dados! Realmente interessante não é mesmo?
E por isso a necessidade do await
na frente do database.set
, as chamas ao banco da dados são assincronas.
O segundo comentário foi feito acima do app.listen
:
/* @klotho::expose {
* id = "api"
* target = "public"
* }
*/
app.listen(8000, () => console.log("API rodando em http://localhost:8000"))
Essa anotação fará com que o a sua API fique disponivel na internet, através do API Gateway e do Lambda (no caso da AWS, não vou explicar o que eles são para não tomar tempo, mas como eu faleim acima, utilizar esses servições tem a vantagem de ter um custo bem baixo).
Instalação e configuração da AWS CLI
Agora, precisamos fazer mais alguns passos para publicar a nossa API. O primeiro, é instalar e configurar a AWS CLI:
Instalação no Linux
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
Instalação no Mac OS
curl "https://awscli.amazonaws.com/AWSCLIV2.pkg" -o "AWSCLIV2.pkg"
sudo installer -pkg AWSCLIV2.pkg -target /
Agora, rode o seguinte comando:
aws --configure
Ele irá pedir seu AWS Access Key ID e sua AWS Secret Access Key. Para obter esses tokens, vá no seu console dá AWS e siga os seguintes passos:
Agora batsa copiar esses valores e colar no terminal:
As outras informações pedidas não precisam ser informadas, então vamos para o próximo passo.
Instalação do Docker e Pulumi
Também vamos precisar do Docker e do Pulumi.
Para instalar o Docker, recomendo seguir o tutorial oficial. Para instalar o Pulumi, o processo é bem simples:
Linux
curl -fsSL https://get.pulumi.com | sh
Mac OS
brew install pulumi/tap/pulumi
Feito isso, execute os seguntes comandos para configurar o Pulumi para uso local:
pulumi login --local
export PULUMI_CONFIG_PASSPHRASE=""
Todas essas configurações só precisam ser feitas uma vez, agora vamos ver como fazer deploy do nosso projeto
Deploy do projeto
Dentro da pasta do projeto, vamos executar o seguinte comandos:
klotho . --app my-api --provider aws
Com esse comando, o Kloto vai inspecionar o código e "compila-lo" para que o pulumi possa fazer o provisionamento. Note que foi gerada uma pasta chamada "compiled".
pulumi config set aws:region us-east-1 --cwd 'compiled/' -s ts-sg // configura o projeto para ser provisionado nqa região "us-east-1"
Por fim, para fazer o deploy na nossa aplicação, vamos executar os segujtes comandos:
cd compiled
npm install
pulumi up
Confirme com yes
quando for pedido. Esse processo pode demorar alguns minutos. No final, você poderá ver a url pública da sua API:
Outputs:
apiUrls : [
[0]: "https://qxy99avspl.execute-api.us-east-1.amazonaws.com/stage/"
]
Pronto, agora você tem a sua API com banco de dados acessícel de qualquer lugar da internet 🥳
Certamente não foram os 5 minutos prometidos no título, mas uma vez que você sabe o processo pode durar até menos de 5 minutos ;)
Espero que esse post tenha te ajudado, caso tenha alguma sugestão ou tenha ficado com dúvida em alguma coisa, por favor coloque nos comentários. Pretendo fazer mais posts aprofundando o que foi dito aqui.