Pular para o conteúdo

Renderização no lado do Servidor

Renderização no lado do Servidor (SSR), se refere a gerar páginas HTML no servidor sob-demanda e então enviar elas para o cliente.

SSR te permite:

  • Implementar sessões para um estado de login no seu app.
  • Renderizar dados de uma API chamada dinamicamente com fetch.
  • Fazer deploy do seu site em uma hospedagem utilizando um adaptador.

Considere habilitar a renderização no lado do servidor em seu projeto do Astro se você precisa do seguinte:

  • Endpoints de API: SSR permite você criar páginas específicas que funcionam como endpoints de API para tarefas como acesso a banco de dados, autenticação e autorização enquanto mantém dados sensíveis ocultos do cliente.

  • Páginas protegidas: Se você precisa restringir o acesso a uma página baseado nas permissões do usuário, você pode habilitar SSR para lidar com o acesso do usuário no servidor.

  • Conteúdo que atualiza frequentemente: Habilitando SSR faz com que você gere páginas individuais sem exigir uma reconstrução estática no seu site. Isso é útil quando o conteúdo de uma página atualiza frequentemente.

Para habilitar funcionalidades do SSR em deploys de produção, atualize sua configuração output para 'server' ou 'hybrid' (introduzido na v2.6.0). Ambos os modos controlam quais páginas ou endpoints do servidor devem ser renderizadas pelo servidor. Cada opção de configuração possui um comportamento padrão, e permite que rotas individuais optem por sair do comportamento padrão de acordo:

  • output: 'server': Renderizado pelo servidor por padrão. Use esta opção quando a maior parte ou todo o seu site deve ser renderizado pelo servidor. Qualquer página individual ou endpoint pode optar em ser pré-renderizada.
astro.config.mjs
import { defineConfig } from 'astro/config';
import nodejs from '@astrojs/node';
export default defineConfig({
output: 'server',
adapter: nodejs(),
});
  • output: 'hybrid': Pré-renderiza para HTML por padrão. Use este opção quando a maior parte do seu site deve ser estático. Qualquer página individual ou endpoint pode optar em não ser pré-renderizada.

Você pode então optar por sair do comportamento de renderização padrão com uma declaração de exportação em qualquer página ou rota:

src/pages/minhapagina.astro
---
export const prerender = true;
// ...
---
<html>
<!-- Página estática, pré-renderizada aqui... -->
</html>

Veja mais exemplos de uso de como configurar rotas individuais

Convertendo um site estático para renderização híbrida

Seção intitulada Convertendo um site estático para renderização híbrida

Para converter um site Astro estático existente para permitir renderização híbrida, mude output para 'hybrid' e adicione um adaptador:

astro.config.mjs
import { defineConfig } from 'astro/config';
import nodejs from '@astrojs/node';
export default defineConfig({
adapter: nodejs({
mode: 'middleware' // or 'standalone'
}),
output: 'hybrid',
});

Quando for a hora de fazer deploy de um projeto SSR, você também vai precisar adicionar um adaptador. Isso porque SSR precisa de um runtime do servidor: o ambiente que executa o seu código no lado do servidor. Cada adaptador permite que o Astro retorne um script que executa seu projeto em um runtime específico.

Você pode encontrar ambos adaptadores SSR, oficias e da comunidade, no nosso diretório de integrações

Você pode adicionar qualquer um dos adaptadores de integração oficiais do Astro com o comando astro add. Ele irá instalar o adaptador e fazer as mudanças apropriadas em seu arquivo astro.config.mjs em uma só etapa. Por exemplo, para instalar o adaptador para Vercel, execute:

Terminal window
npx astro add vercel

Você também pode adicionar um adaptador manualmente instalando o pacote e atualizando astro.config.mjs sozinho. (Veja os links acima para instruções especificadas de cada adaptador para completar as etapas necessárias para habilitar SSR.) Utilizando meu-adaptador como um exemplo, as instruções vão se parecer com isso:

  1. Instale o adaptador nas dependências do seu projetado utilizando seu gerenciador de pacotes preferido:
