Hoje viemos trazer algo que faz parte do cotidiano de muitos e que talvez você já tenha utilizado em algum projeto, mas apenas como fonte de dados ou até mesmo uma ferramenta. Portanto, iremos apresentar uma introdução às APIs. A partir de hoje, além de aprender a desenvolver uma, vamos entender melhor sua composição e funcionamento interno.
Acredito que para entender tal conceito é mais prático abordar uma analogia primeiro, e após isso, entrar em termos técnicos. Então vamos lá: imagine você em um restaurante. Para realizar um pedido, é necessário ler o cardápio e ver quais opções estão disponíveis, e até mesmo, às vezes, analisar o que vem naquele prato. Depois disso, você chama um garçom, correto? Então ele vem até sua mesa e anota seu pedido, mas ele não te entrega na hora o que você pediu. Ele vai até a cozinha, passa seu pedido para o pessoal da cozinha, e lá eles preparam seu pedido. Assim que pronto, o garçom pega o seu pedido e leva até você.
Agora vamos tratar dos termos técnicos de acordo com a analogia acima:
Agora que sabemos teoricamente qual é o objetivo de uma API, que significa Application Programming Interface (Interface de Programação de Aplicação), vamos tratar dos termos mais técnicos. Em uma API, temos basicamente alguns principais componentes responsáveis pelo seu funcionamento.
Primeiro, devemos entender o que é o HTTP. O HTTP, que significa HyperText Transfer Protocol (Protocolo de Transferência de Hipertexto), é um protocolo de comunicação utilizado para transferir dados na web. Ele define como as mensagens são formatadas e transmitidas, além de como os navegadores e servidores devem agir em resposta a vários comandos.
O HTTP possui algumas palavras-chave importantes e muito utilizadas, como métodos, status, request (requisição) e response (resposta).
Host
: Especifica o domínio do servidor (e, opcionalmente, a porta).User-Agent
: Informa sobre o cliente que está fazendo a requisição (navegador, aplicação, etc.).Accept
: Indica os tipos de mídia que o cliente pode processar.Content-Type
: Especifica o tipo de mídia do corpo da requisição (principalmente em requisições POST
e PUT
).Authorization
: Contém credenciais para autenticação.POST
, PUT
, e PATCH
.Requisição GET:
GET /HelloWorld HTTP/1.1 Host: localhost:3000 User-Agent: Mozilla/5.0 Accept: text/html
Requisição POST:
POST /api/data HTTP/1.1
Host: localhost:3000
Content-Type: application/json
Content-Length: 34
{
"nome": "joao",
"idade": 30
}
Dessa forma, conseguimos entender que uma API é basicamente uma conversa entre o cliente e o servidor, onde temos agentes responsáveis por certas etapas. O cliente é sempre o responsável por enviar a requisição (request) à API. Entretanto, precisamos entender que sempre haverá um endereço específico onde devemos realizar a chamada da API. Esta sempre irá disponibilizar alguns endpoints para onde o cliente poderá enviar o que deseja. Dessa forma, cada endpoint tem sua função específica, assim como seu método e retorno, que são determinados pelo servidor.
Como já entendemos o conceito básico de uma API, vamos analisar uma aplicação que utiliza uma API. Aqui no blog temos vários exemplos para explorar, mas hoje vamos focar na “Cotação de Moedas com HTTPClient e ESP32”. Neste projeto, é possível observar claramente o uso de uma API, visto que é através de um site que obtemos a informação da cotação das moedas.
Normalmente, a maioria dos sites possui um front-end/página onde você pode navegar livremente sem conhecimento prévio de tecnologia e descobrir a cotação de uma moeda, por exemplo, no site do Banco Central do Brasil. No entanto, se você quiser obter as mesmas informações sem precisar acessar e navegar pelo site, ou se deseja criar um sistema que interaja com as informações de outro sistema, geralmente é disponibilizada uma API para fazer requisições e obter informações sobre um determinado assunto que o site oferece.
No projeto, vemos que foram utilizados dados disponibilizados pela empresa AwesomeAPI, que possui uma interface mais técnica mostrando ao usuário como usar a API deles. Ao analisar o projeto, notamos que ele se torna o cliente ao realizar uma requisição à API da AwesomeAPI na URL/rota: https://economia.awesomeapi.com.br/json/last/USD-BRL,EUR-BRL,GBP-BRL,ARS-BRL,BTC-BRL,JPY-BRL.
Nessa requisição, foi utilizado o método GET do protocolo HTTP para obter uma lista atualizada da cotação das moedas, passando como parâmetro na URL as moedas desejadas. Ao fazer isso, podemos entender o funcionamento da API: nós, como clientes, queremos saber os valores, então pedimos à API (fazendo a requisição através da rota mencionada). A API recebe essa solicitação e verifica se há informações disponíveis nesse caminho. Caso existam, ela envia ao servidor, onde é realizada uma busca no banco de dados onde os dados da cotação das moedas estão armazenados. Se os dados existirem, a API os retorna ao usuário juntamente com o status HTTP adequado. Caso não existam, haverá um retorno informando a ausência de dados e o respectivo status HTTP.
Ao analisar o projeto, vemos como é importante a API e como podemos utilizá-la, visto que, sem ela, não seria possível obter informações sobre a cotação de moedas.
Dentro dos vários formatos existentes na web, os mais comuns e mais utilizados são o JSON e o XML. O JSON é atualmente mais comum.
JSON:
JSON significa JavaScript Object Notation (Notação de Objetos JavaScript), onde é oferecida uma formatação específica para o envio de dados, permitindo um padrão na troca de informações entre cliente e servidor em APIs. Imagine como seria se cada sistema enviasse dados do jeito à sua maneira; quem fosse receber teria que adivinhar a forma de envio dos dados, o que se tornaria muito complexo. Portanto, foram desenvolvidos padrões para que a comunicação fosse mais fácil e objetiva, garantindo a certeza das informações que serão recebidas e seu formato, tanto para quem envia quanto para quem recebe.
Por padrão, o JSON é formado por chaves {}
e dentro delas é necessário colocar o nome do campo específico entre aspas duplas, seguido por dois pontos e o valor dentro de aspas duplas. No exemplo abaixo, podemos entender melhor:
{ "nome": "João", "idade": 20 }
XML:
O XML (eXtensible Markup Language) é uma linguagem de marcação que oferece uma maneira flexível de criar formatos de dados legíveis por humanos e máquinas. Assim como o JSON, o XML também é usado para trocar dados estruturados entre sistemas em APIs e outros tipos de integrações.
No XML, os dados são estruturados usando tags que definem os elementos e seus atributos. Cada elemento é cercado por tags de abertura e fechamento, como mostrado no exemplo abaixo:
<pessoa> <nome>João</nome> <idade>20</idade> </pessoa>
Nesse exemplo, <pessoa> é o elemento pai que envolve os elementos <nome> e <idade>, representando os dados estruturados de uma pessoa. As tags XML permitem uma organização hierárquica dos dados, tornando possível definir informações complexas de forma clara e legível.
Vamos sair da teoria e partir para a prática, assim aprenderemos de forma mais efetiva. Antes de iniciar a escrita do código, será necessário preparar nosso ambiente para que seja possível realizar todo o processo de criação de uma API.
Para começar, crie uma pasta com o nome do projeto (nome que desejar) em algum local de sua escolha. Abra o VSCode e, na tela inicial, acesse a opção “Open Folder” e selecione a pasta que foi criada anteriormente.
Agora que estamos dentro dessa pasta, vamos iniciar o projeto TypeScript. Para isso, será necessário inserir alguns comandos na linha de terminal. Para acessar o terminal, clique Terminal, na barra de ferramentas superior, e em seguida, New Terminal – ou utilize o atalho Ctrl + Shift + ‘.
Após concluir os passos, uma nova aba será aberta na parte inferior da tela. Lá, devemos digitar os seguintes comandos:
npm init -y npm install typescript @types/node @types/express --save-dev npm install express mkdir src
Os comandos devem ser digitados na ordem em que estão. Após digitá-los, deve ser retornado algo semelhante a imagem abaixo, com os arquivos sendo criados automaticamente.
Crie um novo arquivo através do atalho do VS Code e nomeie-o como “tsconfig.json”.
Dentro desse novo arquivo, cole o código abaixo:
{ "compilerOptions": { "target": "ES6", // Define a versão do JavaScript para a qual o TypeScript será transpilado (ES6). "module": "commonjs", // Especifica o sistema de módulos a ser usado no JavaScript transpilado (commonjs é padrão em Node.js). "strict": true, // Ativa todas as verificações de tipo estritas do TypeScript (modo estrito). "esModuleInterop": true, // Permite a interoperação entre módulos ES6 e CommonJS, facilitando a importação padrão e a importação com namespace. "skipLibCheck": true, // Ignora a verificação de tipo dos arquivos de declaração (arquivos .d.ts) para melhorar a velocidade da compilação. "forceConsistentCasingInFileNames": true, // Impede discrepâncias na utilização de letras maiúsculas/minúsculas nos nomes de arquivos. "outDir": "./dist" // Especifica o diretório de saída para os arquivos JavaScript compilados. }, "include": ["src/**/*.ts"], // Inclui todos os arquivos TypeScript (.ts) dentro do diretório src e subdiretórios. "exclude": ["node_modules"] // Exclui o diretório node_modules da compilação. }
Dentro da pasta src
criada anteriormente, vamos criar um arquivo (podemos criar clicando com o botão direto na pasta src
e selecionando a opção para criar um novo arquivo (new file)) chamado index.ts
, onde vamos começar a escrever o código responsável pela criação da API.
Primeiramente, vamos iniciar o código construindo as configurações e um endpoint de teste, para verificarmos se o script esta funcionando corretamente.
import express, { Request, Response } from 'express'; // Importando a biblioteca express e seus tipos Request e Response const app = express(); // Criando uma instância da biblioteca express const port = 3000; // Atribuindo à variável port o número da porta que usaremos para rodar nossa API (podemos usar a 3000 ou a 8081) // Função responsável pela criação do endpoint /HelloWorld, onde não recebe nenhum parâmetro, usando o método HTTP GET e retornando uma mensagem app.get('/HelloWorld', (req: Request, res: Response) => { res.send('Post blog eletrogate'); }); // Função responsável por iniciar o servidor e ficar escutando requisições app.listen(port, () => { console.log(`Servidor rodando na porta: http://localhost:${port}`); });
Dentro do arquivo package.json, vamos atualizar a seção scripts. Devemos remover a parte de script toda e substituir pelo código a baixo.
{ "scripts": { "start": "node dist/index.js", // Executa o código JavaScript compilado "build": "tsc", // Compila o código TypeScript para JavaScript "dev": "npm run build && npm start" // Compila o código e depois executa o código compilado } },
Para rodar o projeto, vamos voltar onde inserimos os comandos anteriormente. Dentro do terminal, insera o comando npm run dev para executar o script e assim testar seu funcionamento.
Primeiramente, devemos criar uma variável do tipo que desejamos receber os dados dessa API. Vamos chamá-la de “DadosUsuario”, e ela terá os campos ”nome”, ”idade” e ”apelido”. Após a criação dessa variável, vamos criar também uma outra variável do tipo array que servirá para simular um banco de dados. Essa variável será responsável por armazenar os dados criados durante a execução dessa API, e vamos chamá-la de ”armazenamentoEmMemoria”.
Depois disso, iremos criar quatro endpoints iniciais (CRUD): um responsável por adicionar dados ao nosso armazenamento em memória e outro para listar todos os dados existentes na variável. Sabemos que o GET é responsável por retornar dados e o POST é responsável por enviar dados no body. Dessa forma, iremos criar duas funções onde passaremos como parâmetros o endpoint, o request e o response. Assim, quando o endpoint for chamado, ele vai receber os dados do response e os dados a serem enviados pelo request.
Importante: O que é o Express? Para entender, é necessário ter conhecimento do que é uma biblioteca e para que serve. Portanto, sabendo disso, vamos à explicação do Express. É uma biblioteca que nos permite criar e gerenciar servidores web de forma eficiente e simplificada, abstraindo muitos dos detalhes complexos do trabalho com servidores HTTP e permitindo que os desenvolvedores se concentrem na lógica de negócios da aplicação. Com o Express, você pode definir endpoints para manipular solicitações HTTP específicas, como GET, POST, PUT e DELETE. Além disso, o Express facilita o manuseio de solicitações e respostas HTTP, fornecendo métodos e propriedades convenientes para acessar dados da solicitação, enviar respostas e definir cabeçalhos.
Importante: O que significa a sigla CRUD, o CRUD é um acrônimo que representa as quatro operações básicas de persistência em sistemas de banco de dados ou APIs: Create (Criar), Read (Ler), Update (Atualizar) e Delete (Excluir). Estas operações são fundamentais e frequentemente exigidas em quase todos os sistemas, pois permitem a manipulação completa dos dados armazenados.
import express, { Request, Response } from 'express';
Importamos o Express e também os tipos Request
e Response
do próprio Express. Isso é útil para utilizar o auto-completar e validação de tipos ao usar TypeScript.
const app = express(); const port = 3000;
Inicializamos o aplicativo Express chamando express()
. Também definimos a porta que o servidor irá escutar, neste caso, a porta 3000.
type DadosUsuario = { nome: string; idade: number; apelido: string; };
Definimos um tipo DadosUsuario
que especifica a estrutura dos dados que vamos armazenar no “banco de dados” em memória. Esse tipo inclui nome
, idade
e apelido
.
let DB: DadosUsuario[] = [];
Inicializamos uma variável DB
como um array vazio do tipo DadosUsuario
. Esta variável simula um banco de dados armazenando os dados dos usuários em memória.
app.use(express.json());
Utilizamos express.json()
como intermediário para permitir que o Express analise o corpo das requisições como JSON.
app.get('/HelloWorld', (req: Request, res: Response) => { res.json("Post blog eletrogate"); });
Criamos uma rota GET em /HelloWorld
que retorna uma mensagem de teste em JSON. Isso serve para verificar se o servidor está funcionando corretamente.
app.get('/Listar', (req: Request, res: Response) => { res.json(DB); });
Criamos uma rota GET em /Listar
que retorna todos os itens armazenados em DB
. Isso permite listar todos os dados do “banco de dados”.
app.post('/Criar', (req: Request, res: Response) => { const newUser: DadosUsuario = req.body; DB.push(newUser); res.status(201).json(newUser); });
Criamos uma rota POST em /Criar
que permite adicionar um novo item ao “banco de dados”. Os dados são capturados do corpo da requisição (req.body
), adicionados ao array DB
, e o novo item é retornado com um status de 201 (Created).
app.put('/Atualizar/:nome', (req: Request, res: Response) => { const { nome } = req.params; const { idade, apelido } = req.body; const item = DB.find(item => item.nome === nome); if (item) { if (idade) item.idade = idade; if (apelido) item.apelido = apelido; res.json(item); } else { res.status(404).json({ message: 'Item não encontrado' }); } });
Criamos e adicionamos uma rota PUT em /Atualizar/:nome
. Esta rota permite atualizar um item existente no “banco de dados” por nome. Ela captura o nome do item a ser atualizado da URL e os novos dados do corpo da requisição.
app.delete('/Deletar/:nome', (req: Request, res: Response) => { const { nome } = req.params; const index = DB.findIndex(item => item.nome === nome); if (index !== -1) { const deletedItem = DB.splice(index, 1); res.json(deletedItem); } else { res.status(404).json({ message: 'Item não encontrado' }); } });
Criamos e adicionamos uma rota DELETE em /Deletar/:nome
. Esta rota permite remover um item existente no “banco de dados” por nome. Ela captura o nome do item a ser removido da URL.
app.listen(port, () => { console.log(`Servidor rodando na porta: http://localhost:${port}`); });g
import express, { Request, Response } from 'express'; // Importando as funções de request e response dentro do express const app = express(); // Biblioteca responsável pelo gerenciamento do HTTP const port = 3000; // Atribuindo à variável 'port' o número da porta que iremos usar para rodar nossa API (Podemos usar a 3000 ou a 8081) // Definindo o tipo para os dados que serão armazenados no "banco de dados" type DadosUsuario = { nome: string; idade: number; apelido: string; }; // Inicializando um array vazio que simula o "banco de dados" let DB: DadosUsuario[] = []; // Middleware para permitir o parse do corpo das requisições como JSON app.use(express.json()); // Rota GET para realizar teste de funcionamento app.get('/HelloWorld', (req: Request, res: Response) => { res.json("Post blog eletrogate"); }); // Rota GET para listar todos os itens no "banco de dados" app.get('/Listar', (req: Request, res: Response) => { res.json(DB); }); // Rota POST para adicionar um novo item ao "banco de dados" app.post('/Criar', (req: Request, res: Response) => { const newUser: DadosUsuario = req.body; // Captura os dados enviados no corpo da requisição DB.push(newUser); // Adiciona o novo item ao array DB res.status(201).json(newUser); // Retorna o novo item adicionado com status 201 (Created) }); // Rota PUT para atualizar um item do "banco de dados" por nome app.put('/Atualizar/:nome', (req: Request, res: Response) => { const { nome } = req.params; // Captura o nome do item a ser atualizado const { idade, apelido } = req.body; // Captura os novos dados do corpo da requisição // Encontra o item com o nome correspondente no array DB const item = DB.find(item => item.nome === nome); if (item) { // Atualiza os campos do item encontrado if (idade) item.idade = idade; if (apelido) item.apelido = apelido; res.json(item); // Retorna o item atualizado } else { res.status(404).json({ message: 'Item não encontrado' }); // Retorna status 404 se o item não for encontrado } }); // Rota DELETE para remover um item do "banco de dados" por nome app.delete('/Deletar/:nome', (req: Request, res: Response) => { const { nome } = req.params; // Captura o nome do item a ser removido // Encontra o índice do item com o nome correspondente no array DB const index = DB.findIndex(item => item.nome === nome); if (index !== -1) { const deletedItem = DB.splice(index, 1); // Remove o item do array DB res.json(deletedItem); // Retorna o item removido } else { res.status(404).json({ message: 'Item não encontrado' }); // Retorna status 404 se o item não for encontrado } }); // Inicia o servidor e faz com que ele escute na porta especificada (3000) app.listen(port, () => { console.log(`Servidor rodando na porta: http://localhost:${port}`); });
Para realizar os testes dos endpoints criados, vamos precisar de um ambiente mais adequado onde podemos passar o endpoint e os parâmetros ou informações dentro do header e body. Visto que, através de um navegador padrão, não conseguimos realizar alterações na requisição nem adicionar informações dentro dos componentes, utilizaremos o Thunder Client, uma extensão que pode ser facilmente encontrada dentro do VSCode.
Importante: Mesmo continuando a utilizar o VSCode, isso não significa que somos o servidor, apenas estamos usando uma ferramenta disponível dentro do software para facilitar nossos testes.
Após realizar a instalação, surgirá um ícone de um círculo com um raio no meio, onde devemos clicar. Após isso, será aberta uma tela ao lado do ícone, onde teremos algumas informações. Mas antes, devemos clicar em “New Request”. Em seguida, abrirá uma outra tela ao lado onde iremos passar as informações da requisição que queremos.
Primeiro, vamos entender melhor o que temos nessas duas telas novas. Ao lado esquerdo, temos o botão “New Request”, que permite criar uma nova requisição, e logo abaixo, temos um espaço onde ficarão salvas as requisições após a primeira realização. Na segunda tela, que é aberta ao clicar em “New Request”, é a área destinada à configuração da requisição.
Nessa área, temos primeiro o local onde definimos o método a ser usado naquela requisição. Em seguida, temos o input para inserir o endpoint que desejamos. Logo embaixo, temos diversas opções que podemos selecionar. Podemos definir o que iremos mandar no header, se quisermos enviar algum token, temos a opção do “Auth”. Já o “Body” é o principal local onde vamos inserir os dados a serem enviados, caso seja preciso; se for um GET, não há necessidade.
Quando selecionamos uma dessas opções, a barra de opções abaixo muda. No caso acima, temos o “Body” selecionado, portanto, a barra gerada embaixo nos permite selecionar o formato do envio que desejamos, e assim que selecionado, é aberto um campo onde podemos digitar as informações. Mas, onde podemos ver o retorno após realizar a requisição?
Ao lado dessas opções, na mesma tela, haverá um espaço destinado apenas para o retorno. Então, após realizar a requisição, o espaço ao lado estará disponível para mostrar a mensagem de retorno do servidor.
Hoje, as APIs se tornaram algo muito prático e comum no desenvolvimento de software. Mesmo que já seja possível encontrar conceitos mais avançados, como microserviços e outros, o conceito básico de API é fundamental para que possamos continuar evoluindo em nossos estudos e projetos. Portanto, hoje vimos seus conceitos e termos mais técnicos e também praticamos desenvolvendo uma. Entretanto, existe muito mais a ser aprendido com arquiteturas mais avançadas e ferramentas. Não podemos esquecer que usamos a linguagem JavaScript, embora existam muitas outras linguagens com as quais também é possível desenvolver APIs. Espero que tenha ajudado no seu desenvolvimento e compreensão sobre esse tópico. Até a próxima!
|
A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.
Conheça a Metodologia Eletrogate e Lecione um Curso de Robótica nas Escolas da sua Região!