Seu guia completo para construir APIs de sucesso e acelerar sua Transformação Digital

Introdução ao Next.js

Introdução ao Next.js

O Next.js é um framework React, criado pela Vercel, que permite a construção de interfaces Web adicionando inúmeras funcionalidades em cima do React.

Em aplicações React tradicionais, utilizando o create-react-app, por exemplo, toda a interface e chamada à API é feita pelo lado do client (browser), então quando um motor de busca ou crawler tenta indexar uma página, ira esperar que todo o Javascript e a chamada a API esteja carregada para ser feita toda construção da nossa página. 

Então essa busca retorna vazia sem informações relevantes para que a nossa aplicação seja indexada. Esse é um dos principais problemas que o Next.js procura solucionar. Com este framework, podemos renderizar nossas páginas no lado do servidor utilizando o SSR (Server Side Rendering).

Principais características do Next.js

Construindo aplicações utilizando o Next.js, é possível:

  • Renderização estática pelo lado do servidor e do lado do client para desempenho de páginas e SEO (Melhor indexação para os motores de busca do Google);
  • Construção do servidor utilizando o React para conexão com o banco de dados, já que o Next.js utiliza o NodeJs como seu interpretador Javascript padrão;
  • Aplicações em React mais performática e uma melhor indexação de conteúdos pelos motores de busca do Google.
  • Sistema de roteamento da aplicação muito interessante e intuitivo, baseado nas páginas do projeto (Com suporte para rotas dinâmicas);
  • Suporte integrado para CSS e SASS ou qualquer biblioteca CSS-in-JS;
  • Permite construir rotas de API (endpoints) com funções serveless.

Tutorial

Para exemplificar o uso do Next.js, na prática, vamos construir uma aplicação em Next.js consumindo a API do The Movie DB, onde podemos listar os principais filmes da semana e buscar por um filme especifico, explorando uma das principais características do Next.js, o Server Side Rendering.

O projeto final criado neste tutorial está disponível no Github.

Iniciando no portal The Movie DB

  1. Crie uma conta no site do The Movie DB, caso já tenha uma conta efetue seu login:

  1. Para consultar a sua API key, acesse este link.

  2. Neste link podemos consultar a toda documentação. Para o desenvolvimento da nossa aplicação, utilizaremos apenas duas chamadas:

  • GET /trending/movie/week - Vamos listar os principais filmes da semana.
<https://api.themoviedb.org/3/trending/movie/week?api_key=><sua_api_key>&page=2&language=pt-BR
  • GET /search/movie - Faremos uma busca por um filme especifico.
<https://api.themoviedb.org/3/search/movie?api_key=><sua_api_key>&language=en-US&query=iron%20man&page=1&include_adult=false&language=pt-BR

Configurando a aplicação

1- Será necessário ter o Node.js instalado em sua versão 12.22.0 ou superior.

1.1- opcional – Instale o yarn. O Node.js possui um gerenciador de pacotes padrão chamado npm (Node Package Manager). Exemplos serão abordados utilizando ambos gerenciadores de pacotes.

  1. Para criar uma aplicação Next.js integrada ao typescript, utlize o seguinte comando:
npx [email protected] nome-do-projeto --ts
# ou
yarn create next-app nome-do-projeto --typescript

2.1 Inicie o projeto com comando yarn dev. A aplicação estará funcionando em http://localhost:3000

Estilizando os componentes

  1. Agora vamos estilizar nossos componentes. Em global.css, substitua a estilização com o seguinte CSS.
html,
body {
  padding: 0;
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
    Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
  background-color: #000000;
  background-repeat: no-repeat;
  background-size: cover;
  color: #ffff;
  background-image: linear-gradient(147deg, #000000 0%, #434343 74%);
}

a {
  color: inherit;
  text-decoration: none;
}

* {
  box-sizing: border-box;
}

1.1 Em Home.module.css, substitua a estilização pelo seguinte CSS:


.container {
  padding: 0 2rem;
  max-width: 1180px;
  margin: 0 auto;
}

.formSearch {
  display: flex;
  justify-content: center;
  margin-top: 150px;
}

.formSearch form {
  display: flex;
  justify-content: center;
}

.formSearch input {
  margin-bottom: 40px;
  height: 40px;
  width: 250px;
  border: 0;
  background-color: #ffffff;
  border-radius: 5px;
  padding: 0 20px;
  font-size: 16px;
  outline: 0;
}

.formSearch button {
  height: 40px;
  background-color: #13c680;
  font-weight: bold;
  color: #ffffff;
  padding: 0 10px;
  border: 0;
}

.formSearch button:hover {
  opacity: 0.5;
}

.titleContainer {
  display: flex;
  width: 100%;
  margin-bottom: 40px;
  justify-content: center;
}

.titleContainer h1 {
  font-size: 40px;
}

.moviesCointainer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}

.moviesCointainer span {
  color: yellow;
}

.paginationContainer {
  display: flex;
  justify-content: center;
  margin: 50px 0;
}

Criando nosso componente de paginação

  1. Instale as dependências do material-ui, com o seguinte comando:
yarn add @material-ui/core @material-ui/lab 

1.1 Agora vamos adicionar o componente Pagination. Crie uma pasta chamada components, e dentro de components adicione uma pasta chamada Pagination. Na pasta Pagination crie um arquivo chamado index.tsx, com o seguinte exemplo de código:

import React from "react";
import Pagination from "@material-ui/lab/Pagination";

//declaração de tipos das propriedades do component Pagination
interface IPropsComponent {
  handleChange:
    | ((event: React.ChangeEvent<unknown>, page: number) => void)
    | undefined;
  page: number;
  total_pages: number;
}

//Component pagination
export default function PaginationComponent({
  handleChange,
  page,
  total_pages,
}: IPropsComponent) {
  return (
    <div>
      <Pagination
        count={total_pages}
        page={page}
        color="secondary"
        onChange={handleChange}
      />
    </div>
  );
}

Utilizaremos este componente para podermos realizar a navegação entre os resultados de filmes retornados.

Integrando com API

  1. Adicione a biblioteca dotenv com o comando yarn add dotenv. Na raiz do projeto, crie um arquivo chamado .env e adicione sua API Key:
API_KEY=sua-api-key
  1. Crie uma pasta chamada config e adicione um arquivo chamado api.ts com a seguinte configuração:

export const args = {
  api_key: process.env.API_KEY,
  base_url: "<https://api.themoviedb.org/3>",
};
  1. Para que as imagens sejam renderizada, utilizando o componente Image do Next.js, adicione, no arquivo next.config.js, a seguinte configuração:
module.exports = {
  reactStrictMode: true,
  images: {
    domains: ['image.tmdb.org'], //dominio do The movie DB
  },
}
  1. Na pasta pages em index.tsx, substitua o código com o seguinte exemplo:
import React, { FormEvent, useEffect, useState } from "react";
import { useRouter } from "next/router";
import Head from "next/head";
import Image from "next/image";
import styles from "../styles/Home.module.css";
import Pagination from "../components/Pagination";
import { args } from "../config/api";

//declaração de tipos das propriedades que o component Home espera receber
interface IPropsComponent {
  list: any[];
  page: number;
  total_pages: number;
  search: boolean;
  searchParam: string;
}

const Home = ({ list, page, total_pages, searchParam }: IPropsComponent) => {
  //Inicizaliação do state que armazena os dados de lista de filmes retornados da api
  const [data, setData] = useState<any[]>([]);
  const router = useRouter();

  // Inicizaliação do state que armazena o valor do input search
  const [search, setSearch] = useState(searchParam);

  // Inicizaliação do state que armazena o valor da propriedade searchParam
  const [result, setResult] = useState<undefined | string>(undefined);

  const handleChange = (event: React.ChangeEvent<unknown>, value: number) => {
    //caso o valor do search foi setado no estado
    if (search) {
      // redireciona para pagina passando os parâmetros search e value
      return router.push(`?search=${search}&page=${value}`);
    } else {
      // redireciona para a mesma página passando o valor da página atual
      return router.push(`?page=${value}`);
    }
  };

  // Evento do form
  async function handleSearchMovie(e: FormEvent<HTMLFormElement>) {
    // desabilita o reload padrão do form
    e.preventDefault();
    // redireciona para a mesma página passando o query param search que foi setado no search
    return router.push(`/?search=${search}&page=1`);
  }

  useEffect(() => {
    // verifica se a alguma mudança no list, então é armazenado o valor do list novamente
    setData(list);
    // verifica se há alguma mudança de estado no searchParam, caso sim seta o valor novamente
    setResult(searchParam);
  }, [list, searchParam]);

  return (
    <div className={styles.container}>
      <Head>
        <title>NextJs Movies DB</title>
        <meta name="description" content="Generated by create next app" />
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <div>
        <div className={styles.formSearch}>
          <form onSubmit={handleSearchMovie}>
            <input
              type="text"
              placeholder="Procure por um filme, série..."
              onChange={(e) => setSearch(e.target.value)}
            />
            <button type="submit">Pesquisar</button>
          </form>
        </div>
        <div className={styles.titleContainer}>
          {result ? (
            <h1>Exibindo resultados para: {`${result}`}</h1>
          ) : (
            <h1>Filmes Populares</h1>
          )}
        </div>
        <div className={styles.moviesCointainer}>
          {data.map((item: any, index: number) => (
            <div key={index}>
              <Image
                src={`http://image.tmdb.org/t/p/original${item.poster_path}`}
                alt="image movie"
                width={350}
                height={400}
              />
              <div>
                {item.vote_average ? (
                  <p>
                    Nota: <span>{item.vote_average}</span>
                  </p>
                ) : (
                  <p>
                    Nota: <span>Sem avaliação</span>
                  </p>
                )}
              </div>
            </div>
          ))}
        </div>
      </div>
      <div className={styles.paginationContainer}>
        <Pagination
          total_pages={total_pages}
          page={page}
          handleChange={handleChange}
        />
      </div>
    </div>
  );
};

export default Home;

//função getServerSideProps será executado no lado do servidor, possibilitado o Server Side Rendering
export async function getServerSideProps({
  query, // query params
}: {
  query: {
    page?: string; //parâmetro page enviada na url
    search?: string; //parâmetro search page enviada na urr
  };
}) {
  if (query.search) {
    //É efetuada uma requisição GET para o servidor, retornando os dados do filme que foi passado no query param search
    const response = await fetch(
      `${args.base_url}/search/movie?api_key=${
        args.api_key // chave de acesso a API
      }&query=${
        query.search // query param search
      }&page=${
        query.page ? query.page : 1 // caso não tenha sido enviado o valor page, será definido o valor 1 por default
      }&language=pt-BR`
    );

    // desestruturação dos dodos retornados no response pegando os valores results, page, total_pages
    const { results, page, total_pages } = (await response.json()) as any;

    return {
      props: {
        list: results,
        page,
        total_pages,
        searchParam: query.search,
      },
    };
  } else {
    //É efetuada uma requisição GET para o servidor, retornando os dados de todos os filmes
    const response = await fetch(
      `${args.base_url}/trending/movie/week?api_key=${
        args.api_key // chave de acesso a API
      }&page=${
        query.page ? query.page : 1 // caso não tenha sido enviado o valor page, será definido o valor 1 por default
      }&language=pt-BR`
    );

    const { results, page, total_pages } = (await response.json()) as any;

    return {
      props: {
        list: results,
        page: page,
        total_pages: total_pages,
        searchParam: "",
      },
    };
  }
}

No exemplo acima, utilizamos o método GetServerSideProps, onde buscamos os dados da API ainda no lado do servidor antes de enviar para a página no lado do cliente. Com isso podemos otimizar o SEO, já que quando a página for montada em tela, será renderizada já com os dados que buscamos da API.

Utilizamos query params, para realizar as consultas a API, passando o parâmetro page enviando o número da página e o search consultando um filme especifico.

Testando a aplicação

1.- Em http://localhost:3000/ é exibido os principais filmes da semana, e também é possível navegar entre os resultados, através do componente de navegação.

1.2 Também é possível pesquisar por um filme especifico, no exemplo abaixo foram exibidos resultados para a consulta do filme "Iron Man"

Nesse artigo vimos um pouco sobre o framework Next.js, aprendemos a consultar os dados de uma API no lado do servidor com o método GetServerSideProps antes da página ser renderizada, ganhando em performance. Vimos algumas das principais características deste framework que ganhou grande espaço no desenvolvimento web.

O projeto final criado neste tutorial está disponível no Github.

Veja a documentação completa do Next.js


Quer escrever na Prensa?

Junte-se a uma comunidade de Creators que estão melhorando a internet com artigos inteligentes, relevantes e humanos. Além disso, seu artigo pode fazer parte do Projeto de Monetização, e você pode ganhar dinheiro com ele!

Clique aqui para se cadastrar e venha com a gente!


Topo