Consejos para reducir costos en tu proyecto con Next.js y Vercel

Table of contents

Todos queremos reducir costos 🤑 al tener aplicaciones en producción, como: ecommerce, blogs, dashboards, etc. Pero, en muchas ocasiones, delegamos esta responsabilidad a tecnologías y plataformas como Next.js y Vercel.
Claro que estas ofrecen grandes beneficios, pero tienen límites. Si tu aplicación no sabe cómo distribuir la carga y el uso entre los usuarios, podrías recibir una gran factura a fin de mes.
¿De qué tipo de carga estamos hablando? Hablamos del cómputo que tu aplicación realiza al obtener datos de un endpoint, o de las operaciones hechas con una base de datos o archivos sumamente pesados que se encuentran en tu aplicación.
Como inicie mi proyecto.
LaToons es un proyecto que fue creado hace más de 3 años. Durante mis primeros proyectos, nunca le presté atención a la optimización; siempre delegaba esta tarea a Next.js o Vercel.
Pero, conforme el proyecto empezó a recibir una gran cantidad de usuarios, la demanda de recursos fue mayor, por lo que siempre recibía correos de suspensión del servicio.
Y con justa razón, el proyecto estaba recibiendo un tráfico alto y estaba consumiendo alrededor de 1.5 GB de transferencia de datos (SALIDA) como mínimo por día, siendo el día 18 el de mayor salida de datos (18 GB).
Pero porque tanto? de donde venia ese consumo tan exagerado?
Al principio, el proyecto estaba conectado a una base de datos de MongoDB. Esta base de datos, durante mucho tiempo, mantuvo al proyecto, pero consumiendo demasiados recursos.
Inicialmente, implementé el método ISR de Next.js para cachear los datos y evitar solicitudes repetidas, lo cual estaba funcionando bien.
Sin embargo, aquí detecté un problema: la estructura de mi proyecto. En realidad, estaba creando rutas dinámicas entre series y episodios, donde cada serie contaba con temporadas y estas temporadas, a su vez, con alrededor de 12 a 50 episodios.
Con esta implementación, estaba consumiendo alrededor de 80,000 lecturas y escrituras de ISR que ofrece Vercel por mes.
Esto aumentaba muchísimo el consumo y no era sostenible a largo plazo.
El numero de requests cacheadas no era ni el 1% esto porque todo se generaba con ISR. y el revalidate era de 12 horas.
Pensé que la mejor manera era deshacerme de la base de datos, ya que era un paso más que consumía recursos valiosos. Entonces, pensé en extraer los datos en 4 archivos en formato JSON y colocarlos en la carpeta "public" del proyecto.
Fue un error muy crítico, porque el consumo se disparó por las nubes en tan solo un día...
Ese día recibí el correo de Vercel con el aviso de transferencia de datos, lo cual, para un proyecto tan simple, me pareció inaceptable. El consumo era demasiado.
El consumo aumentó porque, al mover todos los archivos a la carpeta "public", el archivo latoonsapp.episodes.json pesaba alrededor de 11.1 MB. Este archivo se ejecutaba cada vez que entrabas a ver un episodio o la serie con sus capítulos.
Con todas estas formas que probé, decidí intentar una estrategia distinta que tuviera como prioridad reducir el consumo de transferencia, lecturas y escrituras de ISR, y que fuera rápida al acceder.
Como optimice mi proyecto.
Era una prioridad cambiar la forma de obtener los datos de los archivos .json, ya que era el mayor consumo de procesos y de transferencia.
Comencé separando las series animadas en archivos .json más pequeños, logrando que cada uno pesara solo 10 KB, lo cual representó una reducción de peso del 90%. Luego los subí a un servicio de almacenamiento como Cloudinary para que tuvieran un cache optimizado.
Este archivo contiene toda la información de la serie animada, así como de sus temporadas y episodios. De esta manera, eliminé la necesidad de transformar los datos usando JavaScript, optimizando el uso de procesos en el servidor.
Te dejo aquí los pasos que seguí para transformar todos los datos en archivos .json.
Lo segundo que tenía que hacer era cambiar el formato de build de mi proyecto en Next.js. Esto porque mis datos no cambian nunca, por lo que un ISR ya no era necesario.
export const getStaticPaths: GetStaticPaths = async () => {
const posts = await fetch(process.env.NEXT_PUBLIC_SERIES ?? "").then((res) =>
res.json()
);
const paths = posts.map((post: Serie) => ({
params: { id: String(post.slug) },
}));
return { paths, fallback: false };
};
export const getStaticProps = async ({
params,
}: {
params: { id: string };
}) => {
const serie: JSON_SCHEMA = await fetch(
`${process.env.NEXT_PUBLIC_SEO}${params.id}.json`
).then((res) => res.json());
return {
props: serie,
};
};
const SeriePage = ({ serie, seasons }: JSON_SCHEMA) => {...
Con este cambio a getStaticProps y getStaticPaths, simplemente obtengo los datos de un archivo .json que tengo en mi CDN, el cual solo pesa 11 KB. Esto resulta en una optimización brutal al generar el build estático, lo que significa que mi página se convertirá en archivos HTML, CSS y JavaScript. El tiempo de generación se reduce de 3 minutos a 45 segundos.
Con estos cambios, ahora estoy obteniendo los siguientes resultados en la plataforma de Vercel:
Un consumo sumamente bajo de tan solo 79 MB de salida, lo cual representa una optimización del 90%. Y es perfecto en comparación con los 1.5 GB que se consumían por día.
Y los procesos de Edge Network a Vercel Compute cayeron en un 70%.
Las solicitudes de Edge tuvieron una caída del 95% en el cache de requests que generaban mis deployments al generar los builds.
La duración de la CPU de las solicitudes de Edge tuvo 0 segundos utilizados durante 4 días seguidos.
El uso de Incremental Static Regeneration (ISR) cayó en un 96% en las lecturas al obtener los datos que generaban las páginas, debido al build estático que retorna HTML, CSS y JavaScript.
Las funciones serverless cayeron drásticamente a solo 230 por día. ¡WHAT! Es una mejora del 100% en comparación con las 3k a 8k que se hacían por día.
Y en PageSpeed Insights, la página mantiene un récord del 100% en usabilidad, lo cual es impresionante.
Todo esto demuestra que existen múltiples soluciones a los problemas. Yo, al principio, me aferraba a una solución que a corto plazo era fácil, pero a largo plazo podía generar un costo alto. Es importante conocer qué tan optimizada está la aplicación para maximizar el uso de los recursos.
¡Gracias por leer!
Espero que esta guía te haya sido de gran ayuda. Ha sido un articulo extenso y detallado, y me encantaría que más personas pudieran beneficiarse de esta información. Si te ha gustado, te invito a compartirlo y a darle un "me gusta". ¡Tu apoyo es invaluable y me motiva a seguir creando contenido de calidad!
ツ
Subscribe to my newsletter
Read articles from Ivan Garcia directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ivan Garcia
Ivan Garcia
Freelance Fullstack Software Engineer