Environments & Caching
Understand the different environments and caching strategies you can leverage to improve your content editing experience.
Environments
Setting up your application so that it handles all of the environments in a seamless fashion is a very important part of integrating with BaseHub.
Local Environment
When developing in localhost, you’ll be writing new code and iterating over your content. This means adding new blocks, changing those blocks’ constraints, writing content and more—all at the same time.
In order to not break your flow, you’ll want two things:
For the schema in BaseHub to be in sync with your IDE, and
For the content to update as you write, without needing to commit it yet.
We bundled these two needs into one command:
basehub dev
This command generates the type-safe SDK and keeps it in sync with changes you make in basehub.com (this is called --watch
mode); and also sets up the SDK so that it queries Draft content from your repository.
This is why we recommend you run it in parallel to next dev
.
"scripts": {
"dev": "basehub dev & next dev",
"build": "basehub && next build",
"start": "next start",
"lint": "next lint"
},
Notice the single &
.
Preview Environment
Setting up an easy way for editors to preview content before committing it into production is essential. We’ve designed our preview workflow with these three pillars in mind:
Content should render in real time, as you write.
Preview should be easy for developers to integrate.
The integration should never degrade production performance in any way.
We achieve this is by using a couple of BaseHub components, <Pump />
and <Toolbar />
, in combination to Next.js’ draftMode
. Additionally, to bridge the gap between basehub.com (the dashboard) and your website, you’ll need to set up the “Preview” Button.
This is how a simple code example can look like:
import { Toolbar } from 'basehub/next-toolbar'
export default function RootLayout({
children,
}: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
{/* Layout UI */}
<main>{children}</main>
<Toolbar />
</body>
</html>
)
}
Finally, we need to set up our Preview Buttons.
Production Environment
Once we’ve set up Local and Preview environments, most of the hard work is done. The only thing you need to make sure when going to production is for the SDK to be generated before the build step of your application.
"scripts": {
"dev": "basehub dev & next dev",
"build": "basehub && next build",
"start": "next start",
"lint": "next lint"
},
That should be it. You’re ready to deploy your website.
Caching
By default, Next.js will try to cache all of our requests made with fetch
—and that includes BaseHub. While this makes subsequent requests to BaseHub much faster, it’ll essentially make your website’s content fully static, instead of reflecting the latest content changes from your BaseHub Repo.
import { Pump } from "basehub/react-pump"
const Page = async () => {
return (
<Pump queries={[{ __typename: true }]}>
{async ([data]) => {
"use server"
// `data` will be cached by Next.js
return <pre>{JSON.stringify(data, null, 2)}</pre>
}}
</Pump>
)
}
export default Page
Time-Based Revalidation
The easiest way to revalidate content is to use Next.js’ time-based revalidate
caching option, so that your data is cached and fast, but it also reacts to new content coming from BaseHub.
import { Pump } from "basehub/react-pump"
const Page = async () => {
return (
<Pump
next={{ revalidate: 30 }}
queries={[{ __typename: true }]}
>
{async ([data]) => {
"use server"
// `data` will be stale after 30 seconds
return <pre>{JSON.stringify(data, null, 2)}</pre>
}}
</Pump>
)
}
export default Page
On-Demand Revalidation
You can leverage BaseHub Webhooks and Next.js On-Demand Revalidation to update the cache of your Next.js Apps in a more fine-grained fashion. Instead of checking to see if something changed every n
seconds, you can listen to the repo.commit
event from BaseHub and use revalidateTag
or revalidatePath
to revalidate on demand.
Set up API Endpoint that will revalidateTag
1
import { revalidateTag } from "next/cache"
const webhookSecret = process.env.BASEHUB_WEBHOOK_SECRET
if (typeof webhookSecret !== "string") {
throw new Error("Missing BASEHUB_WEBHOOK_SECRET.")
}
export const POST = (request: Request) => {
/**
* Authenticate the request.
* For more security, follow the Svix guide on how to verify a webhook: https://docs.svix.com/receiving/verifying-payloads/how
* For simplicity, and because revalidating a cache is not a security risk, we just do basic auth
* with a Bearer token we'll set up ourselves.
*/
const authorization = request.headers.get("authorization")
if (authorization !== `Bearer ${webhookSecret}`) {
return Response.json({ message: "Unauthorized." }, { status: 401 })
}
revalidateTag("basehub")
return Response.json({ revalidated: true, now: Date.now() })
}
The BASEHUB_WEBHOOK_SECRET
can be whatever you define it to be. Just make sure you pass it via the Authorization
header. You can generate a random password using the 1Password generator.
Add a Cache Tag to your queries2
import { Pump } from "basehub/react-pump"
const Page = async () => {
return (
<Pump
next={{ tags: ["basehub"] }}
queries={[{ __typename: true }]}
>
{async ([data]) => {
"use server"
return <pre>{JSON.stringify(data, null, 2)}</pre>
}}
</Pump>
)
}
export default Page
Create the Webhook3
To do this final step, you’ll go to your repo’s README and create a new Webhook Endpoint: