Pequeno guia de webhooks com Ruby!
Tags: ruby, webdev, beginners, braziliandevs
Published: 2024-05-16 10:50:02

Nesse artigo irei explicar um pouco sobre Webhooks, seu uso e um exemplo prático ao final, espero que ajude seus estudos!

## Inicio!

O conceito formal fornecido pela [RedHat]('https://www.redhat.com/pt-br/topics/automation/what-is-a-webhook') é: 

*"Um webhook é uma função de retorno de chamada baseada em HTTP que viabiliza a comunicação lightweight e orientada por eventos entre duas interfaces de programação de aplicações (APIs, na sigla em inglês)."*

Para sintetizar, um webhook existe para comunicar que um evento aconteceu em um sistema A para um sistema B.

Se pararmos para pensar, esse conceito de "comunicar um evento que aconteceu em um sistema A para um sistema B" é muito útil, já que muitos sistemas precisam desse tipo de funcionalidade recorrentemente.

Um exemplo legal e bem comum é um e-commerce, que depende de muitos sistemas diferentes se comunicando para funcionar adequadamente como sistema de pagamento, entrega, o marketplace, mecanismos de recomendações, etc. 

Para ficar mais fácil o exemplo que eu vou dar não irei mencionar muitas variáveis como essas no sistema, então, imagine então um sistema de e-commerce simples com apenas um serviço de pagamento.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qvy01rxwtk9935iccz5d.png)

O e-commerce é naturalmente responsável por promover a venda e captar todas as informações necessárias do cliente, validar os dados, etc.

Já o serviço de pagamento tem como objetivo de efetivamente comprovar o pagamento a respeito do pedido do cliente.

É intuitivo pensar que você só poderá atualizar a página de feedback do pedido para "pagamento efetuado" depois que realmente o pagamento for efetuado, mas como a gente vai fazer para que o e-commerce saiba que o cliente efetivamente pagou o pedido?

O serviço de pagamento é um sistema diferente do e-commerce, como a gente vai fazer para poder integrar ambos?

## A primeira abordagem.

A primeira e mais obvia solução é vc fazer uma função dentro do seu e-commerce que vai ficar fazendo requisições para o serviço de pagamento, perguntando se o cliente fez o pagamento daquele pedido, você não vai querer ficar disparando toda hora essa requisição e pode fazer com que ela faça a requisição de 1 em 1 minuto, parece OK certo?

**ERRADO!**

Vamos supor que ocorreu o primeiro pedido do e-commerce e agora só falta o cliente realizar o pagamento, agora é só esperar ele pagar e dentro de 1 minuto sua aplicação vai atualizar a tela.

No entanto... o usuário não paga depois desse 1 minuto e nem da o ar da graça nas próximas 2hrs, o que aconteceu foi que ele, enquanto fazia o pedido no e-commerce, esqueceu que tinha que levar a vó dele para o treino de Jiu Jitsu e como ele é um bom neto, vai ficar para prestigiar o treino de sua vó e vai demorar 2hrs para voltar. Mas o mesmo usuário pensou "vou deixar o pedido aqui, quando voltar eu pago".

Nessas duas horas que se passaram seu e-commerce fez 120 requisições para o seu serviço se pagamento sem qualquer informação relevante do pagamento, sem qualquer nova atualização, perdendo capacidade computacional e gerando desperdício no sistema (Leia perca de dinheiro).

E isso foi apenas de **um** usuário imagina se fosse **100** usuários, que é um numero baixo, seria **12000** requisições jogadas completamente no lixo.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dh1er285z704zafrt1re.png)

E qual seria uma abordagem melhor?

## Abordagem com Webhook

Agora imagina que a gente pode ter um intermediário que avisa para o e-commerce que o pagamento de um pedido foi realizado? Isso seria ótimo certo? Iriamos fazer com que todo aquele desperdício da primeira abordagem fosse excluído e o sistema teria as informações atualizadas mais breve possível.

E esse intermediário ótimo tem nome, ele se chama Webhook. Com o Webhook sendo orientado a evento, podemos avisar por meio dos serviço de pagamento que um determinado pedido foi pago para o e-commerce, de forma simples e fácil, a lógica é a seguinte:

