Requisições com Javascript
Em conteúdos anteriores, nós vimos que a linguagem Javascript de forma padrão não possui suporte a:
- Leitura e Gravação de arquivos em máquinas clientes e servidoras,
- Comunicação direta com o banco de dados (Mysql, Sql, Postgree, MongoDB...).
Nesse caso, como iremos transportar dados e informações existentes no front-end da nossa aplicação, para uma outra linguagem de programação que seja capaz de trabalhar com esses dados? Isso por meio do Javascript?
Ou seja, de que modo o Javascript pode enviar esses dados para uma outra linguagem de programação que seja capaz de fazer leitura e gravação de arquivos, além é claro, de tratar todas as comunicações com o banco de dados ou API's de terceiros?
Isso é o que iremos descobrir nesse conteúdo 🤩
IMPORTANTE: Antes de continuar, certifique-se de que você possui um mínimo de conhecimento em Redes de Computador e Protocolos HTTP.
Requisições AJAX
Bem, antes do JSON dominar o mundo, lá no ano de 1999 surgia o AJAX que significa Asynchronous JavaScript e XML.
A partir daquele ano, o AJAX foi um grande marco no desenvolvimento web, pois permitiu que nossas aplicações front-end, pudessem enviar e receber dados de aplicações do lado do servidor, sem a necessidade de atualizar a página.
Se você pegou o início da época da internet, você vai se lembrar que toda vez que você enviava um novo formulário, a página era atualizada.
Isso acontecia, pois essa era a única forma de se enviar dados de um formulário para os scripts do lado do servidor.
Hoje em dia, é muito raro nos depararmos com formulários que ainda se comportam dessa maneira, pois agora tudo é feito por meio do AJAX, de modo a não atrapalhar a experiência do usuário com a página.
A era das API's RESTful
Em pleno 2023, nos encontramos no ápice da era das API's RESTful, que nada mais são do que aplicações feitas exclusivamente para a obtenção de dados, envio de dados, atualização de dados e remoção de dados.
Imagina uma API RESTful como uma espécie de HUB, onde enviamos certos dados contendo especificações, para que posteriormente, ela consiga se comunicar com outros sistemas, de modo a retornar uma resposta.
Segundo a Wikipédia: "Hub ou concentrador é o processo pelo qual se transmite ou difunde determinada informação, tendo, como principal característica, que a mesma está sendo enviada para muitos receptores ao mesmo tempo.".
Os correios podem ser considerados uma espécie de API do mundo real, onde podemos enviar cartas e objetos especificando somente o destinatário, de modo que eles cuidem de toda a parte da logística de entrega.
Ou seja, só enviamos uma única "requisição" para os correios, fazendo com que eles cuidem de toda a entrada do pacote, junto aos processos internos que compõem essa entrega.
Sem os correios, nós precisaríamos nos deslocar para o endereço de cada destinatário para fazermos a entrega pessoalmente, olha que loucura 😰
Sem uma API, precisamos nos comunicar diretamente com os arquivos e classes responsáveis pela execução do que queremos.
Como é o exemplo de enviarmos requisições a cada uma das URL's abaixo:
- https://meusite.com/classes/obter_usuarios.php
- https://meusite.com/classes/editar_usuario.php?id_user=59...
Com uma API, basta apenas que nos comuniquemos com esse HUB, informando certos parâmetros, que por de baixo dos panos, ele se encarregará de se comunicar com os arquivos/classes responsáveis pela execução do que queremos.
- https://meusite.com/api/usuario
- https://meusite.com/api/produtos
No exemplos das URL's acima, é só enviar os parâmetros que a API já saberá o que fazer com eles.
Essa estratégia também ajuda a mascarar as classes responsáveis por cada requisição, pois o desenvolvedor não estará entrando em contato diretamente com elas.
Tipos de Requisições
Como em qualquer requisição HTTP, existem alguns tipos de requisições que podem ser feitas.
GET: Método usado para informar ao receptor que estamos buscando a obtenção de dados. Exemplos:
- Obter a lista de usuários de uma base de dados,
- Obter um usuário específico informando o ID do mesmo,
- Obter a lista de cadastros informando uma data específica.
POST: Método usado para informar ao receptor que estamos enviando dados. Exemplos:
- Criar um novo usuário informando o nome, cpf, idade...,
- Criar um novo evento informando os dados daquele evento,
- Criar um novo produto informando os dados daquele produto.
PUT: Método usado para informar ao receptor que estamos atualizando registros existentes. Exemplos:
- Atualizar o CPF de um usuário informando um novo CPF e o ID do mesmo do usuário que será atualizado,
- Atualizar a data do evento informando uma data nova e o ID daquele evento,
- Atualizar o preço de um produto informando seu novo preço e o ID daquele produto.
DELETE: Método usado para informar ao receptor que estamos deletando um registro existente. Exemplos:
- Deletar um determinado usuário informando o ID do mesmo,
- Deletar um determinado evento informando o ID do mesmo,
- Deletar todos os produtos que possuem um preço maior que R$ 399,00.
É importante ressaltar que essas requisições não influenciam em nada durante o processo de uma requisição, pois no final das contas elas são apenas nomenclaturas, que podem ou não ser interpretadas pelo receptor (API).
Por exemplo, se você criar uma API que não faça distinção dos métodos GET, POST, PUT ou DELETE, ao mesmo tempo que ela consiga cadastrar, atualizar, recuperar e deletar informações, sem a distinção dos métodos, não importa muito😆
Pois a API vai continuar funcionando normalmente, não importando se você usa o método GET, POST, PUT ou DELETE para fazer a inserção, atualização, seleção ou remoção de novos usuários.
Podemos dizer que esses métodos funcionam mais como uma espécie de padronização, do que um comando que vão gerar um erro lá na frente caso você informe o método errado.
É claro... se a API for inteligente o suficiente para identificar isso, obviamente que ela poderá gerar um erro, mas isso vai depender exclusivamente da forma como ela foi construída.
Por exemplo, existem API's que nos possibilitam realizar cadastro de usuários via método GET, já outras, quando você tentar realizar o cadastro um erro será gerado alegando que o cadastro de usuários está disponível somente via método POST.
Portanto, utilize sempre o padrão do GET, POST, PUT e DELETE, de modo que cada um execute sua respectiva função.
Nada de criar um GET que faça função de POST, PUT ou DELETE, mesmo que isso seja possível, ok? 😂
Elementos de uma Requisição
Toda requisição que fazemos por meio do HTTP precisa de conter 3 elementos principais.
Solicitação
Também conhecida como Request, a solicitação é usada para enviar dados a uma API ou uma URL, podendo esta solicitação conter parâmetros adicionais.
Exemplo: Quando enviamos o ID de um produto para a URL [https://meusite.com/api/produtos] por meio do método GET a fim de retornar todos os dados daquele produto.
Exemplo: Quando enviamos o Nome, Idade, CPF para a URL [https://meusite.com/api/usuario] por meio do método POST a fim de cadastrar um novo usuário.
Exemplo: Quando enviamos o CPF junto ao ID de um usuário para a URL [https://meusite.com/api/usuario] por meio do método PUT a fim de atualizar o CPF daquele usuário.
Exemplo: Quando enviamos o ID de um produto para a URL [https://meusite.com/api/produtos] por meio do método DELETE a fim de deletar aquele produto em específico.
Resposta
Também conhecida como Response, são todos os dados que recebemos de volta da API ou de uma URL, quando enviamos uma solicitação.
Não importa o método utilizado (GET, POST, PUT, DELETE) a API/URL sempre envia uma resposta de volta.
Exemplo: Retorno dos dados de um produto específico da URL [https://meusite.com/api/produtos]:
//Situação 1 -> Quando a API encontra o produto:
{
"id_produto": 76,
"nome_produto": "TV OLED 48 POLEGADAS",
"marca_produto": "Samsung",
"preco_produto":
}
//Situação 2 -> Quando a API não encontra nada:
{
"Erro": "Nenhum produto foi encontrado..."
}
Exemplo: Retorno quando tentamos cadastrar um novo usuário pela URL [https://meusite.com/api/usuario]:
//Situação 1 -> Quando a API consegue cadastrar:
{
"status": "OK",
"mensagem": "Cadastro realizado com sucesso!",
"id_usuario": 88
}
//Situação 2 -> Quando a API não consegue cadastrar:
{
"status": "Error",
"mensagem": "O [CPF] do usuário é inválido..."
}
Exemplo: Retorno quando tentamos atualizar o CPF de um usuário pela URL [https://meusite.com/api/usuario]:
//Situação 1 -> Quando a API consegue atualizar:
{
"status": "OK",
"mensagem": "Usuário Atualizado com sucesso!"
}
//Situação 2 -> Quando a API não consegue atualizar:
{
"status": "Error",
"mensagem": "O [CPF] do usuário é inválido..."
}
Exemplo: Retorno quando tentamos deletar um produto específico pela URL [https://meusite.com/api/produtos]:
//Situação 1 -> Quando a API consegue deletar:
{
"status": "OK",
"mensagem": "Produto deletado com sucesso!"
}
//Situação 2 -> Quando a API não consegue deletar:
{
"status": "Error",
"mensagem": "Não encontramos o ID especificado..."
}
Cabeçalho
Também conhecido como Headers, os cabeçalhos nada mais são do que metadados que auxiliam o servidor a entender que tipo de solicitação ele esta lidando.
É nele que enviaremos o status da requisição, o tipo de conteúdo, a data de envio, controle de acesso e muitos outros.
Cache-Control:
no-cache, private
Connection:
keep-alive
Content-Encoding:
gzip
Content-Type:
text/html; charset=UTF-8
Date:
Fri, 30 Jun 2023 18:21:58 GMT
Server:
nginx/1.18.0 (Ubuntu)
Transfer-Encoding:
chunked
Vary:
Accept-Encoding
X-Content-Type-Options:
nosniff
X-Frame-Options:
SAMEORIGIN
X-Xss-Protection:
0;mode=block
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding:
gzip, deflate, br
Accept-Language:
pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control:
max-age=0
Connection:
keep-alive
Host:
micilini.com
Sec-Ch-Ua:
"Not.A/Brand";v="8", "Chromium";v="114", "Google Chrome";v="114"
Sec-Ch-Ua-Mobile:
?0
Sec-Ch-Ua-Platform:
"Windows"
Sec-Fetch-Dest:
document
Sec-Fetch-Mode:
navigate
Sec-Fetch-Site:
none
Sec-Fetch-User:
?1
Upgrade-Insecure-Requests:
1
User-Agent:
Mozilla/5.0 (Windows NT 10.0;Win60;x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36
É por meio do cabeçalho que diremos a API/URL que estamos por exemplo:
- Enviando informações no formato JSON,
- Que os parâmetros da requisição pesam 50MB,
- Que o nome do navegador (User-Agent) que estamos utilizando é 'Micilini-Browser',
- Ou que a conexão precisa permanecer ativa.
XMLHttpRequest
Em poucas palavras, o XMLHttpRequest, é uma biblioteca presente na linguagem JS, que serve para se comunicar com os scripts do lado do servidor.
A primeira forma de trocar dados por meio de requisições com o Javascript era usando a biblioteca XMLHttpRequest(), com ela nós somos capazes de retornar dados em formato XML sem a necessidade de recarregar a página atual.
Nas primeiras versões, ela possuía apenas suporte ao formato XML, mas com o passar do tempo ela foi aceitando outros formatos como JSON e textos simples.
Para criarmos uma requisição com essa biblioteca, primeiro precisamos instanciar a classe em uma variável da seguinte forma:
let request = new XMLHttpRequest();//invocar uma nova instância de XMLHttpRequest
Em seguida precisamos definir uma função de callback para receber a resposta da requisição, ou quem sabe um erro caso houver:
function sucesso() {
let resposta = this.responseText;//Resposta está dentro de um atributo da função.
console.log(data);
}
// função para tratar o erro
function error(err) {
console.log('Erro de solicitação', err); //os detalhes do erro estarão no objeto "err"
}
request.onload = sucesso; // chamar a função success se a solicitação for um sucesso
request.onerror = error; // chamar a função error se a solicitação der errado
Note que no caso da resposta da requisição, ela estará dentro de um método chamado responseText, que podemos usar posteriormente com o JSON.Parse(), isto é, caso a resposta for um JSON.
Veja também que atribuímos ambas as funções para o atributo onload e onerror da classe XMLHttpRequest(), que avisa para a biblioteca usar estas funções de retorno.
Por fim basta executarmos o método open() enviando o tipo da requisição junto com a URL a ser aberta, não se esquecendo de chamar o método send() para enviar a requisição:
request.open('GET', 'https://api.github.com/users/micilini'); // abrir uma solicitação GET
request.send(); // enviar a solicitação ao servidor.
Olha como ficou a requisição completa:
// função para tratar o sucesso
function success() {
var data = JSON.parse(this.responseText); //fazer o parsing da string para JSON
console.log(data);//Objeto que contêm os dados do meu github
}
// função para tratar o erro
function error(err) {
console.log('Erro de solicitação', err); //os detalhes do erro estarão no objeto "err"
}
var request = new XMLHttpRequest(); //invocar uma nova instância de XMLHttpRequest
request.onload = success; // chamar a função success se a solicitação for um sucesso
request.onerror = error; // chamar a função error se a solicitação der errado
request.open('GET', 'https://api.github.com/users/micilini'); // abrir uma solicitação GET
request.send(); // enviar a solicitação ao servidor.
Traduzindo: Uma nova requisição do tipo GET será enviada a URL https://api.github.com/users/micilini, de modo que a resposta seja mostrada no console por meio da função de callback chamada success.
Caso houver um erro, a função de callback cujo nome é error será a responsável por mostrar os erros no console.
Enviando Parâmetros com o XMLHttpRequest
Legal, até agora nós vimos como enviar uma requisição via método GET com o XMLHttpRequest(), mas e se quisermos enviar parâmetros?
Para enviar parâmetros precisamos fazer 3 modificações na sintaxe principal.
Onde precisamos colocar dentro dos colchetes do método send(), uma estrutura de chave-valor separados pelo sinal de igual.
- Devemos inserir um terceiro parâmetro (ele é OPCIONAL) com o valor true dentro do método open(), isso fará com que a requisição aconteça de forma assíncrona.
- Devemos definir um cabeçalho dizendo que o conteúdo da requisição será do tipo x-www-form-urlencoded,
- Dentro dos colchetes do método send(), devemos passar os parâmetros em uma estrutura de chave-valor usando o sinal de igual separados pelo &.
var xhr = new XMLHttpRequest();//instancia a função
xhr.open('POST', 'https://meusite.com/api/usuarios', true);//Terceiro parâmetro diz para a requisição acontecer assíncronamente. Este parâmetro é OPCIONAL de pode ser removido
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');//Define o cabeçalho
xhr.onload = function () {
console.log(this.responseText);
};//Definimos a função de resposta (veja que dessa vez criamos ela dentro de uma função anônima)
xhr.send('nome=Micilini&idade=25');//Aqui estamos enviando os parâmetros como nome e idade
Consulte aqui a lista completa de cabeçalhos que podem ser enviados.
Para enviar mais de um cabeçalho basta redeclarar o método setRequestHeader(), da seguinte forma:
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');//Define o cabeçalho do 'content-type'
xhr.setRequestHeader('User-Agent', 'Micilinis-Browsers');//Define o cabeçalho do 'user-agent'
xhr.setRequestHeader('Content-Length', '348');//Define o cabeçalho 'content-length'
O processo de envio de parâmetros é bem simples, o que pode dificultar mesmo é a forma como montamos esses parâmetros dentro do send(), pois muitas vezes temos esses parâmetros salvos dentro de Objetos ou Arrays e queremos monta-los dentro do send(), mas como fazemos isso?
Por meio de uma função customizada
Considerando que temos um objeto, e queremos transforma-los em parâmetros para serem usado pela biblioteca XMLHttpRequest(), nós podemos fazer isso usando uma função customizada, observe:
let meuObjeto = {
nome: "Micilini",
idade: 25
}
function retornaParametros(meuObjeto){
//let urlEncodedData = "", urlEncodedDataPairs = [], name
let urlEncodedData = [];
for(nome in meuObjeto) {
urlEncodedData.push(encodeURIComponent(nome)+'='+encodeURIComponent(meuObjeto[nome]));
}
return urlEncodedData.join("&");
}
let resultado = retornaParametros(meuObjeto);//"nome=Micilini&idade=25"
Com a variável resultado em mãos, é só inseri-la dentro dos colchetes do send();
Usando o FormData()
O FormData é um Objeto em Javascript que permite agrupar um conjunto de pares de chave-valor para ser enviados por meio do XMLHttpRequest.
Seu uso destina-se principalmente ao envio de dados de formulários, mas pode ser usado também como uma nova maneira de se transmitir dados dentro da sua aplicação.
Para declararmos um objeto deste tipo, primeiro precisamos instanciá-lo:
const formData = new FormData();
Em seguida podemos usar o método append() para declararmos o conjunto de pares de chave-valor:
formData.append("nome", "Micilini");
formData.append("idade", 28);//O número 28 será automaticamente convertido para o tipo string, ficando "28"
Por fim, basta enviar a variável formData para dentro do método send() do XMLHttpRequest:
xhr.send(formData);
JSON na Requisição
E se quisermos enviar um JSON na requisição? Bem, nesse caso é só definirmos no cabeçalho que a requisição será um JSON, e dentro do send() enviar os parâmetros em formato JSON:
var xhr = new XMLHttpRequest();//instancia a função
xhr.open('POST', 'https://meusite.com/api/usuarios', true);//Define o terceiro parâmetro
xhr.setRequestHeader('Content-type', 'application/json');//Define o cabeçalho para JSON
xhr.onload = function () {
console.log(this.responseText);
};//Definimos a função de resposta (veja que dessa vez criamos ela dentro de uma função anônima)
xhr.send('{"nome": "Micilini", "idade": 25}');//Enviando diretamente em formato JSON
API Fetch
Como dito anteriormente, o Javascript não tem capacidade de ler e gravar dados/informações dentro de arquivos ou em bases de dados.
Nesse caso, tudo o que a linguagem pode fazer é enviar ou tentar recuperar essas informações, por meio de requisições HTTP que são enviadas para arquivos locais, ou URL's internas/externas.
Vimos que podemos enviar e recuperar requisições usando a biblioteca XMLHttpRequest, porém, essa biblioteca foi caindo em desuso, sendo substituída por uma mais recente, chamada de API Fetch.
A API Fetch nada mais é do que uma interface que nos permite enviar (fazer o push) e receber (fazer o pull) dados e informações.
A maior diferença entre a antiga biblioteca, é que a API Fetch trabalha com promises em vez de callbacks.
O conceito de promises e callbacks fazem parte da programação assíncrona (que não é executada em conjunto com outras coisas), que veremos com detalhes nos próximos conteúdos.
Por hora, vamos entender como funciona o processo de uma requisição por meio da API Fetch.
Uma requisição pode ser feita da seguinte forma:
// Solicitação GET.
fetch('https://api.github.com/users/micilini')
// Tratamento do sucesso
.then(response => response.json())// converter para json
.then(json => console.log(json))//imprimir dados no console
.catch(err => console.log('Erro de solicitação', err));// lidar com os erros por catch
O primeiro parâmetro do Fetch deve ser sempre a URL que é passada como parâmetro para dentro da função:
fetch('https://api.github.com/users/micilini')
Em segundo lugar, trabalhamos com os métodos .then(), cuja tradução para o bom português é: então.
No primeiro método then(), nós estamos dizendo que a variável temporária chamada response, irá armazenar todos os dados advindos da requisição (eles vem em formato de Objetos), em seguida estamos convertendo-os automaticamente para o formato JSON e salvando na mesma variável.
.then(response => response.json())//A variável 'response' vai armazenar ela mesma em formato JSON.
É importante ressaltar que diferente do XMLHttpRequest que retorna os dados como uma resposta, a API Fetch retorna não só os dados da resposta, como também os cabeçalhos, os códigos de status, e qualquer outra informação relevante sobre a requisição em si.
É neste momento que precisamos da função response.json() para obter os dados que necessitamos do objeto da resposta, pois dessa forma fica mais fácil trabalhar com eles.
Para testarmos isso, vamos analisar a resposta dessa requisição:
// Solicitação GET.
fetch('https://api.github.com/users/micilini')
// Tratamento do sucesso
.then(response => console.log('Resposta', response))//Mostra a resposta de forma crua
.catch(err => console.log('Erro de solicitação', err));// lidar com os erros por catch
A resposta desta requisição será um objeto:
"Resposta", [object Response] {
arrayBuffer: function arrayBuffer() { [native code] },
blob: function blob() { [native code] },
body: [object ReadableStream] {
cancel: function cancel() { [native code] },
getReader: function getReader() { [native code] },
locked: false,
pipeThrough: function pipeThrough() { [native code] },
pipeTo: function pipeTo() { [native code] },
tee: function tee() { [native code] }
},
bodyUsed: false,
clone: function clone() { [native code] },
formData: function formData() { [native code] },
headers: [object Headers] {
append: function append() { [native code] },
delete: function delete() { [native code] },
entries: function entries() { [native code] },
forEach: function forEach() { [native code] },
get: function get() { [native code] },
getSetCookie: function getSetCookie() { [native code] },
has: function has() { [native code] },
keys: function keys() { [native code] },
set: function set() { [native code] },
values: function values() { [native code] }
},
json: function json() { [native code] },
ok: true,
redirected: false,
status: 200,
statusText: "",
text: function text() { [native code] },
type: "cors",
url: "https://api.github.com/users/micilini"
}
Note que a resposta em si, é um objeto que armazena uma grande quantidade de dados, desde o corpo da requisição, a URL chamada, o status code e afins.
No caso da resposta da URL, você irá observar que ela está armazenada dentro da chave json.
E é por esse motivo que o primeiro then() precisou converter toda essa requisição para o formato JSON para que o segundo then() consiga ter acesso a essa chave de modo a mostrar os resultados na tela:
.then(response => response.json())//A variável 'response' vai armazenar ela mesma em formato JSON.
.then(json => console.log(json))//Seleciona a chave 'json' de modo a mostrar a mensagem na tela
Por fim, temos o método catch() que é responsável pelo tratamento de erros:
.catch(err => console.log('Erro de solicitação', err));// lidar com os erros por catch
Enviando Cabeçalhos com a API Fetch
Você pode enviar seus próprios cabeçalhos (headers) usando a propriedade headers, vejamos:
fetch('https://api.github.com/users/manishmshiva', {
method: "GET",
headers: {"Content-type": "application/json;charset=UTF-8"}
})
.then(response => response.json())
.then(json => console.log(json));
.catch(err => console.log(err));
Observe que os cabeçalhos são objetos que podemos passar dentro do fetch() como um segundo parâmetro após a URL.
A chave method armazena o tipo de requisição, podendo este ser do tipo GET, POST, PUT ou DELETE.
Já a chave headers armazena os cabeçalhos, onde estes podem ser de diversos tipos, consulte aqui a lista completa de cabeçalhos que podem ser enviados.
Enviando Dados com a API Fetch
Também é possível enviar parâmetros por meio da API Fetch, vejamos:
// dados a serem enviados pela solicitação POST
let dados = {
nome: "Micilini",
idade: 28,
seguro: true
}
fetch('https://jsonplaceholder.typicode.com/posts', {
method: "POST",
body: JSON.stringify(dados),
headers: {"Content-type": "application/json; charset=UTF-8"}
})
.then(response => response.json())
.then(json => console.log(json));
.catch(err => console.log(err));
A chave body, é responsável por armazenar os parâmetros que serão enviados na requisição.
Observe que dentro dela estamos passando nossos objetos em formato JSON.
Se o seu objetivo é passar esses dados em um outro formato que não seja o JSON, você deve preparar seus objetos para que eles sigam a estrutura do sinal = e do &, não se esquecendo de definir o content-type para application/x-www-form-urlencoded:
fetch('https://meusite.com/produtos', {
method: "POST",
body: "nome=Micilini&idade=28&seguro=true",
headers: {"Content-type": "application/x-www-form-urlencoded; charset=UTF-8"}
})
.then(response => response.json())
.then(json => console.log(json));
.catch(err => console.log(err));
Conclusão
Neste conteúdo nós aprendemos algo importantíssimo no mundo do JS, que é o envio de requisições.
Será por meio delas que enviaremos nossos formulários, e receberemos dados e informações de API's, sejam elas internas ou externas.
Até o próximo conteúdo 😀