Renderizado en el servidor
El renderizado en el lado del servidor (SSR) se refiere a la generación de páginas HTML en el servidor bajo demanda y enviarlas al cliente.
SSR te permite:
- Implementar sesiones para iniciar sesión en tu aplicación.
- Renderizar datos desde una llamada de API dinámicamente con
fetch
. - Desplegar tu sitio en un servidor usando un adaptador.
Considera habilitar el renderizado en el lado del servidor (SSR) en tu proyecto de Astro si necesitas lo siguiente:
-
Endpoints de API: SSR te permite crear páginas específicas que funcionan como endpoints de API para tareas como el acceso a bases de datos, la autenticación y la autorización, al tiempo que mantienen oculta la información confidencial del cliente.
-
Páginas protegidas: Si necesitas restringir el acceso a una página según los privilegios del usuario, puedes habilitar SSR para manejar el acceso del usuario en el servidor.
-
Contenido que cambia con frecuencia: Habilitar SSR te permite generar páginas individuales sin necesidad de reconstruir estáticamente todo tu sitio. Esto es útil cuando el contenido de una página se actualiza con frecuencia.
Habilitando SSR en tu proyecto
Sección titulada Habilitando SSR en tu proyectoPara hablitar las características SSR para despliegues en producción, actualiza tu configuración output
a 'server'
o 'hybrid'
(introducido en v2.6.0). Ambos modos controlan cuales páginas o endpoints del servidor deben ser renderizados en el servidor. Cada opción de configuración tiene un comportamiento por defecto diferente, y permite que las rutas individuales opten por no utilizar el valor por defecto:
output: 'server'
: Renderizado en el servidor por defecto. Utiliza esto cuando la mayoría o todo tu sitio debe ser renderizado en el servidor. Cualquier página o endpoint individual puede optar por pre-renderizar.output: 'hybrid'
: Pre-renderizado a HTML por defecto. Utiliza esto cuando la mayoría de tu sitio debe ser estático. Cualquier página o endpoint individual puede optar por no pre-renderizar.
import { defineConfig } from 'astro/config';import nodejs from '@astrojs/node';
export default defineConfig({ output: 'server', adapter: nodejs(),});
Puedes optar por no utilizar el comportamiento de renderizado por defecto con una declaración de exportación en cualquier página o ruta:
---export const prerender = true;// ...---<html> <!-- Estático, página pre-renderizada aquí... --></html>
Ver más ejemplos de uso de configuración de rutas individuales
Convirtiendo un sitio estático a renderizado híbrido
Sección titulada Convirtiendo un sitio estático a renderizado híbridoPara convertir un sitio Astro estático existente para permitir el renderizado híbrido, cambia la opción output
a 'hybrid'
y añade un adaptador:
import { defineConfig } from 'astro/config';import nodejs from '@astrojs/node';
export default defineConfig({ adapter: nodejs({ mode: 'middleware' // or 'standalone' }), output: 'hybrid',});
Añadiendo un Adaptador
Sección titulada Añadiendo un AdaptadorCuando sea el momento de desplegar un proyecto SSR, vas a necesitar añadir un adaptador. Esto es porque SSR requiere un servidor en tiempo de ejecución: el ambiente que ejecuta tu código en el lado del servidor. Cada adaptador le permite a Astro entregar un script que ejecuta tu proyecto en un ambiente específico.
Puedes encontrar tanto adaptadores SSR oficiales como de la comunidad en nuestro directorio de integraciones.
Instalación usando astro add
Sección titulada Instalación usando astro addPuedes añadir cualquiera de los integraciones oficiales del adaptador de Astro con el comando astro add
. Esto instalará el adaptador y hará los cambios apropiados a tu archivo astro.config.mjs
en un solo paso. Por ejemplo, para instalar el adaptador de Vercel, ejecuta:
npx astro add vercel
pnpm astro add vercel
yarn astro add vercel
Instalación Manual
Sección titulada Instalación ManualTambién puedes añadir un adaptador manualmente instalando el paquete y actualizando astro.config.mjs
tú mismo. (Mira los enlaces debajo para instrucciones específicas de cada adaptador y completar los pasos para habilitar SSR.) Usando mi-adaptador
como ejemplo, las instrucciones serían:
-
Instala el adaptador a las dependencias de tu proyecto usando tu gestor de paquetes preferido:
Ventana de terminal npm install @astrojs/mi-adaptadorVentana de terminal pnpm install @astrojs/mi-adaptadorVentana de terminal yarn add @astrojs/mi-adaptador -
Añade el adaptador a tu archivo de configuración
astro.config.mjs
de la siguiente forma.astro.config.mjs import { defineConfig } from 'astro/config';import miAdaptador from '@astrojs/mi-adaptador';export default defineConfig({output: 'server',adapter: miAdaptador(),});
Características
Sección titulada CaracterísticasAstro seguirá siendo un generador de sitios estáticos por defecto. Pero una vez que habilites el renderizado en el servidor y agregues un adaptador, algunas características nuevas estarán disponibles para ti.
Configuración de rutas individuales
Sección titulada Configuración de rutas individualesAmbos modos server
e hybrid
permiten páginas y endpoints renderizados en el servidor y renderizarán todas las páginas de acuerdo a su comportamiento por defecto. Sin embargo, ambos modos permiten marcar una página individual para optar por no utilizar este comportamiento por defecto.
Optar por no renderizar en el servidor
Sección titulada Optar por no renderizar en el servidorPara una aplicación principalmente renderizada en el servidor configurada como output: server
, añade export const prerender = true
a cualquier página o ruta para pre-renderizar una página o endpoint estático:
---export const prerender = true;// ...---<html> <!-- Estático, página pre-renderizada aquí... --></html>
---layout: '../layouts/markdown.astro'title: 'Mi página'---export const prerender = true;# Esta es mi página estática, pre-renderizada
Y para un endpoint:
export const prerender = true;export async function GET() { return { body: JSON.stringify({ message: `Este es mi endpoint estático` }), };}
Optar por no pre-renderizar
Sección titulada Optar por no pre-renderizarPara un sitio principalmente estático configurado como output: hybrid
, añade export const prerender = false
a cualquier archivo que debería ser renderizado en el servidor:
export const prerender = false;export async function GET() { let number = Math.random(); return { body: JSON.stringify({ number, message: `Aquí hay un número al azar: ${number}` }), };}
Astro.request.headers
Sección titulada Astro.request.headersLos encabezados de la solicitud están disponibles en Astro.request.headers
. Esto funciona de manera similar a Request.headers
en el navegador. Es un objeto Headers, es un objeto similar a un Map donde puedes recuperar encabezados como la cookie.
---const cookie = Astro.request.headers.get('cookie');// ...---<html> <!-- Maquetado aquí... --></html>
Astro.request.method
Sección titulada Astro.request.methodEl método HTTP utilizado en la solicitud está disponible como Astro.request.method
. Esto funciona de manera similar a Request.method
. en el navegador. Devuelve la representación en cadena del método HTTP utilizado en la solicitud.
---console.log(Astro.request.method) // GET (cuando se navega en el navegador)---
Astro.cookies
Sección titulada Astro.cookiesEsta es una utilidad para leer y modificar una sola cookie. Te permite comprobar, establecer, obtener y eliminar una cookie.
Ve más detalles sobre Astro.cookies
y el tipo AstroCookie
en la referencia de la API.
El ejemplo a continuación actualiza el valor de una cookie para un contador de vistas de página.
---let counter = 0
if (Astro.cookies.has("counter")) { const cookie = Astro.cookies.get("counter") counter = cookie.number() + 1}
Astro.cookies.set("counter",counter)---<html> <h1>Contador = {counter}</h1></html>
Astro.redirect
Sección titulada Astro.redirectEn el objeto global Astro
, este método te permite redirigir a otra página. Puedes hacer esto después de verificar si el usuario ha iniciado sesión obteniendo la sesión desde una cookie.
---import { isLoggedIn } from '../utils';
const cookie = Astro.request.headers.get('cookie');
// Si el usuario no ha iniciado sesión, redirígelo a la página de inicio de sesión.if (!isLoggedIn(cookie)) { return Astro.redirect('/login');}---<html> <!-- Maquetado aquí... --></html>
Response
Sección titulada ResponseTambién puedes devolver una Response desde cualquier página. Puedes hacer esto para devolver un 404 en una página dinámica luego de buscar y no encontrar la id en la base de datos.
---import { getProduct } from '../api';
const product = await getProduct(Astro.params.id);
// Producto no encontradoif (!product) { return new Response(null, { status: 404, statusText: 'No encontrado' });}---<html> <!-- Maquetado aquí... --></html>
Server Endpoints
Sección titulada Server EndpointsUn server endpoint, también conocido como una ruta API, es una función exportada desde un archivo .js
o .ts
dentro de la carpeta src/pages/
.
La función recibe un contexto del endpoint y devuelve una Response. Una característica poderosa de SSR, las rutas API son capaces de ejecutar código de forma segura en el servidor. Para aprender más, consulta nuestra guía de endpoints.
Streaming
Sección titulada StreamingLos navegadores admiten de forma nativa la transmisión HTTP, donde un documento se divide en fragmentos, se envía por la red en orden y se representa en la página en ese mismo orden.
Durante este proceso, los navegadores consumen HTML de forma incremental: analizando, representando en el DOM y pintando en la pantalla. Esto ocurre tanto si se transmite intencionalmente el HTML como si no. Las condiciones de la red pueden hacer que los documentos grandes se descarguen lentamente, y esperar a que se recuperen los datos puede bloquear la representación de la página.
Utilizando streaming para mejorar el rendimiento de la página
Sección titulada Utilizando streaming para mejorar el rendimiento de la páginaLa siguiente página utiliza await
para obtener algunos datos en su frontmatter. Astro esperará a que todas las llamadas fetch
se resuelvan antes de enviar cualquier HTML al navegador.
---const personResponse = await fetch('https://randomuser.me/api/');const personData = await personResponse.json();const randomPerson = personData.results[0];const factResponse = await fetch('https://catfact.ninja/fact');const factData = await factResponse.json();---<html> <head> <title>Un nombre y un hecho</title> </head> <body> <h2>Un nombre</h2> <p>{randomPerson.name.first}</p> <h2>Un hecho</h2> <p>{factData.fact}</p> </body></html>
Mover las llamadas await
a componentes más pequeños te permite aprovechar el streaming de Astro. Utilizando los siguientes componentes para realizar las recuperaciones de datos, Astro puede representar primero algo de HTML, como el título, y luego los párrafos cuando los datos estén listos.
---const personResponse = await fetch('https://randomuser.me/api/');const personData = await personResponse.json();const randomPerson = personData.results[0];---<p>{randomPerson.name.first}</p>
---const factResponse = await fetch('https://catfact.ninja/fact');const factData = await factResponse.json();---<p>{factData.fact}</p>
La página Astro a continuación, utilizando estos componentes, puede renderizar partes de la página más rápido. Las etiquetas <head>
, <body>
y <h1>
ya no se bloquean por las solicitudes de datos. El servidor luego obtendrá los datos RandomName
y RandomFact
en paralelo y transmitirá el HTML resultante al navegador.
---import RandomName from '../components/RandomName.astro'import RandomFact from '../components/RandomFact.astro'---<html> <head> <title>Un nombre y un hecho</title> </head> <body> <h2>Un nombre</h2> <RandomName /> <h2>Un hecho</h2> <RandomFact /> </body></html>
Incluyendo promesas directamente
Sección titulada Incluyendo promesas directamenteTambién puedes incluir promesas directamente en la plantilla. En lugar de bloquear todo el componente, se resolverá la promesa en paralelo y solo se bloqueará el marcado que viene después de ella.
---const personPromise = fetch('https://randomuser.me/api/') .then(response => response.json()) .then(arr => arr[0].name.first);const factPromise = fetch('https://catfact.ninja/fact') .then(response => response.json()) .then(factData => factData.fact);---<html> <head> <title>Un nombre y un hecho</title> </head> <body> <h2>Un nombre</h2> <p>{personPromise}</p> <h2>Un hecho</h2> <p>{factPromise}</p> </body></html>
En este ejemplo, Un nombre
se renderizará mientras se cargan personPromise
y factPromise
.
Una vez que personPromise
se haya resuelto, aparecerá Un hecho
y factPromise
se mostrará cuando haya terminado de cargarse.