Skip to content

Commit

Permalink
add revalidate global functionality, remove all optional locales
Browse files Browse the repository at this point in the history
  • Loading branch information
kahlstrm committed Jan 23, 2024
1 parent 8402b1a commit 8b7cbe9
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 8 deletions.
52 changes: 52 additions & 0 deletions apps/cms/src/hooks/revalidate-globals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// revalidate the page in the background, so the user doesn't have to wait
// notice that the hook itself is not async and we are not awaiting `revalidate`

import type { AfterChangeHook } from "payload/dist/globals/config/types";

// only revalidate existing docs that are published (not drafts)
export const revalidateGlobal: AfterChangeHook = ({ doc, req, global }) => {
const locale = req.locale;
if (!locale) {
req.payload.logger.error("locale not set, cannot revalidate");
return;
}
const revalidate = async (): Promise<void> => {
const revalidationKey = process.env.PAYLOAD_REVALIDATION_KEY;
if (!revalidationKey) {
req.payload.logger.error(
"PAYLOAD_REVALIDATION_KEY not set, cannot revalidate",
);
return;
}
try {
const fetchUrl = `${
process.env.PUBLIC_FRONTEND_URL
}/next_api/revalidate-global?${new URLSearchParams({
secret: encodeURIComponent(revalidationKey),
global: encodeURIComponent(global.slug),
locale: encodeURIComponent(locale),
}).toString()}`;
req.payload.logger.info(
`sending revalidate request ${fetchUrl.replace(revalidationKey, "REDACTED")}`,
);
const res = await fetch(fetchUrl);
if (res.ok) {
const thing = await res.json();

Check warning on line 34 in apps/cms/src/hooks/revalidate-globals.ts

View workflow job for this annotation

GitHub Actions / Format, Lint, Check types & Build

Unsafe assignment of an `any` value
req.payload.logger.info(`revalidate response ${JSON.stringify(thing)}`);
req.payload.logger.info(`Revalidated global ${global.slug}`);
} else {
req.payload.logger.error(
`Error revalidating collection ${global.slug}`,
);
}
} catch (err: unknown) {
req.payload.logger.error(
`Error hitting revalidate collection ${global.slug}`,
);
}
};

void revalidate();

return doc;

Check warning on line 51 in apps/cms/src/hooks/revalidate-globals.ts

View workflow job for this annotation

GitHub Actions / Format, Lint, Check types & Build

Unsafe return of an `any` typed value
};
17 changes: 17 additions & 0 deletions apps/cms/src/payload.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import { Footer } from "./globals/footer";
import { LandingPage } from "./globals/landing-page";
import { MainNavigation } from "./globals/main-navigation";
import { useCloudStorage } from "./util";
import { revalidateGlobal } from "./hooks/revalidate-globals";

declare module "payload" {
// eslint-disable-next-line @typescript-eslint/no-empty-interface -- not applicable
Expand Down Expand Up @@ -203,5 +204,21 @@ export default buildConfig({
},
},
}),
// add revalidateGlobal hook to all globals
(config) => {
return {
...config,
globals: config.globals?.map((global) => ({
...global,
hooks: {
...global.hooks,
afterChange: [
...(global.hooks?.afterChange ?? []),
revalidateGlobal,
],
},
})),
};
},
],
});
2 changes: 1 addition & 1 deletion apps/web/src/app/[lang]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export default async function Home({
}) {
const dictionary = await getDictionary(lang);

const landingPageData = await fetchLandingPage({});
const landingPageData = await fetchLandingPage(lang)({});
if (!landingPageData) {
// TODO: Real error handling / show error page
return "Unable to fetch landing page data, please refresh";
Expand Down
38 changes: 38 additions & 0 deletions apps/web/src/app/next_api/revalidate-global/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { revalidateTag } from "next/cache";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";

// this endpoint will revalidate a page by tag or path
// this is to achieve on-demand revalidation of pages that use this data
// send either `collection` and `slug` or `revalidatePath` as query params
export function GET(request: NextRequest): NextResponse {
const global = decodeURIComponent(
request.nextUrl.searchParams.get("global") ?? "",
);
const locale = decodeURIComponent(
request.nextUrl.searchParams.get("locale") ?? "",
);
const secret = decodeURIComponent(
request.nextUrl.searchParams.get("secret") ?? "",
);

if (secret !== process.env.NEXT_REVALIDATION_KEY) {
// eslint-disable-next-line no-console -- for debugging purposes
console.log("invalid secret from revalidate request: ", secret);
return NextResponse.json({ revalidated: false, now: Date.now() });
}

if (typeof global === "string" && typeof locale === "string") {
const tagToRevalidate = `getGlobal_/api/globals/${global}?locale=${locale}`;
// eslint-disable-next-line no-console -- for debugging purposes
console.log("revalidating tag: ", tagToRevalidate);
revalidateTag(tagToRevalidate);
return NextResponse.json({ revalidated: true, now: Date.now() });
}
// eslint-disable-next-line no-console -- for debugging purposes
console.log(
"invalid collection or fetchData from revalidate request: ",
global,
);
return NextResponse.json({ revalidated: false, now: Date.now() });
}
8 changes: 4 additions & 4 deletions apps/web/src/lib/api/fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,19 +74,19 @@ export const getAll = <

export const getOne =
<Request extends Record<string, unknown>, Response>(path: string) =>
(req: Request & { locale?: string }) =>
(req: Request & { locale: string }) =>
getAll<Request, Response[]>(path)(req).then((res) => res?.[0]);

export const getGlobal = <Response>(path: string, locale?: string) =>
export const getGlobal = <Response>(path: string, locale: string) =>
fetcher<Record<string, never>, Response>(
() => `getGlobal_${path}`,
() => `getGlobal_${path}?locale=${locale}`,
async (_, draft, fetchOptions): Promise<Response | undefined> => {
const result = await fetch(
`${process.env.PUBLIC_SERVER_URL}${path}?${qsStringify({
locale,
depth: 10, // TODO: remove this when we have a better way to handle depth for example with GraphQL
// Needs to be bigger than 1 to get media / images
...(draft ? { draft: "true" } : {}),
...(locale ? { locale } : {}),
}).toString()}`,
{
method: "GET",
Expand Down
5 changes: 2 additions & 3 deletions apps/web/src/lib/api/landing-page.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { LandingPage } from "@tietokilta/cms-types/payload";
import { getGlobal } from "./fetcher";

export const fetchLandingPage = getGlobal<LandingPage>(
"/api/globals/landing-page",
);
export const fetchLandingPage = (locale: string) =>
getGlobal<LandingPage>("/api/globals/landing-page", locale);

0 comments on commit 8b7cbe9

Please sign in to comment.