Alura > Cursos de Inteligência Artificial > Cursos de IA para Programação > Conteúdos de IA para Programação > Primeiras aulas do curso Supabase: ferramentas para resiliência e escalabilidade

Supabase: ferramentas para resiliência e escalabilidade

Edge Functions - Apresentação

Apresentando o curso e o instrutor

Boas-vindas a mais um curso da Alura. Eu sou o Ricardo Bugan, Head de Produto e desenvolvedor, e serei a pessoa instrutora neste curso.

Aqui, vamos continuar trabalhando no HermeX, nossa agência de aluguel de carros, com foco principalmente no Supabase. Esta é uma continuação do nosso curso de Supabase, na qual aprofundaremos o conteúdo e exploraremos mais maneiras de tornar o back-end (camada de servidor) robusto, trabalhando da forma necessária para atender aos requisitos do negócio e da aplicação que estamos criando.

Detalhando o escopo e aprofundando o back-end

Seguiremos utilizando o Supabase. No front-end (camada de interface), faremos poucas alterações nesta etapa; criaremos apenas algumas telas adicionais. Já no back-end, vamos nos aprofundar significativamente.

Nosso objetivo será entender melhor onde posicionar as regras de negócio, as regras de usabilidade e as regras de segurança, como estruturamos uma stack (pilha tecnológica) e onde essas regras devem ser aplicadas. Definiremos em que momento aplicaremos cada conjunto de regras.

Também vamos trabalhar mais com as Edge Functions do Supabase.

No curso anterior, trabalhamos um pouco nos fundamentos. Neste, nós vamos aprofundar em mais casos de uso, quando vamos utilizá-los e por que vamos adotá-los.

Preparando o sistema para escala e disponibilidade

Nós vamos pensar em estratégias para quando tivermos um pico de carga. Diante de um pico de demandas em nosso sistema, precisaremos lidar com ele utilizando as ferramentas do próprio Supabase, sem deixar o sistema cair, mantendo-o estável e sempre disponível.

Para isso, nós também vamos utilizar os recursos de filas do próprio Supabase. Além disso, nós vamos analisar a parte de observabilidade, para que você compreenda como acompanhar seu ambiente em produção.

Implementando observabilidade e encerrando a apresentação

Agora que você já tem seu sistema e já desenvolveu sua aplicação com o curso anterior, você começou a enfrentar problemas: precisou depurar, entender onde estão as coisas e identificar onde está o erro. Nós vamos discutir como montar um sistema de observabilidade em torno do seu produto e do seu serviço, para que você saiba onde o erro está ocorrendo, quando deve corrigi-lo e para que isso aconteça de maneira muito mais rápida.

Nós vamos ver tudo isso neste curso. Eu te espero lá!

Edge Functions - Revisando o projeto

Contextualizando o curso e ferramentas

Para começarmos o curso, estamos aqui novamente com a nossa locadora, a Herme Locadora, e vamos continuar desenvolvendo essa aplicação para aprofundarmos mais no nosso Supabase. Estamos também com a API do Supabase em execução e vamos prosseguir o desenvolvimento da aplicação com essa ferramenta.

Lembrando que, para este curso, estamos usando o Cloud Code. Todo o desenvolvimento de código está sendo feito via Cloud Code, que gera o VS Code com a aplicação, mas não é obrigatório utilizar essas ferramentas. Você pode usar o Codex, o Lovable, o Bolt, o Rapid; pode usar qualquer ferramenta de assistente que tenha conexão com o MCP. Basicamente, todas possuem, mas precisa ser o MCP do Supabase. No nosso caso, podemos acessar 'customização' > 'conectores' > MCP do Supabase, que fará a conexão com a ferramenta para utilizarmos as features (funcionalidades). Assim, qualquer assistente que você estiver usando pode ser conectado ao Supabase e o fluxo seguirá; você conseguirá realizar as tarefas.

