
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
If you've been following along with the Prisma Project, you've seen how to build a server action that fetches data from a database. If you've been testing your server action, you've probably noticed that that the data we're fetching is sometimes .
You might have noticed that you need to refresh the page to see the latest updated data.
This is a feature, not a bug. When we use tools like "router.push", we're not actually refreshing the page. We're just changing the URL. This means that the data we fetched when the page first loaded is still the data we're seeing.
In this lesson, we'll learn how to use caching to make sure that the data we're seeing is always up-to-date.
A cache is a temporary storage location that stores data so that future requests for that data can be served faster. The data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere.
When we fetch data from a database, we can store that data in a cache. This way, when we fetch the data again, we can check the cache first to see if the data. We can also mark requests as "stale" and fetch the data again if it's been a while since we last fetched it- or if we've made some database action that we know updated the data.
That last part is what we'll take advantage of here. We'll use the cache to store the data we fetch from the database. When we make a database action that we know updates the data, we'll mark the data in the cache as "stale" and fetch the data again. This way, we can be sure that the data we're seeing is always up-to-date- and we don't have to refresh the page to see the latest data.
Next comes with the implementation of the cache called . "Unstable" in this sense means it's not yet stable and might change in the future. But it's still a great tool to use for now.
Let's open up our file once more.
For now, the only function we need to consider caching is the function. We'll add the function to it.
products.ts;
async function _getProductById(id: number) {
try {
const product = await prisma.product.findUnique({
where: { id },
include: {
images: true,
reviews: true,
},
});
return product;
} catch (error) {
return null;
}
}
export const getProductById = cache(_getProductById, ["getProductById"], {
tags: ["Product"],
});
What's happening here?
We're importing the function from the module. We're no longer exporting the function directly. Instead, we're exporting a new function that wraps the function with the function.
The function takes three arguments:
In this case, we're passing the function as the first argument, an array with the string as the second argument, and an object with the property set to an array with the string as the third argument.
The property is used to group cached data together. This way, we can invalidate all cached data with a specific tag when we know that the data is stale.
Now that this is setup- even if the user refreshes- they won't make another request to our database to grab new data. This is because it's now cached. The only way for it to be updated is if we mark it as "stale" by "revalidating" it.
When we make a database action that we know updates the data, we can mark the data in the cache as "stale" and fetch the data again. We do this in one of two ways.
Let's see how we can do this in our file using the first method- explicitly calling the function whenever we know the data is stale.
products.ts;
async function _getProductById(id: number) {
try {
const product = await prisma.product.findUnique({
where: { id },
include: {
images: true,
reviews: true,
},
});
return product;
} catch (error) {
return null;
}
}
export const getProductById = cache(_getProductById, ["getProductById"], {
tags: ["Product"],
});
export async function updateProduct(id: number, product: CreateProductInput) {
try {
const updatedProduct = await prisma.product.update({
where: { id },
data: {
name: product.name,
description: product.description,
price: product.price,
category: product.category,
images: {
deleteMany: {},
create: product.images?.map((url) => ({ url })),
},
},
});
// Mark the data as stale, and re-fetch it from the database
revalidateTag("Product");
return updatedProduct;
} catch (error) {
return null;
}
}
export async function deleteProduct(id: number) {
try {
await prisma.product.delete({
where: { id },
});
// Mark the data as stale, and re-fetch it from the database
revalidateTag("Product");
return true;
} catch (error) {
return false;
}
}
In this example, we've added to the and functions. This way, when we update or delete a product, we mark the data as stale and re-fetch it from the database- ensuring we always have the latest data when we make a change to the database.
We'll also want to add a revalidate property to the options object when we call the unstable_cache function. This way, we can automatically re-fetch the data from the database after a certain amount of time has passed.
products.ts;
export const getProductById = cache(_getProductById, ["getProductById"], {
tags: ["Product"],
revalidate: 60, // Re-fetch the data every 60 seconds
});
In this example, we've added a revalidate property to the options object with a value of 60. This means that the data will be re-fetched from the database every 60 seconds.
Note: The revalidate property is optional. If you don't provide a revalidate property, the data will only be re-fetched when you call the revalidateTag function.
Revalidation is a powerful tool that allows you to keep your data up-to-date without having to refresh the page. By using the cache and revalidating the data when you know it's stale, you can ensure that your users always see the latest data.
Later, when we create "reviews", we'll see this in action! We'll be able to create a review and see it immediately on the product page without having to refresh the page!
In this lesson, we learned how to use caching to keep our data up-to-date without having to refresh the page. We used the unstable_cache function from the next/cache module to cache the data we fetch from the database. We also learned how to mark the data as stale and re-fetch it from the database when we know it's stale.