Objeto: escrever uma função de API e uma página que exiba os resultados dessa função. A chamada à função de API deve ser feita a partir do navegador
Principais termos técnicos: banco de dados, MySql, API, fetch, hook, HTML, JavaScript
Requisitos para as instruções funcionarem: ter concluído a Receita7 e executar as instruções no projeto resultante desta receita
Requisitos para compreensão das instruções: ter concluído as receitas 4 e 7, ter noções básicas de programação, HTML e NextJS. Ter alguma noção de banco de dados
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
E se quiséssemos um sistema parecido com o da Receita4, sendo que, em vez de acessar uma API de um site externo, acessamos uma API que nós mesmos implementamos em nosso sistema. Essa API tem funções que recebem (podem receber) parâmetros, acessam uma base de dados e retornam objetos JSON, tal qual todas as API's que acessamos até agora. A diferença é que as outras API's eram escritas por terceiros e rodavam em algum servidor sobre o qual a gente não tinha nenhuma gerência e acessavam uma base de dados que a gente não sabia nem qual era. A API desta receita será escrita por nós e rodará em nossos servidores, e acessará o banco de dados MySql que configuramos na Receita7.
Se você não está entendendo esse termo API, sugiro que, ao menos, dê uma relida na Receita4. Se você não está entendendo sobre o acesso à base de dados releia a Receita7.
Sintetizando: vamos criar uma API, que nesse contexto pode ser compreendida como um conjunto de funções que podem ser invocadas remotamente, via HTTP. Nessa API, implementaremos apenas uma função, que pesquisa todos os filmes de nossa base de dados (o código que acessa o banco de dados já está implementado). Em seguida, escreveremos uma página que faz, via JavaScript e rodando no navegador, uma requisição a essa função dessa API. A página processa os objetos JSON recebidos como resposta da chamada à função da API e produz uma página HTML, que é exibida ao usuário.
Abrir o projeto resultante da Receita7.
Executar o código de migração do banco de dados, que vai povoar o banco com valores fixos. Se seu banco já estiver povoado, não precisa executar o comando a seguir:
npm run migrate
Você tem um servidor de banco de dados rodando numa máquina remota e seu código existente tem funções capazes estabelecer conexão, pesquisar e alterar valores desse banco. Vamos escrever uma API e, nessa API, implementar uma função que pesquise todos os filmes em nossa base de dados e retorne objetos JSON representando essa coleção de filmes.
Criar um diretório api na raiz do projeto. O nome do diretório é pra ser api, não seja criativo.
mkdir api
Criar, dentro do diretório api, o arquivo movies.js.
Editar o arquivo movies.js e deixá-lo com o seguinte conteúdo.
import {allMovies, searchMovies} from '../../lib/db'
export default async function handler(req,res){
const {titleStringSearch} = req.query
let result = ''
if (!titleStringSearch || titleStringSearch==='') result = await allMovies()
else result = await searchMovies(titleStringSearch)
res.status(200).json(result)
}
A gente chamou a função de handler, mas poderíamos chamar de joaoGrilo. Isso pouco importa, pois sua execução vai ser requisitada através da rota que condiz com seu caminho no sistema de arquivos. Quando o servidor receber uma requisição a /api/movies [sem o .js], será executada a função exportada por default, qualquer que seja o nome dela. Funções de API só são executadas a partir de requisições, e sempre mandam uma resposta de volta, mesmo que seja um código de erro, então, muito convenientemente, o servidor web, no que final das contas invoca a função diretamente, passa-lhe como parâmetro dois objetos, req e res, para que seja facilitada a tarefa de recuperar dados da requisição (dados de pesquisa, chaves de autorização etc.) e de mandar de volta os resultados da função uma vez que estes tenham sido computados.
A implementação da função handler, em si, é bem mamão com açúcar, porque o trabalho sujo é feito nas funções de biblioteca allMovies e searchMovies, importadas lá no início, e é assim que tem que ser.
Desta forma, a linha const {titleStringSearch} = req.query simplesmente vai buscar no atributo query do objeto req algum parâmetro de nome titleSearchString. Se você for um pouquinho esperto já deve ter percebido que, numa requisição a /api/movies?titleSearchString=MariaPrea esse valor MariaPrea vai bater lá naquela constante titleSearchString da linha const {titleStringSearch} = req.query.
Depois disso a função simplesmente checa se essa dado existe. Se não existir, chama a função que retorna todo mundo. Se existir, chama uma função que filtra do dados do banco de acordo com os parâmetros.
Uma vez de posse dos resultados de qualquer uma das pesquisas, é executada a linha res.status(200).json(result).
- Ah, e esse 200, vem de onde?
Vem do protocolo HTTP. Quem fizer uma requisição de execução dessa função o fará através do protocolo HTTP e deve receber um código de resposta HTTP junto com o conteúdo do que foi produzido pela função. Nesse caso, esse encadeamento de chamadas manda o código 200 (significa OK) e o resultado da pesquisa junto ao bando convertido em um string estruturado como JSON. Você provavelmente já conhece um código HTTP, o 404, que é enviado quando a requisição solicita um recurso inexistente. Há vários outros códigos, no entanto. Claro que não é a coisa mais linda do mundo ter esse 200 aí como um número mágico, mas você pode criar uma constante simbólica com o mesmo valor e usá-la sem maiores dificuldades. Não vamos cobrir isso aqui.
Que tal testar se essa fuleiragem já funciona?
Salve o arquivo e suba o servidor.
Não temos a página ainda, mas temos uma API aberta a requisições HTTP, e temos uma criança que sabe fazer muito bem requisições HTTP - o navegador.
Abrir o navegador, escrever a URL http://localhost:3000/api/movies na barra de endereços e pressionar enter.
Você deve receber uma resposta parecida com isso:
// 20210407120018
// http://localhost:3000/api/movies
[
{
"imdbId": "tt0015400",
"title": "The Thief of Bagdad",
"year": "1924",
"poster": "https://m.media-amazon.com/images/M/MV5BNmM0MjdkMDQtMDMwMy00ZjE4LThjMDUtNjA4ZjkxYzM0MWRjXkEyXkFqcGdeQXVyMDI2NDg0NQ@@._V1_SX300.jpg"
},
{
"imdbId": "tt0029833",
"title": "A-Lad-In Bagdad",
"year": "1938",
"poster": "https://m.media-amazon.com/images/M/MV5BYjM4MDBiMmQtZmNhYy00ZmFhLTkxMmItOTViYzdlYTk2YTIzXkEyXkFqcGdeQXVyMTU5NjQzMzU@._V1_SX300.jpg"
},
(vários outros)
Se isso acontecer, sua API está ok, está publicada, ainda que em ambiente de desenvolvimento. Você pode tentar pesquisar por http://localhost:3000/api/movies?titleStringSearch=bagdad e vai ver que retornarão apenas filmes em cujo título conste a palavra bagdad.
Se temos uma API pública para chamar nosso trabalho se reduz ao trabalho da Receita4: escrever uma página que chame essa API, através da função fetch(), processe esses objetos feiosos JSON e os transforme num código HTML bonitinho para o navegador exibir para seu usuário.
Criar um arquivo movies2.js dentro do diretório pages. Dá pra copiar e colar o código do movies2.js lá da Receita4, trocando apenas o valor da URL da API. O código deve ficar assim:
import useSWR from 'swr'
export default function Movies2(){
const {data, error} = useSWR(`http://localhost:3000/api/movies`, fetcher)
if (error) return <div>falha na requisição...</div>
if (!data) return <div>carregando...</div>
return (
<div>
{ data.map( (m) => <div>{m.title} --- {m.year}</div> ) }
</div>
)
}
async function fetcher(url) {
const res = await fetch(url);
const json = await res.json();
return json;
}
O código é bastante familiar para quem tiver acompanhado as receitas anteriores e não vamos explicá-lo novamente.
Tecnicamente, está pronta essa receita. Se você salvar e tentar acessar a página, deve encontrar uma mensagem de erro, no entanto, algo como Module not found: Can't resolve 'swr'.
Mas não entre em pânico. Isso ocorre porque estamos importando um hook sem que o pacote onde o hook está tenha sido instalado na aplicação web. Isso é fácil de resolver.
Derrubar o servidor (no terminal, um Ctrl+C faz esse serviço.
Instalar o pacote swr, a partir do terminal:
npm i swr
Subir o servidor novamente.
Acessar a página http://localhost:3000/movies2 e ver o resultado.
https://nextjs.org/learn/basics/api-routes
https://www.npmjs.com/package/serverless-mysql
https://vercel.com/guides/deploying-next-and-mysql-with-vercel
1. Acrescentar o poster dos filmes à página
2. Faça uma nova página, inicialmente apenas com um link Mostrar. Quando o link é clicado, ele muda seu texto para Ocultar e mostra, abaixo do link, uma pesquisa de todos os filmes. A pesquisa deve ser feita a partir do navegador e usando a API que criamos, como nesta receita
3. Faça uma nova página, inicialmente apenas com um campo de texto e um link Pesquisar. Quando o link é clicado, são exibidos, abaixo, filmes em cujo título conste o texto escrito no campo de texto. A pesquisa deve ser feita a partir do navegador e usando a API que criamos, como nesta receita