Também utilizamos o Figma, porque nosso layout (projeto visual) está no Figma, então o conectamos para facilitar o desenvolvimento. Novamente, você pode usar qualquer aplicação para desenvolver o seu site (sítio eletrônico). Interagimos com o Cloud Code para ele ir desenvolvendo para nós, e é isso que vamos fazer aqui.

Demonstrando alterações na busca e nos detalhes

Entre este curso e o curso anterior que você viu, fizemos algumas alterações pequenas no projeto. Basicamente, agora, no momento em que registramos, o local de retirada...

Vamos selecionar São Paulo e, em seguida, definir as datas. Vamos especificar um dia para retirarmos o carro: do dia 30 ao dia 1º.

Ao clicar em "Buscar", o sistema filtra a lista exibida abaixo (essa funcionalidade já existia). Porém, ao acessarmos "Ver detalhes", agora o sistema preenche o local e os campos de retirada e devolução. Ele não preenche o horário, porque não o definimos, mas traz as demais informações.

Apresentando novas páginas de perfil e reservas

Essa é uma das alterações. A outra é que, na parte superior, agora que estamos logados, aparece nosso nome (o nome da conta logada), juntamente com as informações pessoais. Há também uma seção de perfil que conseguimos preencher e uma página "Minhas reservas". Essas páginas novas foram criadas entre um curso e outro para iniciarmos este curso. Vamos utilizá-las ao longo das aulas, mas nosso foco será o Supabase, portanto não queríamos gastar o tempo do curso desenvolvendo-as.

Basicamente, enviamos um prompt (instrução) para o Claude Code, que gerou essas páginas; no entanto, temos apenas o layout (disposição visual), sem as regras implementadas. É isso que vamos ver neste curso.

Estabelecendo regras e introduzindo edge functions

Vamos estudar como vamos trabalhar, principalmente neste começo, com regras de negócio, regras de validação e regras de contrato. Regras de contrato é o termo que usamos para definir como o nosso front-end (camada de interface) vai conversar com o nosso back-end (camada de servidor).

Vamos aplicar essas regras, e a primeira funcionalidade que veremos é o recurso do Supabase chamado Edge Functions (Funções de Borda). Já vimos esse recurso anteriormente e criamos a função cadastrar-veiculo para a parte administrativa, mas agora veremos como utilizá-lo de uma maneira um pouco diferente e aplicá-lo de fato na aplicação.

Edge Functions - Criando Edge Function de reserva

Contextualizando o uso da edge function e a orquestração anterior

Para nossa Edge Function (função de borda) desta vez, vamos utilizá-la para reservar carros, porque havíamos feito, no curso anterior, um tipo de Edge Function (função de borda) que serve para a orquestração, quando registramos nosso veículo. Agora, queremos reforçar a regra de negócio e a validação de informações. Para isso, vamos usar a Edge Function (função de borda) desta vez.

Deixamos um prompt (instrução) pronto e o deixamos em execução, pois sabemos que os assistentes demoram um pouco para responder, enquanto acompanhamos as coisas funcionando e revisamos o que fizemos.

No registrar veículo, que foi o que fizemos no curso anterior, trabalhamos com orquestração. Tínhamos os dados do nosso bucket (repositório), os dados do nosso veículo, a imagem do veículo e precisávamos registrar tudo em conjunto. Precisávamos ir ao banco de dados para registrar as informações, acessar o Storage (armazenamento) do Supabase para enviar a imagem, obter a URL dessa imagem e vinculá-la a esse veículo no nosso banco de dados. Dessa forma, a função registrar veículo orquestra serviços: chama o Storage (armazenamento), chama o banco de dados, pega a resposta do Storage (armazenamento), insere em outra tabela do banco de dados e faz tudo funcionar para que o registro de veículos ocorra corretamente.

Definindo a reserva atômica e tratando concorrência

Desta vez, queremos que a função garanta algumas regras. Enquanto isso acontece, vamos ver o prompt (instrução) que deixamos pronto. No nosso prompt (instrução), pedimos: criar uma Edge Function (função de borda) no Supabase para reservar carros e implementar uma reserva atômica — termo que pode soar estranho neste contexto. O que significa uma reserva atômica? Vamos ver com mais detalhes. Basicamente, o objetivo é, em uma única operação, verificar a disponibilidade, criar a reserva e bloquear o carro, impedindo o overbooking (excesso de reservas). Queremos três validações, três etapas distintas: verificar se o carro está disponível, criar a reserva (ou seja, efetivamente reservar o carro) e bloquear o carro, tudo de uma vez.

A operação atômica funciona assim: se qualquer uma dessas etapas falhar, toda a operação deve falhar. Então, se verificamos a disponibilidade e há disponibilidade, se criamos a reserva e deu certo, mas ao bloquear o carro ocorreu algum problema, precisamos desfazer a reserva. Em resumo, atômico significa que, ou toda a operação funciona por completo, ou tudo deve ser desfeito.

Dessa forma, sempre que formos reservar, primeiro verificaremos a disponibilidade, depois tentaremos criar a reserva e, em seguida, bloquear o carro. Se, ao verificar a disponibilidade, não houver disponibilidade, retornamos que não há disponibilidade. Se, ao criar a reserva, não for possível, retornamos que não foi possível criar a reserva. Se a reserva for criada, mas houver falha ao bloquear o carro, o problema pode ser, por exemplo, um volume muito grande de pedidos chegando ao mesmo tempo para aquele carro específico. Verificamos a disponibilidade, pegamos o ID de um carro físico (é um único carro) e, simultaneamente, outra operação de outra pessoa cliente tenta reservar o mesmo carro, porque ambas as verificações indicaram disponibilidade. As duas operações criam a reserva, mas a outra pessoa cliente foi mais rápida, concluiu a reserva e bloqueou o carro; quando formos bloquear, ocorrerá um erro. Nesse caso, precisamos desfazer, porque, em cenário de concorrência — concurrency (concorrência) — quando uma pessoa cliente conclui a operação, a outra não pode completá-la. Assim, se uma conseguiu bloquear o carro e a outra não, porque o carro já estava bloqueado, ao tentar bloquear, toda a função deve ser desfeita. É isso que chamamos de operação atômica: ou funciona completamente, ou não funciona, e então desfazemos os passos anteriores.

Especificando a requisição e validando no servidor

Também definimos alguns requisitos adicionais. Quando solicitarmos a reserva do carro, queremos receber um objeto em JSON (formato de intercâmbio de dados) com: ID do carro, data de início, data de fim, local de retirada, local de devolução e observações. Além disso, a chamada deve estar autenticada; ao chamar a função, precisa haver alguém autenticado no fluxo.

Por fim, além das regras sobre como queremos que a requisição chegue ao servidor, pedimos ao Supabase para validar algumas informações que, para nós, são óbvias, mas que para o sistema em desenvolvimento não serão óbvias.

Neste sistema, por exemplo, a data de início, a data de retirada do carro e a data de devolução do carro precisam fazer sentido. Temos que retirar o carro antes de devolvê-lo; não podemos fazer o contrário. Por isso, solicitamos a validação de que a data de início deve ser menor que a data de fim. Isso pode parecer óbvio para nós, mas, para um sistema, são apenas dois números. Se não pedirmos para validar nenhuma regra, o sistema analisará os dois números e, simplesmente, os salvará no banco de dados. Para nós, esses números precisam ter uma correlação: um deve ser, obrigatoriamente, anterior ao outro. Assim, a data de início precisa ser anterior à data de fim. Esse tipo de regra de negócio é o que estamos pedindo para validar aqui, e queremos que essa validação ocorra no servidor. Vamos ver isso também.