Terminal window
npx astro add netlify
  1. Adicione o adaptador no import e default export do seu arquivo astro.config.mjs

    astro.config.mjs
    import { defineConfig } from 'astro/config';
    import meuAdaptador from '@astrojs/meu-adaptador';
    export default defineConfig({
    output: 'server',
    adapter: meuAdaptador(),
    });

O Astro continuará sendo um gerador de sites estáticos por padrão. Porém, quando você habilita a renderização no lado do servidor e adiciona um adaptador, algumas novas funcionalidades são disponibilizadas a você.

Ambos os modos server e hybrid permitem páginas e endpoints renderizados pelo servidor e vão renderizar todas as páginas de acordo com seu comportamento padrão. Entretanto, ambos os modos permitem que você marque uma página individual para optar em sair desse comportamento padrão.

Optando por sair da renderização pelo servidor

Seção intitulada Optando por sair da renderização pelo servidor

Para uma aplicação majoritariamente renderizada pelo servidor configurada como output: server, adicione export const prerender = true para qualquer página ou rota para pré-renderizar uma página ou endpoint:

src/pages/minhapagina.astro
---
export const prerender = true;
// ...
---
<html>
<!-- Página estática, pré-renderizada aqui... -->
</html>
src/pages/minhapagina.mdx
---
layout: '../layouts/markdown.astro'
title: 'Minha página'
---
export const prerender = true;
# Esta é minha página estática, pré-renderizada

E para um endpoint:

src/pages/meuendpoint.js
export const prerender = true;
export async function GET() {
return {
body: JSON.stringify({ message: `Este é meu endpoint estático` }),
};
}

Optando por sair da pré-renderização

Seção intitulada Optando por sair da pré-renderização

Para um aplicação majoritariamente estática configurada como output: hybrid, adicione export const prerender = false a qualquer arquivo que deve ser renderizado pelo servidor:

src/pages/numeroaleatorio.js
export const prerender = false;
export async function GET() {
let numero = Math.random();
return {
body: JSON.stringify({ numero, mensagem: `Aqui está um número aleatório: ${numero}` }),
};
}

Os cabeçalhos de uma requisição estão disponíveis em Astro.request.headers, que funciona como o Request.headers do navegador. Ele é um objeto Headers, parecido com um Map, onde você pode pegar cabeçalhos como o cookie.

src/pages/index.astro
---
const cookie = Astro.request.headers.get('cookie');
// ...
---
<html>
<!-- Página aqui... -->
</html>

O método HTTP usado na requisição está disponível em Astro.request.method, que funciona como o Request.method do navegador. Ele retorna a representação em string do método HTTP usado na requisição.

src/pages/index.astro
---
console.log(Astro.request.method) // GET (quando navegado para cá no navegador)
---

Este é um utilitário para ler e modificar um único cookie. Ele te permite verificar, definir, pegar e deletar um cookie.

Para mais detalhes sobre Astro.cookies e o tipo AstroCookie na referência da API.

O exemplo abaixo atualiza o valor de um cookie da contagem de visualizações da página.

src/pages/index.astro
---
let contagem = 0
if (Astro.cookies.has("contagem")) {
const cookie = Astro.cookies.get("contagem")
contagem = cookie.number() + 1
}
Astro.cookies.set("contagem", contagem)
---
<html>
<h1>Contagem = {contagem}</h1>
</html>

Você também consegue retornar uma Response de qualquer página. Você talvez faça isso para retornar um 404 em uma página dinâmica após verificar um id em um banco de dados.

src/pages/[id].astro
---
import { getProduto } from '../api';
const produto = await getProduto(Astro.params.id);
// O produto não foi encontrado
if (!produto) {
return new Response(null, {
status: 404,
statusText: 'Não encontrado'
});
}
---
<html>
<!-- Página aqui... -->
</html>

