¿Cómo crear una página web con ISR? y por qué lo usamos en EDteam

En una de las campañas de EDteam tuvimos problemas con el rendimiento de nuestra web e identificamos 2 problemas. La página en ocasiones hacía peticiones innecesarias y había mucha duda técnica. ¿Cómo lo resolvimos? Te lo cuento en este post.

Diseño web
8 minutos
Hace 4 años
¿Cómo crear una página web con ISR? y por qué lo usamos en EDteam

¡Accede a cientos de cursos
con solo un pago al año!

Blog Microbanner

En EDweekend tuvimos serios problemas de rendimiento que decidimos mejorar, antes nuestro método de rendering era SSR (Server Side Rendering) el cual por naturaleza del mismo hacia las peticiones necesarias para mostrar la información cada que un usuario entraba a la página, pero encontramos dos problemas.

  1. la página en ocaciones hacía peticiones innecesarias
  2. Había mucha deuda técnica

Cómo lo resolvimos? principalmente cambiando 2 cosas, la estructura de la aplicación y el método de rendering.

Estructura de la aplicación

Decidimos dividir nuestra página web en dos, ed.team y app.ed.team, en ed.team está todo el contenido que puede ser público, es decir que no hay necesidad de iniciar sesión para poder interactuar en ella y en app.ed.team está todo el contenido que sí necesita de un inicio de sesión para poder interactuar en ella, esto con el fin de aminorar la carga y dividir estrategias como el SEO, en app.ed.team no hay necesidad de meter mucho SEO, ya que al final es una aplicación privada que necesita de un inicio de sesión para poder acceder a sus recursos, en cambio en ed.team sí es necesario, ya que es la parte pública y es la que tiene el dominio que todos conocemos.

Método de rendering

Decidimos cambiar nuestra estrategia de renderizado a ISR (Incrementral Static Regeneration) el cual nos permitirá aminorar la carga de peticiones al servidor y al devolver sólo archivos estáticos con páginas ya generadas, la velocidad de la web aumentó y los tiempos de carga disminuyeron, pero qué es ISR?, bien lo explico.

Qué es ISR

ISR (Incremental Static Regeneration) es un método de rendering que te permite utilizar la generación estática por página sin necesidad de realizar un rebuild de todo la aplicación, es decir que las páginas estáticas pueden ser generadas en demanda en vez de solo al momento de hacer el build. Esto nos sirve mucho cuando queremos conservar las ventajas de los sitios estáticos en páginas que requieren información que es cambiante en un intervalo de tiempo.

Intervalo de tiempo

Cómo se mencionó las páginas son regeneradas en un intervalo de tiempo que nosotros le indicamos a cada página de Next.js, este tiempo puede ser 1 hora, 12 horas, 24 horas o el tiempo en el que necesites que se vuelva a generar la página, si este tiempo de revalidación no se le indica en una página esta permanecerá estática y sin cambios hasta que se vuelva a hacer otro build de la aplicación.

Cómo funciona ISR

Suponiendo que el tiempo de revalidación que establecemos es de 60 segundos.

  • En tiempo de build nosotros generamos las páginas que le indiquemos a Next.js junto a su tiempo de revalidación y estas son cacheadas en el servidor
  • En las peticiones a la página que suceden antes del tiempo de revalidación se responderá con las páginas que están cacheadas en el servidor
  • Una vez que pase el tiempo de revalidación y se haga una petición, Next.js mostrará la página cacheada (obsoleta) y por debajo regenerará esa misma página
  • Cuando la regeneración de la página haya concluido con éxito esta sobrescribirá la que estaba en caché (obsoleta), teniendo así la nueva página con los datos actualizados guardada en la caché del servidor y lista para ser mostrada en la siguiente petición.

Diagrama del proceso de ISR

Por qué EDteam lo usa

Nosotros necesitábamos una página web pública con un muy buen rendimiento que nos pudiera proporcionar un muy buen SEO, por lo cual decidimos crear desde cero la parte pública (sin sesión) del sitio web de EDteam, utilizando este método de rendering (ISR), ya que necesitábamos mantener las ventajas de las páginas estáticas para nuestro SEO y el buen rendimiento de la página.