Por que validamos no servidor? Podemos, e é comum, ter o nosso frontend (interface de cliente) validando essas regras. Porém, precisamos ter a regra no servidor, porque o servidor é a nossa fonte da verdade. Quando a requisição chega ao servidor, a regra precisa estar válida e, se não estiver, deve retornar um erro.

A reserva só pode ser criada se não existir nenhuma reserva ou bloqueio que se sobreponha ao intervalo solicitado. Portanto, se, para o mesmo carro informado pelo campo carId, já existir uma reserva que coincida em algum dia do intervalo solicitado, a criação da nova reserva não pode ocorrer. Essa é outra regra que estamos estabelecendo.

Garantindo idempotência com chave de requisição

A função também deve ser idempotente. Esse termo pode parecer estranho para quem não vem do mundo do desenvolvimento, mas, basicamente, significa o seguinte: podemos ter erros na requisição ou alguém pode acessar nosso site e clicar repetidamente em “confirmar reserva”. Se isso acontecer, teremos várias requisições idênticas chegando ao nosso backend (servidor), o que pode gerar o problema de a mesma pessoa tentar reservar o mesmo carro diversas vezes. Com idempotência, se várias requisições idênticas chegarem, elas precisam produzir o mesmo resultado. O resultado da operação deve ser igual quando a requisição é exatamente igual.

Em termos práticos, teremos uma validação: se a primeira requisição chegou, conseguiu reservar o carro e retornou a reserva, então uma segunda, terceira ou quarta requisição com todos os campos idênticos não deve tentar executar a operação novamente nem gerar erro por o carro já estar reservado para a mesma pessoa. Em vez disso, deve responder informando que o carro já está reservado para essa pessoa e retornar sucesso. Assim, ao chegar a mesma informação ao backend (servidor), a resposta será sempre a mesma. Para possibilitar esse comportamento, utilizaremos uma chave de idempotência.

Implementando a transação atômica e o fluxo de validação

Há ainda algumas regras de implementação adicionais. Primeiro, vamos criar uma reserva atômica do carro no banco de dados; teremos uma função de banco de dados para isso e utilizaremos códigos de erro na nossa função de validação das Edge Functions (funções de borda).

Com base nisso, foi implementado o seguinte fluxo: utilizamos uma validação de JWT (JSON Web Token) para identificar o usuário; validamos a idempotência por meio da chave, a fim de verificar se a requisição já foi enviada; uma estratégia complementar de idempotência é, caso a mesma chave seja recebida, simplesmente ignorar a requisição seguinte porque a primeira já está em processamento; em seguida, validamos os campos e as informações fornecidas e, depois, chamamos a função do banco de dados que executará a transação de forma atômica. Dessa maneira, organizamos todo o processo e concentramos a lógica na função do banco de dados.

Para isso, foram criadas algumas tabelas: uma tabela de marcação de período bloqueado e uma tabela de cache (armazenamento em memória) de respostas por chave, para que, ao chegar a mesma requisição com a mesma chave, a resposta já esteja disponível, sem necessidade de reprocessamento. Também foi definido o esquema utilizado pela API (interface de programação de aplicações).

Verificando o código e integrando na aplicação

Agora, temos a função de reservar o carro criada e pudemos observar seu código em execução. Podemos validá-lo e verificar que as informações estão coerentes. O ideal é ler com atenção e tentar entender; mesmo sem conhecer código profundamente, há comentários que tornam o entendimento razoavelmente acessível. Obviamente, existem pontos mais complexos, mas é possível ter noção de que os passos descritos no assistente estão sendo executados.

A prova real, porém, é colocar essa função na aplicação para rodar. Vamos fazer isso.

Sobre o curso Supabase: ferramentas para resiliência e escalabilidade

O curso Supabase: ferramentas para resiliência e escalabilidade possui 105 minutos de vídeos, em um total de 34 atividades. Gostou? Conheça nossos outros cursos de IA para Programação em Inteligência Artificial, ou leia nossos artigos de Inteligência Artificial.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda IA para Programação acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas