Skip to content

Demonstração de uso e notações utilizadas pelo Next para pastas, arquivos e como isso impacta no roteamento

License

Notifications You must be signed in to change notification settings

ananuness/next-demo

Repository files navigation

⚙️ Next Demo

Demonstração de uso e notações utilizadas pelo Next 13 para pastas, arquivos e como impacta no roteamento.

📑 Conteúdos

🛠️ Rodando o projeto

Após baixar o projeto:

# instale as dependências
npm i
# or
yarn
# or
pnpm i

# rode o projeto em dev mode
npm run dev
# or
yarn dev
# or
pnpm dev

🗃️ Organizando a aplicação

O Next permite com que você siga sua própria organização de projeto, porém, tomando cuidado com as notações utilizadas pelo framework para mapear rotas através da organização das pastas no diretório principal, o app, além de definir diferentes tipos de tela baseando-se nos nomes dos arquivos.

Organização de arquivos

O Next tem uma nomenclatura de nomes de arquivos de acordo com sua utilidade, são elas:

nome do arquivo função
error define uma página de erro inesperado para uma determinada rota e seus filhos
layout define uma página a ser compartilhada por mais de uma rota
loading define uma página de carregamento utilizando o Suspense
not-found define uma página de not found além do next retornar o erro 404
page define uma página única para uma rota
route define as requests externas de determinada rota, não podendo estar no mesmo nível de um arquivo page
template são similares aos layouts no que se refere a envolver layouts ou pages, mas criam uma nova instância paga cada "filho", estados não são preservados e efeitos são ressincronizados. Por isso, são usados em casos específicos

⚠️ As funções retornadas por essas páginas apenas devem ser feitas com export default, sem isso, a aplicação quebra. O mesmo que acontece o root layout e o root page.

Organização de pastas

No diretório principal, podemos criar pastas que irão definir as rotas da nossa aplicação, por exemplo:

rotas roteáveis

Observe que os nomes das pastas e como estão aninhadas definem os nomes das rotas. Além disso, quando não informamos um arquivo page ou route, a pasta não será roteada, pois apenas o conteúdo retornado por um arquivo page ou route que será enviado para o client:

rotas não roteáveis

Isso significa que qualquer arquivo que não for nomeado como page ou route não será roteável e se não for um arquivo com um dos nomes especiais, também não irá ter uma finalidade determinada para o Next.

pastas

Opções de organização de pasta

Como foi dito, se um arquivo não carrega nenhum dos nomes reservados para uma determinada funcionalidade no Next, ele será apenas um arquivo, mas o framwork oferece algumas opções de organização de pastas:

  • Pastas privadas: são pastas que serão ignoradas pelo sistema de roteamento do Next e podem ser criadas colocando underline na frente do nome, como:

pastas privadas

  • Grupos de rotas: caso queira trazer uma organização além de separar as pastas por rotas, podemos fazer a separação por pastas que não serão adicionadas nas URLs geradas. Para isso, devemos colocar o nome da pasta entre parênteses:

grupo de rotas

⚠️ Lembrando que essas convenções não são obrigatórias, mas podem ajudar na organização e não ficar dependendo de lembrar as notações nomeação reservadas para arquivos especiais do Next.

Rotas Dinâmicas

Quando você não conhece os segmentos exatos de uma rota com antecedência e deseja criar rotas a partir de dados dinâmicos, pode usar segmentos dinâmicos que são preenchidos no momento da solicitação ou pré-renderizados no momento do build da aplicação.

Uma rota dinâmica pode ser criada seguindo a notação: [nome_da_pasta], como [id], [slug] etc. Esses segmentos são passados através da prop params para as funções de layout, page, route e generateMetadata.

Por exemplo, um blog poderia incluir a seguinte rota: app/blog/[slug]/page.tsx, na qual slug é o segmento dinâmico para os posts do blog:

Rota URL params
app/blog/[slug]/page.js /blog/a { slug: 'a' }
interface PageProps {
  params: {
    slug: string;
  };
}

export default function Page({ params }: PageProps) {
  return <div>My Post: {params.slug}</div>;
}

Rotas Paralelas

O roteamento paralelo permite que você, simultaneamente ou condicionalmente, renderize uma ou mais páginas no mesmo layout. Por exemplo, você pode renderizar, simultaneamente, as páginas de team e analytics:

rotas paralelas

Como observado, a notação para criar uma rota paralela é: @nome_da_pasta. Além disso, essa estrutura de pastas permite com que o componente em layout.js aceite @team e @analytics slot props e possa renderizá-las em paralelo juntamente com a prop children:

export default function Layout(props: {
  children: React.ReactNode;
  analytics: React.ReactNode;
  team: React.ReactNode;
}) {
  return (
    <>
      {props.children}
      {props.team}
      {props.analytics}
    </>
  );
}

💡 Note que a prop children é um slot implícito que não precisa estar ligado a uma pasta. Isso significa que app/page.js é equivalente a app/@children/page.js.

Esse tipo de rota também permite com que possamos renderizar rotas condicionalmente baseada em certas condições, como o estado de autenticação do usuário:

rotas paralela condicional

📡 Server Side Rendering

A ideia de renderizar uma aplicação do lado do seridor veio para ajudar a resolver problemas antes enfrentados por Single Page Applications (SPAs):

  • Reduzindo javascript do lado do browser
  • Gerando mais compatibilidade com dispositivos e browsers diferentes
  • Permitir várias otimizações, como fazer o cache de uma página, proporcionando um carregamento mais rápido, otimizar a renderização de imagens, recursos para SEO, além de recursos para Edge computing.

A computação de borda (Edge computing) é um paradigma de computação distribuída que aproxima a computação e o armazenamento de dados das fontes de dados. Espera-se que isso melhore os tempos de resposta e economize largura de banda.

Por ter um lado servidor, é possível trabalhar com lógicas, como acesso direto ao banco de dados e até criação de APIs Rest. E é muito utilizado para ser um Backend for Frontend (BFF), pois permite que as necessidades do front sejam supridas pelo próprio back do Next.

Server Components vs Client Components

Os Server Components são gerados totalmente pelo lado do servidor, eles não têm nenhuma interatividade quando renderizado no navegador.

Já o Client Component, serve para suprir demandas que iremos precisar de interatividade e manipular no navegador, como um componente de vídeo, que irá ser pré-renderizado no servidor, mas terá uma parte javascript que será rodada no navegador, ou seja, se for necessário contato com APIs do navegador, então um Client Component é ideal. As vantagens:

  • bundle js menor
  • carregamento inicial mais rápido
  • cacheamento em nível de componente
  • chamadas externas em paralelo

📥 Data Fetching

Data fetching é uma parte essencial em qualquer aplicação, aqui será mais explicado sobre fetch no servidor e fetch no client via Router Handler. Mas antes disso, vamos ver sobre cache e revalidating, que são conceitos essenciais para o entendimento do fetch no server.

Caching

Por padrão, qualquer requisição de dados já será cacheada pelo Next, garantindo que não haja necessidade de fazer um fetch a cada nova requisição, persistindo os dados no Data Cache embutido existente.

Agora, como o Data Cache funciona?

  • A primeira vez que há uma requisição durante a renderização, o Next checa o Data Cache para obter alguma resposta já cacheada;
  • Se encontra ele retorna esse valor imediatamente e já memoizado.
  • Caso não encontre, a requisição é feita e o resultado é guardado e memoizado nesse Data Cache.
  • Para dados não cacheados, o resultado é sempre trazido do data source e ainda memoizado.

Observe que independente dos dado serem cacheados ou não, as requisições sempre serão memoizadas para evitar requisições duplicadas durante as renderizações.

Revalidating

O cache de dados (Data Cache) persiste nas requisições e deployments recebidos, a menos que você revalide ou recuse. Falando da revalidação, ela pode ser feita de duas maneiras:

  • Baseada no tempo: revalida os dados em intervalos de tempo pré-determinados. Isso é útil para dados que mudam com pouca frequência e a atualização não é tão crítica.
// Revalida a cada uma hora
fetch("https://...", { next: { revalidate: 3600 } });

E claro, para recusar o cache utilizando a opção:

// Recusando o caching para uma requisição fetch individual
fetch(`https://...`, { cache: "no-store" });

Busca de dados no cliente

Se você precisa fazer um fetch em um Client Component, há três alternativas:

  • Chamar um Route Handler. Eles são executados no servidor e retornam os dados para o cliente, sendo útil para quando você não quer expor informações sensíveis para o cliente como API tokens;
  • Utilizar libs como SWR ou React Query.

Busca de dados no servidor

O Next estende a fetch API nativa para permitir o usuário configurar o caching e revalidating para cada request no servidor. E o React estende o fetch para memoizar as requests automaticamente enquanto renderiza componentes react.

Pode-se usar o fetch com async/await em Server Components diretamente, mas é recomendado a utilização de Route Handlers ou Server Actions. Aqui o foco será os Route Handlers.

async function getData() {
  const res = await fetch("https://api.example.com/...");
  // O valor retornado não é serializado, você pode
  // retornar um Date, Map, Set etc.

  if (!res.ok) {
    // Isso vai ativar o `error.js` Error Boundary
    // mais próximo
    throw new Error("Failed to fetch data");
  }

  return res.json();
}

export default async function Page() {
  const data = await getData();

  return <main></main>;
}

⚠️ Bom saber:

  • Em Route Handlers, as requisições não são memoizdas, já que esses handlers não fazem parte dos componentes react;
  • Para usar async/await em Server Components com TypeScript, é necessário utilizar o ts 5.1.3 ou maior e @types/react 18.2.8 ou maior.

Route Handlers

Esses arquivos permitem você criar manipuladores de requisições customizados para uma dada rota, usando as APIs Web Request e Response.

Route handler

Route Handlers só são disponíveis dentro da pasta app. Eles são equivalentes às API Routes dentro de pages, então é necessário utilizar os dois juntos.

Convenção

Route Handlers são definidos em um arquivo route.js|ts dentro da pasta app:

export async function GET(request: Request) {}

Eles podem ser organizados de forma similar aos arquivos de page.ts e layout.ts, mas não pode ter um arquivo route.ts no mesmo nível que uma page.

Já os métodos suportados são: GET, POST, PUT, PATCH, DELETE, HEAD e OPTIONS. Além disso, as Requests e Responses nativas são estendidas pelo Next por NextRequest e NextResponse para prover helpers para casos de uso avançados.

Exemplos de utilização

  • CORS
export async function GET(request: Request) {
  return new Response("Hello, Next.js!", {
    status: 200,
    headers: {
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS",
      "Access-Control-Allow-Headers": "Content-Type, Authorization",
    },
  });
}
  • GET
import { NextResponse } from "next/server";

export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const id = searchParams.get("id");

  const res = await fetch(`https://data.mongodb-api.com/product/${id}`, {
    headers: {
      "Content-Type": "application/json",
      "API-Key": process.env.DATA_API_KEY,
    },
  });

  const product = await res.json();

  return NextResponse.json({ product });
}
  • POST
import { NextResponse } from "next/server";

export async function POST() {
  const res = await fetch("https://data.mongodb-api.com/...", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "API-Key": process.env.DATA_API_KEY,
    },
    body: JSON.stringify({ time: new Date().toISOString() }),
  });

  const data = await res.json();

  return NextResponse.json(data);
}
  • Cookies
import { cookies } from "next/headers";

export async function GET(request: Request) {
  const cookieStore = cookies();
  const token = cookieStore.get("token");

  return new Response("Hello, Next.js!", {
    status: 200,
    headers: { "Set-Cookie": `token=${token.value}` },
  });
}
  • Headers
import { headers } from "next/headers";

export async function GET(request: Request) {
  const headersList = headers();
  const referer = headersList.get("referer");

  return new Response("Hello, Next.js!", {
    status: 200,
    headers: { referer: referer },
  });
}
  • Redirects
import { redirect } from "next/navigation";

export async function GET(request: Request) {
  redirect("https://nextjs.org/");
}
  • Rotas dinâmicas
interface DynamicParams {
  params: {
    slug: string;
  };
}

export async function GET(request: Request, { params }: DynamicParams) {
  const slug = params.slug; // 'a', 'b', or 'c'
}

🚧 Readme em construção 👷‍♀️

🌱 Para saber mais


Feito com 🤍 por Ana Beatriz Nunes

About

Demonstração de uso e notações utilizadas pelo Next para pastas, arquivos e como isso impacta no roteamento

Topics

Resources

License

Stars

Watchers

Forks