Pero nuestros datos son muy cambiantes, en nuestra comunidad se publican posts de los usuarios al igual que en el blog por el equipo constantemente, se agregan y editan cursos, es decir que si nosotros queríamos mantener páginas estáticas, a cada cambio debíamos estar haciendo build para actualizar los datos, y es ahí donde entra ISR, nosotros solo le agregamos un tiempo de revalidación a las páginas que necesitamos que se estén regenerando y ya no nos preocupamos por que nuestras páginas estén obsoletas en cuanto a información

Cómo hacer un página con ISR

Set up del proyecto

Iniciamos una aplicación de next con create-next-app

1npx create-next-app --ts

Una vez instalada entramos a la carpeta del proyecto y corremos el comando npm run dev y vemos nuestro proyecto en nuestro navegador.

Ahora, dentro de nuestra carpeta pages creamos una carpeta llamada products y dentro de ella creamos un archivo llamado [slug].tsx

Después, vamos a agregar las interfaces que necesitaremos para este ejemplo, creamos la carpeta interfaces en el root de nuestro proyecto y dentro de la carpeta creamos el archivo product.interface.ts

1export interface Product { 2 id: number 3 title: string 4 price: number 5 description: string 6 category: string 7 image: string 8 rating: Rating 9} 10 11export interface Rating { 12 rate: number 13 count: number 14}

Y listo ya podemos empezar a desarrollar.

getStaticProps y getStaticPaths

Primero agregaremos el código necesario para el archivo index.tsx, empezando por la función getStaticProps

1import type { GetStaticProps, NextPage } from 'next' 2import Link from 'next/link' 3import { Product } from '../interfaces/product.interface' 4 5const Home: NextPage<Props> = ({ products }) => { 6 return <main></main> 7} 8 9export const getStaticProps: GetStaticProps = async () => { 10 const res = await fetch('https://fakestoreapi.com/products') 11 const products = await res.json() 12 13 return { 14 props: { 15 products: products 16 } 17 } 18} 19 20interface Props { 21 products: Product[] 22} 23 24export default Home

En la función getStaticProps estamos haciendo una petición hacia nuestra fakeapi que nos devuelve una lista de productos y retornamos las props que se le inyectarán a nuestro componente home. Esta funcion en desarrollo se ejecutará cada vez que se recargue la página, pero en tiempo de build se ejecutará para guardar la información en un archivo json y generar el html, teniendo así una página estática.

Ahora en nuestro componente home agregamos el marcado necesario

1const Home: NextPage<Props> = ({ products }) => { 2 return ( 3 <main> 4 <h1>Lista de productos</h1> 5 <ol> 6 {products.map(item => ( 7 <li key={item.id}> 8 <Link href="/products/[slug]" as={`/products/${item.id}`}> 9 <a>{item.title}</a> 10 </Link> 11 </li> 12 ))} 13 </ol> 14 </main> 15 ) 16}

Hasta el momento nuestra página debería lucir así

Ahora pasemos a realizar la página /products/[slug]

1import { GetStaticPaths, GetStaticProps, NextPage } from 'next' 2import { useRouter } from 'next/dist/client/router' 3import Image from 'next/image' 4import { Product } from '../../interfaces/product.interface' 5 6const ProductPage: NextPage<Props> = ({ product }) => { 7 return ( 8 <main> 9 <h1>{product.title}</h1> 10 <Image src={product.image} alt={product.title} width="200" height="200" layout="responsive" /> 11 </main> 12 ) 13} 14 15export const getStaticPaths: GetStaticPaths = async () => { 16 const res = await fetch('https://fakestoreapi.com/products?limit=10') 17 const first10Products = (await res.json()) as Product[] 18 19 // Lista de paginas que se renderizarán en tiempo de build 20 const paths = first10Products.map(item => ({ params: { slug: String(item.id) } })) 21 22 return { 23 paths, 24 fallback: true 25 } 26} 27 28export const getStaticProps: GetStaticProps = async ctx => { 29 const slug = ctx.params?.slug as string 30 31 const res = await fetch(`https://fakestoreapi.com/products/${slug}`) 32 const product = await res.json() 33 34 return { 35 props: { 36 product 37 } 38 } 39} 40 41interface Props { 42 product: Product 43} 44 45export default ProductPage

Como se puede ver en la línea 13 exportamos una función llamada getStaticPaths La cual nos sirve para decirle a Next.js cuáles son las páginas dinámicas que debe renderizar a la hora de hacer build, en este caso le decimos que las páginas de los primeros 10 productos se deben de pre renderizar esto se lo indicamos retornando un objeto con la propiedad paths con la siguiente estructura

1paths: [ 2 { params: { ... } } 3],

Como pueden ver en el retorno de la función getStaticPaths también retornamos un atributo llamado fallback como true, esto quiere decir que cuando un usuario intente entrar a una página dinámica de la ruta /products y esta no se generó en tiempo de build, mostrará un estado de loading mientras que por debajo Next.js genera la página estática. Si fallback fuese false, quiere decir que todas las páginas que no se hayan generado en tiempo de build retornarán un 404.

Para agregar este estado de loading mientras Next.js genera la página debemos hacerlo explícitamente agregando el siguiente código a nuestro componente ProductPage.

1const ProductPage: NextPage<Props> = ({ product }) => { 2 const router = useRouter() 3 4 if (router.isFallback) { 5 return <h1>Loading...</h1> 6 } 7 8 ... 9} 10 11...

Este estado de loading sólo aparecerá cuando por primera vez un usuario entre a la página, ya que no ha sido generada en tiempo de build, pero una vez que un usuario entre por primera vez, esta se generará y en las futuras peticiones se mostrará la página estática sin el estado de loading.

Hasta el momento nuestra página se debería ver así

Como podrán ver, si van a la url http://localhost:3000/products/1 no aparecerá el estado de loading, ya que esa fue una de las páginas que le indicamos a Next.js que generara en tiempo de build, en cambio si vamos a la url http://localhost:3000/products/11 sí veremos el estado de loading, ya que esta no fue unas de las páginas que le indicamos a Next.js que generara en tiempo de build.

ISR

Pero que pasa si un producto cambia sus datos cada x tiempo? o si a la lista de productos se le agrega otro más o se le quita, en ese caso tendríamos que volver a hacer el build del proyecto para que se vuelvan a generar las páginas con los datos nuevos, pero esto es poco práctico en algunos casos, para ello utilizaremos la estrategia ISR, agregando un tiempo de revalidación para que cada que pase ese tiempo y un usuario entre a esa página se vuelva a generar la página con los datos nuevos.

Para agregar ISR a una página en Next.js basta con agregar un atributo llamado revalidate en el objeto que retorna la función getStaticProps , en nuestro ejemplo modificaremos nuestros getStaticProps en las dos páginas que tenemos index.ts y /products/[slug].ts

  • index.tsx
1export const getStaticProps: GetStaticProps = async () => { 2 const ONE_HOUR = 3600 3 4 const res = await fetch('https://fakestoreapi.com/products') 5 const products = await res.json() 6 7 return { 8 props: { 9 products: products 10 }, 11 revalidate: ONE_HOUR 12 } 13}

Aquí le indicamos a Next.js que regenere la página cada 1 hora y que un usuario haya visitado la página después de ese tiempo de revalidación así como se explicó en la sección de Cómo funciona ISR

  • /products/[slug].tsx
1export const getStaticProps: GetStaticProps = async ctx => { 2 const ONE_DAY = 86400 3 4 const slug = ctx.params?.slug as string 5 6 const res = await fetch(`https://fakestoreapi.com/products/${slug}`) 7 const product = await res.json() 8 9 return { 10 props: { 11 product 12 }, 13 revalidate: ONE_DAY 14 } 15}

El tiempo de revalidación debe ser decidido por el equipo de desarrollo, ya que depende de que tanto cambian los productos o los contenidos de la página, por ejemplo en EDteam se le da un tiempo menor a la comunidad, ya que esta página está en constante cambio, usuarios dan like, comentan, publican y es muy fácil que en poco tiempo esta página se quede obsoleta, a diferencia del home, en el cual tenemos un tiempo un tanto más grande, ya que es una página que no muestra datos muy cambiantes.

Y así es como se crea una página con ISR y el por qué en EDteam decidimos usarlo, espero que este blog te haya sido de ayuda, haya aprendido o hayas reforzado tus conocimientos sobre ISR.

Si te han quedado dudas o quieres aportar al artículo puedes expresarte en la sección de los comentarios.

Comentarios de los usuarios