Last active
December 23, 2023 23:28
-
-
Save SimeonGriggs/6649dc7f4b0fec974c05d29cae969cbc to your computer and use it in GitHub Desktop.
Sanity + Next.js Preview API Route with Config for SEO Pane
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./pages/api/preview.ts | |
import { groq } from "next-sanity"; | |
import { previewClient } from "../../lib/sanity.client"; | |
import { NextApiRequest, NextApiResponse } from "next"; | |
export const STUDIO_URL_DEV = "http://localhost:3333"; | |
export const STUDIO_URL_PROD = "https://replace-with-yours.sanity.studio"; | |
export const WEBSITE_URL_DEV = "http://localhost:3000"; | |
export const WEBSITE_URL_PROD = "https://replace-with-yours.com"; | |
export default async function preview( | |
req: NextApiRequest, | |
res: NextApiResponse | |
) { | |
const host = req.headers.host; | |
// Is the SEO plugin trying to fetch and return HTML? | |
// AND is the Studio on a different URL to the website? | |
if (req.query.fetch) { | |
// Allow requests from the Studio's URL | |
const corsOrigin = host.includes("localhost") | |
? // Possibly required for Node 18 which doesn't like "localhost" | |
// STUDIO_URL_DEV.replace("//localhost:", "//127.0.0.1:") | |
// Otherwise fine on Node 16 | |
STUDIO_URL_DEV | |
: STUDIO_URL_PROD; | |
res.setHeader("Access-Control-Allow-Origin", corsOrigin); | |
res.setHeader("Access-Control-Allow-Credentials", "true"); | |
} | |
const { id } = req?.query ?? {}; | |
if (!id) { | |
return res.status(401).json({ message: "No document id provided" }); | |
} | |
// Ensure this slug actually exists in the dataset | |
const query = groq`*[_id == $id && defined(slug.current)][0]`; | |
// This is an authenticated client with a token that can fetch draft ID's | |
const doc = await previewClient.fetch(query, { id }); | |
if (!doc) { | |
return res.status(401).json({ message: "Invalid document id" }); | |
} | |
const slug = doc.slug.current | |
// Initialise preview mode | |
res.setPreviewData({}); | |
// Return just the HTML if the SEO plugin is requesting it | |
if (req.query.fetch) { | |
// Create preview URL | |
const baseOrigin = host.includes("localhost") | |
? WEBSITE_URL_DEV | |
: WEBSITE_URL_PROD; | |
const absoluteUrl = new URL(slug, baseOrigin).toString(); | |
// Create preview headers from the setPreviewData above | |
const previewHeader = res.getHeader("Set-Cookie"); | |
const previewHeaderString = | |
typeof previewHeader === "string" || typeof previewHeader === "number" | |
? previewHeader.toString() | |
: previewHeader?.join("; "); | |
const headers = new Headers(); | |
headers.append("credentials", "include"); | |
headers.append("Cookie", previewHeaderString ?? ""); | |
const previewHtml = await fetch(absoluteUrl, { headers }) | |
.then((previewRes) => previewRes.text()) | |
.catch((err) => console.error(err)); | |
return res.send(previewHtml); | |
} | |
// Redirect to the path from the fetched post | |
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities | |
return res.writeHead(307, { Location: slug }).end(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@SimeonGriggs The SEO plugin sends no data except the query param
fetch=true
, howapi/preview
is supposed to get the document ID then? this API will always return401