Hey is a decentralized and permissionless social media app built with Lens Protocol 🌿

feat: cache persisted GraphQL queries (#46)

authored by yoginth.com and committed by

GitHub c38bcb82 c4fbbf64

+75
+12
apps/web/README.md
··· 6 6 7 7 - `pnpm dev` – run the development server. 8 8 - `pnpm build` – create a production build. 9 + 10 + ## GraphQL CDN caching 11 + 12 + GET requests to `/graphql` that include a persisted query hash are cached at the 13 + CDN. Responses set `Cache-Control: public, max-age=600` and are retained for 14 + ten minutes. 15 + 16 + ### Purging cached queries 17 + 18 + When a persisted query changes, deploy the updated hash. To immediately 19 + invalidate a cached response, purge the specific `/graphql` URL—containing the 20 + `sha256Hash` and any variables—using the Cloudflare cache API.
+63
apps/web/functions/graphql.ts
··· 1 + import { LENS_API_URL } from "@hey/data/constants"; 2 + 3 + export interface Context { 4 + request: Request; 5 + } 6 + 7 + const CACHE_TTL_SECONDS = 600; 8 + const CACHE_CONTROL_HEADER = `public, max-age=${CACHE_TTL_SECONDS}`; 9 + 10 + interface CfOptions { 11 + cacheTtl?: number; 12 + cacheEverything?: boolean; 13 + } 14 + 15 + const fetchUpstream = (url: URL, request: Request, cf?: CfOptions) => 16 + fetch(`${LENS_API_URL}${url.search}`, { 17 + body: request.body, 18 + headers: request.headers, 19 + method: request.method, 20 + ...(cf ? { cf } : {}) 21 + } as RequestInit); 22 + 23 + export const onRequest = async ({ request }: Context): Promise<Response> => { 24 + const url = new URL(request.url); 25 + 26 + if (request.method !== "GET") { 27 + return fetchUpstream(url, request); 28 + } 29 + 30 + const extensions = url.searchParams.get("extensions"); 31 + if (!extensions) { 32 + return fetchUpstream(url, request); 33 + } 34 + 35 + let hash: string | undefined; 36 + try { 37 + hash = JSON.parse(extensions).persistedQuery?.sha256Hash; 38 + } catch { 39 + return fetchUpstream(url, request); 40 + } 41 + 42 + if (!hash) { 43 + return fetchUpstream(url, request); 44 + } 45 + 46 + const upstreamResponse = await fetchUpstream(url, request, { 47 + cacheEverything: true, 48 + cacheTtl: CACHE_TTL_SECONDS 49 + }); 50 + 51 + if (!upstreamResponse.ok) { 52 + return upstreamResponse; 53 + } 54 + 55 + const headers = new Headers(upstreamResponse.headers); 56 + headers.set("Cache-Control", CACHE_CONTROL_HEADER); 57 + 58 + return new Response(upstreamResponse.body, { 59 + headers, 60 + status: upstreamResponse.status, 61 + statusText: upstreamResponse.statusText 62 + }); 63 + };