Desai Hardik
Desai Hardik

Reputation: 218

How to cache fetched data from firebase firestore in Next.js 13 app directory

I want to cache data on next js server side page, when we fetch data from firebase. i already know that we can cache default data using fetch api

https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-fetch

but i want to cache data without this, as i don't have any APIs

basically in my next js app i have one dynamic route called "[username]"

enter image description here

So the example url will be "https://prozo.me/@hardikdesai", and i am fetching that user's data by username with this function

export const getUserDetailsByUsername = async (username: string): Promise<{ userDetails: any; message?: string }> => {
  const q = DB.collection('users').where('username', '==', username)
  const querySnapshot = await q.get()
  if (querySnapshot.empty) {
    return { userDetails: null, message: 'User not found' }
  }

  const userDetails = querySnapshot.docs[0].data()
  return { userDetails }
}

so, i want that my fetched data would ne cached for 60 seconds. and not make any firebase db request for that time

page.tsx

// sections
import { Metadata } from 'next'
import { getUserDetailsByUsername } from 'src/queries/user'
import { ProfileView } from 'src/sections/username/view'

// ----------------------------------------------------------------------
type Props = {
  params: { username: string }
}

export async function generateMetadata({ params }: Props): Promise<Metadata> {
  return {
    title: `${decodeURIComponent(params?.username)} | Prozo`,
  }
}

export default async function ProfilePage({ params }: { params: { username: string } }) {
  const { userDetails } = await getUserDetailsByUsername(params?.username?.slice(3))
  return <ProfileView userDetails={userDetails} />
}

Upvotes: 0

Views: 476

Answers (1)

Ahmed Abdelbaset
Ahmed Abdelbaset

Reputation: 4926

Next.js offers an experiential cache function designed to write to the Next.js Data Cache, functioning similarly to the extended fetch API.

import { unstable_cache as cache } from "next/cache";

const getUserDetailsByUsernameImpl = async (username: string): Promise<{ userDetails: any; message?: string }> => {
  const q = DB.collection('users').where('username', '==', username)
  const querySnapshot = await q.get()
  if (querySnapshot.empty) {
    return { userDetails: null, message: 'User not found' }
  }

  const userDetails = querySnapshot.docs[0].data()
  return { userDetails }
}

const getUserDetailsByUsername = cache(
  /* fetch function */ getUserDetailsByUsernameImpl,
  /* unique key     */ ["getUserDetailsByUsername"],
  /* options        */ {
    tags: ["getUserDetailsByUsername"],
    revalidate: 60 * 60 * 24 /* same as fetch.revalidate */
  }
)

Functioning similar to caching in the fetch API, it allows setting a revalidate option to define when cached data should become stale or invoking revalidateTag("getUserDetailsByUsername") to instantly invalidate the cache.

It accepts 3 parameters:

  • A fetch function to be cached
  • An array of key parts ensuring global uniqueness when combined.
  • An options object containing:
    • tags: An array of keys usable with revalidateTag.
    • revalidate: The interval in seconds after which the cache should be revalidated.

Read more in the documentation: https://nextjs.org/docs/app/api-reference/functions/unstable_cache


Advanced:

Currently, there's a limitation with revalidateTag where individual user revalidation isn't supported, for instance, revalidateTag("getUserDetailsByUsername", "JohnDoe") isn't feasible. Invoking revalidateTag("getUserDetailsByUsername") invalidates all cached users. A workaround involves a slight restructuring:

const getUserDetailsByUsername = async (username) => {
  const cachedFn = cache(
    getUserDetailsByUsernameImpl,
    [`getUserDetailsByUsername-${username}`],
    {
      tags: ["getUserDetailsByUsername", `getUserDetailsByUsername-${username}`],
      revalidate: 60 * 60 * 24
    }
  )

  return cachedFn(username); 
}

This approach includes the username in the cache tag, enabling the revalidation of a specific user when necessary:

revalidateTag(`getUserDetailsByUsername-${username}`)

Upvotes: 2

Related Questions