-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #118 from contentful/feat/next-js-example
feat: next js example [TOL-1162]
- Loading branch information
Showing
13 changed files
with
352 additions
and
6 deletions.
There are no files selected for viewing
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
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# This is the Space ID from your Contentful space. | ||
CONTENTFUL_SPACE_ID= | ||
# This is the Content Delivery API - access token, which is used for fetching published data from your Contentful space. | ||
CONTENTFUL_ACCESS_TOKEN= | ||
# This is the Content Preview API - access token, which is used for fetching draft data from your Contentful space. | ||
CONTENTFUL_PREVIEW_ACCESS_TOKEN= | ||
# This can be any value you want. It must be URL friendly as it will be send as a query parameter to enable draft mode. | ||
CONTENTFUL_PREVIEW_SECRET= |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. | ||
|
||
# dependencies | ||
/node_modules | ||
/.pnp | ||
.pnp.js | ||
|
||
# testing | ||
/coverage | ||
|
||
# next.js | ||
/.next/ | ||
/out/ | ||
|
||
# production | ||
/build | ||
|
||
# misc | ||
.DS_Store | ||
*.pem | ||
|
||
# debug | ||
npm-debug.log* | ||
yarn-debug.log* | ||
yarn-error.log* | ||
|
||
# local env files | ||
.env*.local | ||
|
||
# vercel | ||
.vercel | ||
|
||
# typescript | ||
*.tsbuildinfo | ||
next-env.d.ts |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
# Next.js Contentful live preview SDK example | ||
|
||
This is an example project that demonstrates how to use the `@contentful/live-preview` SDK with a Next.js application. The SDK provides live preview functionality for content changes and the inspector mode for your Contentful space. | ||
|
||
## 1. Installation | ||
|
||
Install the dependencies: | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
## 2. Environment variables | ||
|
||
To run this project, you will need to add the following environment variables to your `.env.local` file: | ||
|
||
- `CONTENTFUL_SPACE_ID`: This is the Space ID from your Contentful space. | ||
- `CONTENTFUL_ACCESS_TOKEN`: This is the Content Delivery API - access token, which is used for fetching **published** data from your Contentful space. | ||
- `CONTENTFUL_PREVIEW_ACCESS_TOKEN`: This is the Content Preview API - access token, which is used for fetching **draft** data from your Contentful space. | ||
- `CONTENTFUL_PREVIEW_SECRET`: This can be any value you want. It must be URL friendly as it will be send as a query parameter to enable draft mode. | ||
|
||
|
||
## 3. Setting up the content model | ||
|
||
You will need to set up a content model within your Contentful space. For this project, we need a `Post` content type with the following fields: | ||
|
||
- `slug` | ||
- `title` | ||
- `description` | ||
|
||
Once you've set up the `Post` content model, you can populate it with some example entries. | ||
|
||
## 4. Setting up Content preview URL | ||
|
||
In order to enable the live preview feature in your local development environment, you need to set up the Content preview URL in your Contentful space. | ||
|
||
`http://localhost:3000/api/draft?secret=<CONTENTFUL_PREVIEW_SECRET>&slug={entry.fields.slug}` | ||
|
||
Replace `<CONTENTFUL_PREVIEW_SECRET>` with its respective value in `.env.local`. | ||
|
||
## 5. Running the project locally | ||
|
||
To run the project locally, you can use the `npm run dev` command. You can now use the live preview feature. |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
const POST_GRAPHQL_FIELDS = ` | ||
__typename | ||
sys { | ||
id | ||
} | ||
slug | ||
title | ||
description | ||
`; | ||
|
||
async function fetchGraphQL(query, draftMode = false) { | ||
return fetch( | ||
`https://graphql.contentful.com/content/v1/spaces/${process.env.CONTENTFUL_SPACE_ID}`, | ||
{ | ||
method: "POST", | ||
headers: { | ||
"Content-Type": "application/json", | ||
Authorization: `Bearer ${ | ||
draftMode | ||
? process.env.CONTENTFUL_PREVIEW_ACCESS_TOKEN | ||
: process.env.CONTENTFUL_ACCESS_TOKEN | ||
}`, | ||
}, | ||
body: JSON.stringify({ query }), | ||
} | ||
).then((response) => response.json()); | ||
} | ||
|
||
function extractPost(fetchResponse) { | ||
return fetchResponse?.data?.postCollection?.items?.[0]; | ||
} | ||
|
||
function extractPostEntries(fetchResponse) { | ||
return fetchResponse?.data?.postCollection?.items; | ||
} | ||
|
||
export async function getPreviewPostBySlug(slug) { | ||
const entry = await fetchGraphQL( | ||
`query { | ||
postCollection(where: { slug: "${slug}" }, preview: true, limit: 1) { | ||
items { | ||
${POST_GRAPHQL_FIELDS} | ||
} | ||
} | ||
}`, | ||
true | ||
); | ||
return extractPost(entry); | ||
} | ||
|
||
export async function getAllPostsWithSlug() { | ||
const entries = await fetchGraphQL( | ||
`query { | ||
postCollection(where: { slug_exists: true }) { | ||
items { | ||
${POST_GRAPHQL_FIELDS} | ||
} | ||
} | ||
}` | ||
); | ||
return extractPostEntries(entries); | ||
} | ||
|
||
export async function getAllPostsForHome(draftMode) { | ||
const entries = await fetchGraphQL( | ||
`query { | ||
postCollection(preview: ${draftMode ? "true" : "false"}) { | ||
items { | ||
${POST_GRAPHQL_FIELDS} | ||
} | ||
} | ||
}`, | ||
draftMode | ||
); | ||
return extractPostEntries(entries); | ||
} | ||
|
||
export async function getPost(slug, draftMode) { | ||
const entry = await fetchGraphQL( | ||
`query { | ||
postCollection(where: { slug: "${slug}" }, preview: ${ | ||
draftMode ? "true" : "false" | ||
}, limit: 1) { | ||
items { | ||
${POST_GRAPHQL_FIELDS} | ||
} | ||
} | ||
}`, | ||
draftMode | ||
); | ||
return { | ||
post: extractPost(entry), | ||
}; | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
module.exports = { | ||
async headers() { | ||
return [ | ||
{ | ||
source: '/:path*', | ||
headers: [ | ||
{ | ||
key: 'X-Frame-Options', | ||
value: 'SAMEORIGIN', | ||
}, | ||
{ | ||
key: 'Content-Security-Policy', | ||
value: `frame-ancestors 'self' https://app.contentful.com`, | ||
}, | ||
], | ||
}, | ||
]; | ||
}, | ||
}; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"private": true, | ||
"scripts": { | ||
"dev": "next", | ||
"build": "next build", | ||
"start": "next start" | ||
}, | ||
"dependencies": { | ||
"@contentful/live-preview": "^2.0.2", | ||
"next": "^13.4.2", | ||
"react": "^18.2.0", | ||
"react-dom": "^18.2.0" | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ContentfulLivePreviewProvider } from "@contentful/live-preview/react"; | ||
import "@contentful/live-preview/style.css"; | ||
|
||
function App({ Component, pageProps }) { | ||
return ( | ||
<ContentfulLivePreviewProvider locale="en-US" enableInspectorMode={pageProps.draftMode} enableLiveUpdates={pageProps.draftMode}> | ||
<Component {...pageProps} /> | ||
</ContentfulLivePreviewProvider> | ||
); | ||
} | ||
|
||
export default App; |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { Html, Head, Main, NextScript } from "next/document"; | ||
|
||
export default function Document() { | ||
return ( | ||
<Html lang="en"> | ||
<Head /> | ||
<body> | ||
<Main /> | ||
<NextScript /> | ||
</body> | ||
</Html> | ||
); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export default function handler(req, res) { | ||
res.setDraftMode({ enable: false }); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { getPreviewPostBySlug } from "../../lib/api"; | ||
|
||
export default async function draft(req, res) { | ||
const { secret, slug } = req.query; | ||
|
||
// Check the secret and next parameters | ||
// This secret should only be known to this API route and Contentful | ||
if (secret !== process.env.CONTENTFUL_PREVIEW_SECRET || !slug) { | ||
return res.status(401).json({ message: "Invalid token" }); | ||
} | ||
|
||
// Fetch the post to check if the provided `slug` exists | ||
const post = await getPreviewPostBySlug(slug); | ||
|
||
// If the slug doesn't exist prevent draft mode from being enabled | ||
if (!post) { | ||
return res.status(401).json({ message: "Invalid slug" }); | ||
} | ||
|
||
// Enable Draft Mode by setting the cookie | ||
res.setDraftMode({ enable: true }); | ||
|
||
// Redirect to the path from the fetched post | ||
// We don't redirect to req.query.slug as that might lead to open redirect vulnerabilities | ||
// res.writeHead(307, { Location: `/posts/${post.slug}` }) | ||
const url = `/posts/${post.slug}`; | ||
res.setHeader("Content-Type", "text/html"); | ||
res.write( | ||
`<!DOCTYPE html><html><head><meta http-equiv="Refresh" content="0; url=${url}" /> | ||
<script>window.location.href = '${url}'</script> | ||
</head> | ||
</html>` | ||
); | ||
res.end(); | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { getAllPostsForHome } from "../lib/api"; | ||
import Head from "next/head"; | ||
import Link from "next/link"; | ||
|
||
export default function Index({ posts }) { | ||
return ( | ||
<> | ||
<Head> | ||
<title>Contentful live preview example with Next.js</title> | ||
</Head> | ||
{posts.map((post) => ( | ||
<Link href={`/posts/${post.slug}`}>{post.title}</Link> | ||
))} | ||
</> | ||
); | ||
} | ||
|
||
export async function getStaticProps({ draftMode = false }) { | ||
const posts = (await getAllPostsForHome(draftMode)) ?? []; | ||
return { | ||
props: { posts }, | ||
}; | ||
} |
Oops, something went wrong.