Hello! That’s correct, you’d need the id
and updatedAt
timestamp in order to form that key. If you’re only caching a single row in the database then the cache may not provide any benefit: you need to select it anyway to get the values of the key, so you may as well just select it all in the first query anyway. The benefits come in when you’re dealing with cases like:
The contents of that single row are potentially huge (a BLOB perhaps) and you want to avoid that if it rarely changes. You can retrieve the key by adding a select: { id: true, updatedAt: true }
to the first call to get the values for the key and so that query will always be quick, and you can cache and avoid the second call to get ALL of the data.
Maybe a product
has an image
column which is a BLOB containing the actual image (let’s not worry about whether this is a good database design!). You could do something like the following:
export const product = ({ id }) => {
// super fast query, runs every time
const key = await db.product.findUnique({
select: { id: true, updatedAt: true },
where: { id },
})
return cache(['product', key.id, key.updatedAt], () => {
// slow query, but now only runs after the product has changed
return db.product.findUnique({ where: { id })
})
}
You’re selecting/computing more data besides just that single database row. Imagine a case where you’re building someone’s order history page: you could cache all of the data required to build their order history forever—until they place a new order, or modify an existing one. You can use the id
and updatedAt
stamp of the latest order (“latest” meaning whichever one has the most recent updatedAt
timestamp) and use that to cache all the others: if a new order is placed (or an existing order is modified) then the first query will return a different order than last time, which would bust the cache:
export const orderHistory => () => {
const latestOrder = await db.order.findFirst({
select: { id: true, updatedAt: true },
where: { userId: context.currentUser.id },
orderBy: { updatedAt: { desc: true }
})
return cache(['orders', latestOrder.id, latestOrder.updatedAt], () => {
return db.order.findMany({
where: { userId: context.currentUser.id },
include: {
orderItems: {
include: {
product: true,
}
}
}
})
})
}
I almost included currentUser.id
as part of the key, but the order list itself is limited to just those placed by the current user, so in effect the key is already “scoped” to the current user anyway!
The idea is just to get the key using the simplest query possible, to avoid a more costly query/computation later on. You will always incur the overhead of the query to get the key, but we think this is worth the tradeoff over having to manually expire cache keys, or try and come up with an expiration time that’s “good enough” to give the user’s a good experience. If you’re finding that just getting the single row to create the key is too much overhead, then you can fallback to manually expiring (see the deleteCacheKey() docs) or setting expire times.
Hope that helps!