back

it is alive

It is alive!

Happy to notice it for myself that my setup with NextJS and Contentful was smoother as I thought. I am happy to use things like that without unnecessary hassle.

[hint]: Although, Contentful might miss some features, community support helps a lot, since I couldn't find a way to paste a code samples in one block. They usually are written as a different paragraphs if you use simple `return` as a line break.
Like this:
first line of code
second line of code
Instead, you may use shift+return to skip adding new paragraph like that:
first line of code
second line of code
To sum up all the markdown options handler, I kept not too many (not sure if I need all of them) and provided following custom options:
renderMark: {
  [MARKS.BOLD]: (text) => <b className="bold">{text}</b>,
  [MARKS.CODE]: (text) => (
    <div className="p-3 bg-slate-500/20 rounded">
      <pre>{text}</pre>
    </div>
  ),
},
renderNode: {
  [BLOCKS.HR]: () => <SectionSeparator/>,
  [BLOCKS.PARAGRAPH]: (_node, children) => (
    <p className="text-lg align-center mb-4">{children}</p>
  ),
  [BLOCKS.HEADING_1]: (_node, children) => (
    <h1 className="text-4xl h1 mb-4">{children}</h1>
  ),
  [BLOCKS.HEADING_2]: (_node, children) => (
    <h2 className="text-3xl h2 mb-4">{children}</h2>
  ),
  [BLOCKS.HEADING_3]: (_node, children) => (
    <h3 className="text-2xl h3 mb-4">{children}</h3>
  ),
  [BLOCKS.HEADING_4]: (_node, children) => (
    <h4 className="text-xl h4 mb-4">{children}</h4>
  ),
  [INLINES.HYPERLINK]: ({ data }, children) => (
    <a
      href={data.uri}
      target="_blank"
      rel="noreferrer"
      className="underline font-medium hover:text-emerald-600"
    >
      {children}
    </a>
  )
}

Next point was the satisfaction of using new NextJS API Routes feature to add API to the project with ease. This helps rebuilding static pages on-demand by calling Contentful's Webhook with certain API path. I would totally recommend it and provide NextJS usage of regeneration to understand the concept together with Contentful documentation.
My API handler, as an example of usage, looks like the following:
function sendResponse(res, { code = 200, json = {} }) {
  return res.status(code).json(json)
}

export default async function handler(req, res) {
// secret key withing the webhook's headers
let inboundRevalToken = req.headers['x-vercel-reval-key']

if (!inboundRevalToken) {
 return sendResponse(res, { code:401, json: { message: 'not defined' } })
} else if (inboundRevalToken !== process.env.CONTENTFUL_REVALIDATE_SECRET) {
 return sendResponse(res, { code:401, json: { message: 'does not match' } })
}

let slugForDynamicPaths = req.body.fields.slugForDynamicPaths
let isHomeUpdated = req.body.sys.contentType.sys.id === 'home'

// regenrate static pages on routes you need to update
let promises = [res.revalidate('/path')]

if (slugForDynamicPaths) {
  // check if slug is present to regenerate static pages with dynamic path
  // you can define request body in contentful webhook management page
  promises.push(res.revalidate(`/path/${slugForDynamicPaths}`))
}

if (isHomeUpdated) {
  promises.push(res.revalidate('/home'))
}

return await Promise.all(promises)
  .then(() => sendResponse(res, { json: { revalidated: true } }))
  .catch((error) => sendResponse(res, { code: 500, json: { message:"not revalidated", error }
}))
}
First of all, we compare the secret token we received in the request with one stored in the environment of where our application is stored (in our case is Vercel). That is done due to protection of this endpoint, so not a single request would affect our data until it contains the secret key.
Since I have multiple pages and multiple types of content models in Contentful, I need to revalidate multiple places, and to be more flexible I decided to update only affected places to keep the rest built statically as it is without unnecessary regeneration on server. There are two types of pages I introduced here: one contains dynamic slug and the second one is primitive path.
One really needs to check webhook request settings to define the fields in body to trigger regeneration in this implementation. I am currently checking the content type id to catch home content updated, and dynamic slug of a certain post that has been updated:
let slugForDynamicPaths = req.body.fields.slugForDynamicPaths
let isHomeUpdated = req.body.sys.contentType.sys.id === 'home'
Then, depending on the appearance of certain flag I add to my promises array async functions to regenerate pages that has such a content - so if home update, I would ask to regenerate page on /home path.
Overall, the combination of NextJS and Contentful Webhook allow you to create highly flexible and maintainable features. In case you have a question, do not hesitate to reach me out via email :)

See you around, will try to prepare more meaningful posts!