O serviço de pagamento avisa que foi pago um determinado pedido para o webhook, o webhook repassa essa informação para o e-commerce que atualiza agora a tela do usuário para "pagamento realizado" ou outra mensagem escolhida.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4305qifuk33vc1j3i8wc.png)

## Fazendo o primeiro webhook simples!

Vamos colocar a mão na massa e ver como realmente como implementar webhooks. Como eu já expliquei anteriormente webhooks servem para comunicar que um evento aconteceu em um sistema A para um Sistema B, para deixar mais enxuto, vou fazer APIs simples para do webhook.

Para isso é necessário os seguintes elementos:

- Primeira API: A primeira API fica esperando um evento acontecer na segunda, ligando com o exemplo que eu dei, poderia ser a API do e-commerce.
- O webhook 
- Segunda API: Que acontece o evento e informa para o  webhook para a primeira API, ligando com o exemplo, seria o serviço de pagamento.

> As APIs nesse caso poderiam também ser um serviço ou outro sistema!

Só para deixar claro, os exemplos a seguir foram feitos em Ruby com o micro-framework Sinatra, pelo fato de ser a linguagem que eu estou mais gostando de estudar e aprender, mas você pode implementar o mesmo exemplo na sua ferramenta preferida.

Também deixei todo os códigos em um repositório no Github para que você não precise ficar copiando, [clicka aqui](https://github.com/GiovannyCordeiro/webhook).


Vamos começar criando a segunda API que é responsavel por notificar o evento para o webhook, o que ela precisa fazer é justamente fazer uma requisição simples para o webhook, como a gente faria para uma API normalmente. Segue o código.

```Ruby
require 'sinatra'
require 'rest-client'  
get '/' do
	puts 'test second app'
end    
post '/service' do
	# Logica do serviço de pagamento...     
	data_service = {
		'body' => 'dados para o primeiro serviço'
	}
	RestClient.post(
	    'http://127.0.0.1:4568/webhook/event-created',
        data_service.to_json,
        {content_type: :json, accept: :json}
    )
end
```

Agora vamos criar o próprio webhook, para isso você precisa criar outra API normalmente, no entanto, essa API vai receber os dados que vem da API 2 (no exemplo que dei, serviço de pagamento) e vai passar para a API 1 por meio de uma requisição POST (no exemplo que dei, e-commerce). Não atoa Webhook são chamados também como APIs inversas.

Ou seja, o webhook recebe uma requisição de uma API (ou serviço) e faz outra requisição POST passando esses dados para outra API (ou serviço).

```Ruby
require 'sinatra'
require 'rest-client'
post '/webhook/event-created' do
	puts 'chegou no webhook'
    request_first_app = RestClient.post(
		'http://127.0.0.1:4567/recieve_event',
        request.body,
        headers: { 'Content-Type': 'application/json' }
    )
end
```

Só ficou faltando criar a primeira API, que vai receber os dados da Segunda API por meio do webhook, fazendo assim com que tudo funcione da forma adequada.

```Ruby
require 'sinatra'
before do
	request.body.rewind
	@request_payload = JSON.parse request.body.read
end
get '/' do
	puts 'hello world'
end
post '/recieve_event' do
	puts "evento que ocorreu no sistema 2, os dados são: #{@request_payload['body']}"
	# logica após receber o dado do body....
end
```

Agora o que vai acontecer? Quando você fizer uma requisição POST na rota `/service` da segunda API essa ela mesma vai executar sua logica e avisar para o webhook que determinado evento evento ocorreu passando os dados escolhidos, o webhook repassa para a primeira API.

Antes de testar, se atente em rodar as APIs e o Webhook nas portas certas, caso contrario, pode ser que tenha dor de cabeça com isso, logo as portas nesse meu exemplo são as seguintes:

- *first_app*: porta 4567
- *weebhook*: porta 4568
- *second_app*: porta 4569

Após rodar todas as aplicações com: 

> ruby src/nome_do_servico -p numero_da_porta

Faça uma requisição POST com body JSON para o endpoint *http://127.0.0.1:4569/service*, você notará que o dado mockado que você mandou no `second_app` vai ser logado da mesma forma no `first_app`, o que prova que conseguimos implementar de forma bem-sucedida o webhook.

## É isso!

Espero que esse artigo tenha te ajudado a entender sobre webhooks! Qualquer coisa estou a disposição de ajudar e trocar uma ideia sobre tech nas minhas redes sociais, é isso!