Um endpoint do servidor, também conhecido como rota de API, é um arquivo .js ou .ts dentro da pasta src/pages. A função recebe um Request e retorna uma Response. Sendo uma poderosa funcionalidade de SSR, rotas de API são capazes de executar código no lado do servidor de forma segura. Para aprender mais, veja nosso Guia de Endpoints.

Os navegadores nativamente suportam streaming HTTP, onde um documento é quebrado em pedaços, enviado para a rede em ordem, e renderizado na página naquela ordem.

Durante esse processo, os navegadores consomem o HTML incrementalmente: analisando, renderizando para o DOM e desenhando. Isso acontece independente de você fazer stream do seu HTML intencionalmente ou não. As condições da rede podem causar que documentos grandes sejam baixados lentamente, e esperar por requisições de dados pode bloquear a renderização da página.

Usando streaming para melhorar o desempenho da página

Seção intitulada Usando streaming para melhorar o desempenho da página

A página a seguir espera por alguns dados no seu frontmatter usando await. O Astro vai esperar até que todas as chamadas fetch tenham resolvido antes de enviar qualquer HTML para o navegador.

src/pages/index.astro
---
const respostaPessoa = await fetch('https://randomuser.me/api/');
const dadosPessoa = await respostaPessoa.json();
const pessoaAleatoria = dadosPessoa.results[0];
const respostaFato = await fetch('https://catfact.ninja/fact');
const dadosFato = await respostaFato.json();
---
<html>
<head>
<title>Um nome e um fato</title>
</head>
<body>
<h2>Um nome</h2>
<p>{pessoaAleatoria.name.first}</p>
<h2>Um fato</h2>
<p>{dadosFato.fact}</p>
</body>
</html>

Mover as chamadas com await para componentes menores permite que você tome vantagem do streaming do Astro. Usando os seguintes componentes para executar as requisições de dados, o Astro pode renderizar um pouco de HTML primeiro, como o título, e então os parágrafos quando os dados estiverem prontos.

src/components/NomeAleatorio.astro
---
const respostaPessoa = await fetch('https://randomuser.me/api/');
const dadosPessoa = await respostaPessoa.json();
const pessoaAleatoria = dadosPessoa.results[0];
---
<p>{pessoaAleatoria.name.first}</p>
src/components/FatoAleatorio.astro
---
const respostaFato = await fetch('https://catfact.ninja/fact');
const dadosFato = await respostaFato.json();
---
<p>{dadosFato.fact}</p>

A página do Astro abaixo usando esses componentes pode renderizar parte da página mais cedo. As tags <head>, <body> e <h1> não são mais bloqueadas pelas requisições de dados. O servidor vai requisitar dados para NomeAleatorio e FatoAleatorio em paralelo e fazer stream do HTML resultante para o navegador.

src/pages/index.astro
---
import NomeAleatorio from '../components/NomeAleatorio.astro'
import FatoAleatorio from '../components/FatoAleatorio.astro'
---
<html>
<head>
<title>Um nome e um fato</title>
</head>
<body>
<h2>Um nome</h2>
<NomeAleatorio />
<h2>Um fato</h2>
<FatoAleatorio />
</body>
</html>

Você também pode incluir promises diretamente no template. Ao invés de bloquear um componente inteiro, isso vai resolver a promise em paralelo e só bloquear a marcação que vier depois disso.

src/pages/index.astro
---
const pessoaPromise = fetch('https://randomuser.me/api/')
.then(resposta => resposta.json())
.then(arr => arr[0].name.first);
const fatoPromise = fetch('https://catfact.ninja/fact')
.then(resposta => resposta.json())
.then(dadosFato => dadosFato.fact);
---
<html>
<head>
<title>Um nome e um fato</title>
</head>
<body>
<h2>Um nome</h2>
<p>{pessoaPromise}</p>
<h2>Um fato</h2>
<p>{fatoPromise}</p>
</body>
</html>

Nesse exemplo, Um nome vai renderizar enquanto pessoaPromise e fatoPromise estão carregando. Quando pessoaPromise tiver resolvido, Um fato irá aparecer e fatoPromise vai renderizar quando tiver terminado de carregar.