From 0a9cfd3a0847ac2fa40a58cd8d1d66102a83ca5f Mon Sep 17 00:00:00 2001 From: Benny Joo Date: Tue, 20 May 2025 03:04:52 -0400 Subject: [PATCH] perf: cache TRPC queries in /availability RSC (#21398) * cache trpc queries in /availability * fix * fix * add comments * use undefined --- .../(main-nav)/availability/page.tsx | 51 ++++++++++++++++--- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx index 901ff60376..e867f6f31d 100644 --- a/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx +++ b/apps/web/app/(use-page-wrapper)/(main-nav)/availability/page.tsx @@ -1,6 +1,8 @@ -import { createRouterCaller } from "app/_trpc/context"; -import type { PageProps } from "app/_types"; +import { createRouterCaller, getTRPCContext } from "app/_trpc/context"; +import type { PageProps, ReadonlyHeaders, ReadonlyRequestCookies } from "app/_types"; import { _generateMetadata, getTranslate } from "app/_utils"; +import { unstable_cache } from "next/cache"; +import { cookies, headers } from "next/headers"; import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable"; import { OrganizationRepository } from "@calcom/lib/server/repository/organization"; @@ -21,16 +23,53 @@ export const generateMetadata = async () => { ); }; +const getCachedMe = unstable_cache( + async (headers: ReadonlyHeaders, cookies: ReadonlyRequestCookies) => { + const meCaller = await createRouterCaller(meRouter, await getTRPCContext(headers, cookies)); + return await meCaller.get(); + }, + ["viewer.me.get"], + { revalidate: 3600 } // Cache for 1 hour +); + +const getCachedAvailabilities = unstable_cache( + async (headers: ReadonlyHeaders, cookies: ReadonlyRequestCookies) => { + const availabilityCaller = await createRouterCaller( + availabilityRouter, + await getTRPCContext(headers, cookies) + ); + return await availabilityCaller.list(); + }, + ["viewer.availability.list"], + { revalidate: 3600 } // Cache for 1 hour +); + const Page = async ({ searchParams: _searchParams }: PageProps) => { const searchParams = await _searchParams; const t = await getTranslate(); + const _headers = await headers(); + const _cookies = await cookies(); - const [meCaller, availabilityCaller] = await Promise.all([ - createRouterCaller(meRouter), - createRouterCaller(availabilityRouter), + const [me, cachedAvailabilities] = await Promise.all([ + getCachedMe(_headers, _cookies), + getCachedAvailabilities(_headers, _cookies), ]); - const [me, availabilities] = await Promise.all([meCaller.get(), availabilityCaller.list()]); + // Transform the data to ensure startTime, endTime, and date are Date objects + // This is because the data is cached and as a result the data is converted to a string + const availabilities = { + ...cachedAvailabilities, + schedules: cachedAvailabilities.schedules.map((schedule) => ({ + ...schedule, + availability: schedule.availability.map((avail) => ({ + ...avail, + startTime: new Date(avail.startTime), + endTime: new Date(avail.endTime), + date: avail.date ? new Date(avail.date) : null, + })), + })), + }; + const organizationId = me.organization?.id ?? me.profiles[0]?.organizationId; const isOrgPrivate = organizationId ? await OrganizationRepository.checkIfPrivate({