From 8765f4f85a8e383d4c23178553c6106a1e27f5bc Mon Sep 17 00:00:00 2001 From: Ethan Niser Date: Sun, 20 Oct 2024 12:19:08 -0400 Subject: [PATCH] cache search results in kv --- package.json | 1 + pnpm-lock.yaml | 14 ++++++++++++++ src/lib/actions.ts | 30 +++++++++++++++++++++++++++++- 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 2049567..027f0ad 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "@vercel/analytics": "^1.3.1", "@vercel/blob": "^0.25.1", "@vercel/functions": "^1.4.2", + "@vercel/kv": "^3.0.0", "@vercel/postgres": "^0.10.0", "@vercel/speed-insights": "^1.0.12", "bcryptjs": "^2.4.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cdc9295..ca3ed39 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -47,6 +47,9 @@ importers: "@vercel/functions": specifier: ^1.4.2 version: 1.4.2 + "@vercel/kv": + specifier: ^3.0.0 + version: 3.0.0 "@vercel/postgres": specifier: ^0.10.0 version: 0.10.0 @@ -1532,6 +1535,13 @@ packages: "@aws-sdk/credential-provider-web-identity": optional: true + "@vercel/kv@3.0.0": + resolution: + { + integrity: sha512-pKT8fRnfyYk2MgvyB6fn6ipJPCdfZwiKDdw7vB+HL50rjboEBHDVBEcnwfkEpVSp2AjNtoaOUH7zG+bVC/rvSg==, + } + engines: { node: ">=14.6" } + "@vercel/postgres@0.10.0": resolution: { @@ -5205,6 +5215,10 @@ snapshots: "@vercel/functions@1.4.2": {} + "@vercel/kv@3.0.0": + dependencies: + "@upstash/redis": 1.34.3 + "@vercel/postgres@0.10.0": dependencies: "@neondatabase/serverless": 0.9.5 diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 6fb4c57..f5e98c9 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -8,6 +8,8 @@ import { subcollection, } from "../db/schema"; import { getCart, updateCart } from "./cart"; +import { kv } from "@vercel/kv"; +import { z } from "zod"; export async function addToCart(prevState: unknown, formData: FormData) { const prevCart = await getCart(); @@ -60,7 +62,29 @@ export async function removeFromCart(formData: FormData) { await updateCart(newCart); } +const searchResultSchema = z.array( + z.object({ + href: z.string(), + name: z.string(), + slug: z.string(), + image_url: z.string().nullable(), + description: z.string(), + price: z.string(), + subcategory_slug: z.string(), + }), +); + export async function searchProducts(searchTerm: string) { + const kvKey = `search:${searchTerm}`; + + const rawCachedResults = await kv.get(kvKey); + const parsedCachedResults = searchResultSchema.safeParse(rawCachedResults); + if (parsedCachedResults.success) { + return parsedCachedResults.data; + } + + // cache miss, run the search + let results; if (searchTerm.length <= 2) { @@ -110,11 +134,15 @@ export async function searchProducts(searchTerm: string) { ); } - return results.map((item) => { + const searchResults = results.map((item) => { const href = `/products/${item.categories.slug}/${item.subcategories.slug}/${item.products.slug}`; return { ...item.products, href, }; }); + + await kv.set(kvKey, searchResults, { ex: 60 * 60 }); // 1 hour + + return searchResults; }