Objeto: criação de uma página que é construída com base em interação com com um sistema externo, onde esse sistema externo é implementado pelo próprio desenvolvedor - é uma API sua e não de algum ente externo. A interação com a API própria é realizada exatamente como em relação a alguma API externa, é realizada no cliente/navegador, a partir de um "client component", um componente que é renderizado no navegador. Utilizaremos o hook useState no processo.
Principais termos técnicos abordados: NextJS, react hook, useState, fetch, async, await, JavaScript, HTML
Requisitos para as instruções funcionarem: ter concluído a Receita4 e estar com o projeto resultante aberto
Requisitos para compreensão das instruções: noções bem básicas de programação, HTML e JS/NextJS
Como ler essa receita: instruções, dentro dos passos da receita e que requerem ação sua no computador, estarão escritas em cor azul. Comentários sobre os passos estarão em fonte normal, cor preta. Comandos, código-fonte, termos técnicos ou configuração explícita, estarão com fonte diferenciada.
Observação: eventuais instruções de terminal serão comandos Linux. Adapte caso esteja usando outro sistema operacional
A gente terminou o último "episódio" comentando justamente que o tipo de requisição que a gente estava fazendo não era exatamente o ideal para fazer diretamente do navegador, porque estávamos expondo a chave da API. Qualquer Zé Mané poderia clicar na página com o botão direito, e acionar a exibição do código-fonte da página, onde estaria seu código "secreto" para acesso à API. Daí um cara que viu você no carnaval agarrado com a ex-namorada dele poderia gastar todos os créditos dessa sua chave e deixar seu sistema inoperante.
Eu sei que é um pouco fora da realidade um estudante de Sistemas de Informação sair no carnaval e, ainda por cima, arrumar uma namorada, mas você entendeu meu ponto: qualquer coisa envolvendo chave de API ou qualquer informação de acesso a banco de dados ou qualquer sistema externo não é coisa para se fazer a partir de um "client component". Se você usou a diretiva "use client", tome cuidado.
Então a gente vai resolver isso sem voltar ao modelo anterior, na Receita 3a, com "server components".
Veja, aquela solução está ótima e deve ser a mais recomendada para esse caso de uso, mas a gente precisa entender e estudar toda a gama de possibilidades que a Programação Web nos oferece. Então a gente quer continuar usando o "use client" porque os componentes que são renderizados no navegador acrescentam elementos interessantes de interatividade, como a manipulação de eventos, e o uso dos "Reack Hooks", que não podem ser usados nos "server components".
A gente só não quer aquela informação da chave de API aparecendo num "client component".
Acontece que a gente pode fazer uma requisição a uma API que está implementada no nosso próprio servidor.
É essa solução que vamos implementar, vamos fazer a nossa própria API e eu espero que você diga para si próprio algo como "porra, e é fácil desse jeito???".
Crie uma nova rota /app/api/searchMovies/route.js.
Isso mesmo, valentão de rede social: uma API é uma rota no seu sistema web, só isso. A gente pode entender a diferença para as rotas que a gente vinha fazendo nas receitas anteriores da seguinte forma simplificada:
Uma rota que acaba num arquivo page.js é uma rota cujo objetivo é ser acessada por um vivente na frente de um navegador web, então deve retornar uma página React/HMTL que o navegador saiba transformar em interface gráfica com o usuário, por isso a gente precisa de um componente exportado por default e que retorna marcas JSX.
Já uma rota que acaba num arquivo route.js é chamada de "API endpoint" e é uma rota cujo objetivo é servir dados a um programa de computador - como o código JS que você escreve para manipular responder a uma submissão de formulário, mas pode ser qualquer programa, desde que saiba se comunicar via protocolo HTTP (a função fetch sabe e quebra esse galho pra você). Hoje, o formato majoritariamente usado pra servir esses dados, imaginem só, é o nosso querido JSON. Na prática uma API pode servir dados em qualquer formato, mas a gente vai se concentrar no JSON por essa receita.
Assim, você pode criar uma api em qualquer canto do diretório app, desde que não gere conflito com nenhum page.js. Ou seu "endpoint" é um route.js ou um page.js ou nenhum dos dois, mas jamais os dois. O NextJS precisa saber se a rote serve a um vivente ou a um programa, então não ponha jamais um arquivo page.js no mesmo diretório de um arquivo route.js.
Por fim, todos os nomes no caminho de diretórios são arbitrários (fora o app, claro) mas, por convenção, a gente sempre bota um "api" no início de uma rota que representa uma API, e convenção em programação, é extremamente importante. Já o nome de arquivo tem que ser route.js ou route.ts (se você estives usando TypeScript como linguagem).
Chega de conversa e agora vamos escrever nossa API de uma vez.
Edite o route.js...
export async function GET(request){
const searchParams = request.nextUrl.searchParams
const titleSearchKey = searchParams.get("titleSearchKey")
const httpRes = await fetch(`http://www.omdbapi.com/?apikey=f1cbc41e&s=${titleSearchKey}`)
const jsonRes = await httpRes.json()
return Response.json({...jsonRes})
}
E pronto, salve sua rota e pode testar no navegador mesmo.
Digitando, no navegador, a URL http://localhost:3000/api/searchMovies?titleSearchKey=happy, você deve ver algo to tipo...
- Oxente, já terminou!?
Já.
- Porra, e é fácil desse jeito!!??
Eu avisei 😉
Note que, de novidade, só mesmo o que está em destaque no código, que é mais burocracia que programação. O resto a gente já fazia.
O nome da função precisa ser GET ou algum dos métodos HTTP, que são palavras que definem o tipo de requisição que o cliente quer fazer ao servidor através do protocolo - essas palavras incluem POST, PUT, DELETE, e já dão uma ideia da intenção do que se quer fazer. Usamos o GET quando queremos dar acesso a informações, sem modificar nenhuma mutação, sem modificar nenhum dado de nosso sistema em alguma base de dados. Há quem use o GET pra tudo, acrescentar dados, atualizar dados, pesquisar etc., mas o intento original é esse que estou descrevendo - pesquisar, obter, dar acesso a dados.
Afora isso, a função recebe um objeto request como parâmetro, e esse objeto nos dá acesso a tudo o que estiver na requisição. Se o cliente enviou uma chave de pesquisa, você chega a ela através do objeto request, se mandou um arquivo, você chega a ele através do objeto request, e por aí vai. Simplificadamente, podemos dizer que o Next JS recebeu a requisição do cliente na forma de texto HTTP, transformou tudo num objeto JavaScript, o request, e passou esse objeto como parâmetro para sua função, facilitando bastante sua vida.
No miolo do método fazemos uma requisição à API de filmes, mas poderíamos muito bem acessar uma base de dados ou coisa do tipo. O conteúdo desse código não fica exposto ao cliente, apenas a URL de acesso à API.
Por fim, nossa função que representa uma rota de API precisa retornar um objeto Response. O Next JS vai pegar esse objeto, transformar em mensagem de resposta HTTP e mandar de volta para o cliente que fez a requisição, então você não precisa realmente sacar muita coisa sobre esse protocolo para implementar suas API's. O construtor Response.json serve para criar uma resposta na forma de JSON, há outros construtores para outros tipos de resposta, inclusive para erros, consultando a documentação a gente tem acesso a essas informações, vou deixar links lá na seção "Para Saber Mais".
Por hora, falta fazer um componente cliente se comunicar com sua API, mas vou deixar isso para os exercícios, pois já foi tratado em receita anterior.
- Professor, não é querendo ser chato não, mas o cara que me viu com a boyzinha dele no carnaval continua podendo acanalhar com o sistema. Basta sobrecarregar nossa API de requisições, e a gente vai repassar todas as requisições para a outra API.
Está correto, é isso mesmo. Mas a chamada estando em nosso servidor a gente pode implementar código para protegê-la, exigir login, controlar a quantidade de requisições por usuário etc. Isso a gente vai ter que fazer com diversas rotas convencionais, inclusive. Há rotas de páginas convencionais que precisam estar protegidas por mecanismos de autenticação e autorização, assim como as rotas de API, mas isso já é outro assunto.
O que tem que ficar na cabeça da gente é que código com informação sensível tem que rodar do lado do servidor.
Essa estratégia de usar uma API como uma camada de software sobre os dados do seu sistema pode ser bastante interessante, se você conseguir controlar direitinho o acesso. Se você quiser implementar uma interface mobile para seu sistema, por exemplo, não precisaria implementar mais nada (ou quase nada) no servidor: seu app para dispositivos móveis poderia interagir com sua API web para obter os dados e montar uma interface para dispositivos móveis da mesma forma que seu app Web interage - há bibliotecas diversas para dispositivos móveis que sabem fazer requisições HTTP e entregar para você só o milho da pipoca (os objetos JSON).
Se seu sistema for ficar dentro do mundo Web, existe um tópico quente que são as server actions e que são mais legais e fáceis de programar que as API's.
A gente aprende isso na próxima receita.
Abra uma nova rota e faça um client component que interaja com a sua API em vez de com a API do omdb (adapte da receita anterior).
(Desafio) Faça com que sua API acesse uma base de dados sua em vez de uma outra API.