cache queries and revalidate with incremental static regeneration

This commit is contained in:
ponas committed 2026-07-07 02:32:21 +02:00
1 parent d63b14b2e5
commit e600d0a663
22 files changed
+381 -114

No files matched your search

-2
View File
@@ -8,8 +8,6 @@ import {
} from "@/components/general/GenericPageView";
import { getSeoMetadata } from "@/lib/seo";
export const dynamicParams = false;
function getWagtailUrlPath(url: string[]): string {
// for the page /foo/bar we need to look for `/home/foo/bar/`
return `/home/${url.join("/")}/`;
+2 -24
View File
@@ -1,36 +1,14 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import {
NewsPageView,
loadNewsPageProps,
} from "@/components/news/NewsPageView";
import { graphql } from "@/gql";
import { getSeoMetadata } from "@/lib/seo";
export async function generateStaticParams() {
const allNewsSlugsQuery = graphql(`
query allNewsSlugs {
pages(contentType: "news.NewsPage") {
id
slug
}
}
`);
const { data, error } = await getClient().query(allNewsSlugsQuery, {});
if (error) {
throw new Error(error.message);
}
if (!data?.pages) {
throw new Error(
"Failed to generate static params for subpages of /aktuelt"
);
}
return data.pages.map((page: any) => ({
slug: page.slug,
}));
// Prerender nothing at build time; render and cache on first request
return [];
}
type Params = Promise<{ slug: string }>;
+28
View File
@@ -0,0 +1,28 @@
import { timingSafeEqual } from "node:crypto";
import { revalidateTag } from "next/cache";
import { NextRequest, NextResponse } from "next/server";
import { CMS_CACHE_TAG } from "@/lib/revalidation";
function secretMatches(provided: string, expected: string): boolean {
const a = Buffer.from(provided);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}
export async function POST(req: NextRequest) {
const secret = process.env.REVALIDATE_WEBHOOK_SECRET;
if (!secret) {
return NextResponse.json(
{ error: "revalidation is not configured" },
{ status: 503 }
);
}
const provided = req.headers.get("x-revalidate-secret") ?? "";
if (!secretMatches(provided, secret)) {
return NextResponse.json({ error: "invalid secret" }, { status: 401 });
}
// expire: 0 purges immediately; the next request renders fresh
revalidateTag(CMS_CACHE_TAG, { expire: 0 });
return NextResponse.json({ revalidated: true, tag: CMS_CACHE_TAG });
}
+6 -1
View File
@@ -1,5 +1,6 @@
import { NextRequest, NextResponse } from "next/server";
import { getClient } from "@/app/client";
import { cachedUntilOsloMidnight } from "@/lib/revalidation";
import {
eventsOverviewQuery,
getSingularEvents,
@@ -45,7 +46,11 @@ export async function GET(req: NextRequest) {
);
}
const { data, error } = await getClient().query(eventsOverviewQuery, {});
const { data, error } = await getClient().query(
eventsOverviewQuery,
{},
cachedUntilOsloMidnight()
);
if (error) {
throw new Error(error.message);
}
+2 -24
View File
@@ -1,36 +1,14 @@
import { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation";
import { getClient } from "@/app/client";
import {
EventPageView,
loadEventPageProps,
} from "@/components/events/EventPageView";
import { graphql } from "@/gql";
import { getSeoMetadata } from "@/lib/seo";
export async function generateStaticParams() {
const allEventSlugsQuery = graphql(`
query allEventSlugs {
pages(contentType: "events.EventPage") {
id
slug
}
}
`);
const { data, error } = await getClient().query(allEventSlugsQuery, {});
if (error) {
throw new Error(error.message);
}
if (!data?.pages) {
throw new Error(
"Failed to generate static params for subpages of /arrangementer"
);
}
return data.pages.map((page: any) => ({
slug: page.slug,
}));
// Prerender nothing at build time; render and cache on first request
return [];
}
type Params = Promise<{ slug: string }>;
+5 -2
View File
@@ -3,6 +3,8 @@ import "server-only";
import { cacheExchange, createClient, fetchExchange } from "@urql/core";
import { registerUrql } from "@urql/next/rsc";
import { CMS_CACHE_TAG } from "@/lib/revalidation";
const wagtailBaseUrl = process.env.WAGTAIL_BASE_URL;
if (!wagtailBaseUrl) {
throw new Error("WAGTAIL_BASE_URL is not set");
@@ -13,8 +15,9 @@ const makeClient = () => {
return createClient({
url: graphqlEndpoint,
exchanges: [cacheExchange, fetchExchange],
// requestPolicy: "network-only",
fetchOptions: { next: { revalidate: 0 } },
// Cache all queries in the Data Cache until revalidateTag(CMS_CACHE_TAG);
// override per operation with the contexts in @/lib/revalidation
fetchOptions: { cache: "force-cache", next: { tags: [CMS_CACHE_TAG] } },
});
};
+6 -1
View File
@@ -1,4 +1,5 @@
import { getClient } from "@/app/client";
import { uncached } from "@/lib/revalidation";
import { PreviewBanner } from "@/components/general/PreviewBanner";
import {
AssociationIndexView,
@@ -149,7 +150,11 @@ export default async function PreviewRender() {
return <ExpiredPreview />;
}
const { data, error } = await getClient().query(previewPageQuery, { token });
const { data, error } = await getClient().query(
previewPageQuery,
{ token },
uncached
);
if (error) {
throw new Error(error.message);
}
+2 -1
View File
@@ -1,4 +1,5 @@
import { getClient } from "@/app/client";
import { uncached } from "@/lib/revalidation";
import {
type SearchResult,
SearchResults,
@@ -65,7 +66,7 @@ export default async function Page({
}
`);
const { data } = await getClient().query(searchQuery, { query });
const { data } = await getClient().query(searchQuery, { query }, uncached);
const all = (data?.results ?? []) as SearchResult[];
totalCount = all.length;
results = all.slice(0, RESULT_LIMIT);