-
Notifications
You must be signed in to change notification settings - Fork 2
Contentful
To install contentful to your template, you must add dependencies and define some files first.
Dependencies:
yarn add contentful
The data model is our fancy way of saying predefined structure representing your whole page / series of components / whatever big piece of your application you want to query for. Usually, it is a whole page, layout or even all buttons.
To query your data model you need first a function to query data. Contentful came with a solution with their own package to getEntries
with your query.
Usually in your application you only need two ways to query your content models. Query one content model or query all content models. To work better with typescript we come with a solution:
First you need a universal function to query data:
import type { Entry } from 'contentful'
import type { CONTENT_TYPE } from '$/types/generated/contentful'
const contentful = require('contentful')
const spaceId = process.env.CONTENTFUL_SPACE_ID
const accessToken = process.env.CONTENTFUL_ACCESS_TOKEN
if (!(spaceId && accessToken)) {
throw Error(
'Please configure your .env file with contentful or disable it for your project!',
)
}
const client = contentful.createClient({
space: spaceId,
accessToken: accessToken,
})
const getData = async (
contentType: CONTENT_TYPE,
): Promise<Entry<CONTENT_TYPE>[]> => {
const TypeEntries = await client.getEntries<CONTENT_TYPE>({
content_type: contentType,
include: 10,
})
return TypeEntries.items
}
export default getData
And then two ways for querying:
import type { CONTENT_TYPE } from '$/types/generated/contentful'
import getData from './getData'
const getOneContentfulData = async <T>(
contentType: CONTENT_TYPE,
): Promise<T> => {
const data = await getData(contentType)
/**
* JSON parse and stringify is used here to eliminate unexpected behavior from contentful
**/
return JSON.parse(
JSON.stringify({
fields: data[0]?.fields,
sys: data[0]?.sys,
}),
)
}
export default getOneContentfulData
import type { CONTENT_TYPE } from '$/types/generated/contentful'
import getData from './getData'
const getManyContentfulData = async <T>(
contentType: CONTENT_TYPE,
): Promise<T[]> => {
const data = await getData(contentType)
/**
* JSON parse and stringify is used here to eliminate unexpected behavior from contentful
**/
return Object.values(
JSON.parse(
JSON.stringify({
...data,
}),
),
)
}
export default getManyContentfulData
The way we are splitting this so much is because it will become much more readable at the end. Your final data model would look like this:
import type {
ILayout,
IHeader,
IFooter,
IButton,
} from '$/types/generated/contentful'
import getManyContentfulData from '../getManyContentfulData'
import getOneContentfulData from '../getOneContentfulData'
export interface ILayoutData {
layout: ILayout
header: IHeader
footer: IFooter
buttons: IButton[]
}
const getLayoutData = async (): Promise<ILayoutData> => {
const [layout, header, footer, buttons] = await Promise.all([
getOneContentfulData<ILayout>('layout'),
getOneContentfulData<IHeader>('header'),
getOneContentfulData<IFooter>('footer'),
getManyContentfulData<IButton>('button'),
])
return {
layout,
header,
footer,
buttons,
}
}
export default getLayoutData
To operate on types as we in examples above are highly suggesting and operating, we can use a library to generate types from the contentful itself.
Add this package as dev dependency:
yarn add --dev contentful-management contentful-typescript-codegen
Add command to your package.json:
"contentful-typescript-codegen": "contentful-typescript-codegen --output @types/generated/contentful.d.ts && eslint --quiet --fix ./@types/generated/contentful.d.ts",
Then your script file:
require('dotenv').config()
const contentfulManagement = require('contentful-management')
module.exports = function () {
const contentfulClient = contentfulManagement.createClient({
accessToken: process.env.CONTENTFUL_MANAGEMENT_API_ACCESS_TOKEN
})
return contentfulClient
.getSpace(process.env.CONTENTFUL_SPACE_ID)
.then(space => space.getEnvironment(process.env.CONTENTFUL_ENVIRONMENT))
}
In this script, we are using a couple of envs that you can get from the contentful service.
You can use getStaticProps
to query contentful on the initial build:
export interface IHomePageProps extends ILayoutData {}
export const getStaticProps: GetStaticProps<IHomePageProps> = async () => {
const layout = await getLayoutData()
return {
props: {
layout,
},
}
}
Plain and simple 🎉
Easy to use 🌈
Flexible ⭐
Filled with example data for context:
CONTENTFUL_SPACE_ID=gdewij512zpj
CONTENTFUL_ACCESS_TOKEN=RWpH4ANMXwFBDgh5jmlaC65qAeAtfGgZa_D5Pn2FtZY
CONTENTFUL_MANAGEMENT_API_ACCESS_TOKEN=CGAAP-iYL24G_V-2BQ2kl_VJMlzXuTBHnHQiM6JCq4-kD9Jl4
CONTENTFUL_ENVIRONMENT=master
import type { Asset } from 'contentful'
import Image from 'next/image'
/**
* An easy way to include Contentful Image with Next.js Image tag
*
* @example
* Where `data.fields.icon` contains an Asset from Contentful
* <ContentfulImage {...data.fields.icon} />
**/
const ContentfulImage = (props: Asset) => {
if (!props.fields.file.details.image?.width) {
return null
}
return (
<Image
src={'https:' + props.fields.file.url}
alt={props.fields.title}
width={props.fields.file.details.image?.width}
height={props.fields.file.details.image?.height}
/>
)
}
export default ContentfulImage