-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathgraphql.js
213 lines (192 loc) · 5.71 KB
/
graphql.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
import Head from "next/head";
import { documentToReactComponents } from "@contentful/rich-text-react-renderer";
import { BLOCKS, INLINES } from "@contentful/rich-text-types";
// Create a bespoke renderOptions object to target BLOCKS.EMBEDDED_ENTRY (linked block entries e.g. code blocks)
// INLINES.EMBEDDED_ENTRY (linked inline entries e.g. a reference to another blog post)
// and BLOCKS.EMBEDDED_ASSET (linked assets e.g. images)
function renderOptions(links) {
// create an asset map
const assetMap = new Map();
// loop through the assets and add them to the map
for (const asset of links.assets.block) {
assetMap.set(asset.sys.id, asset);
}
// create an entry map
const entryMap = new Map();
// loop through the block linked entries and add them to the map
for (const entry of links.entries.block) {
entryMap.set(entry.sys.id, entry);
}
// loop through the inline linked entries and add them to the map
for (const entry of links.entries.inline) {
entryMap.set(entry.sys.id, entry);
}
return {
renderNode: {
[INLINES.EMBEDDED_ENTRY]: (node, children) => {
// find the entry in the entryMap by ID
const entry = entryMap.get(node.data.target.sys.id);
// render the entries as needed
if (entry.__typename === "BlogPost") {
return <a href={`/blog/${entry.slug}`}>{entry.title}</a>;
}
},
[BLOCKS.EMBEDDED_ENTRY]: (node, children) => {
// find the entry in the entryMap by ID
const entry = entryMap.get(node.data.target.sys.id);
// render the entries as needed
if (entry.__typename === "CodeBlock") {
return (
<pre>
<code>{entry.code}</code>
</pre>
);
}
// render the entries as needed by looking at the __typename
// referenced in the GraphQL query
if (entry.__typename === "VideoEmbed") {
return (
<iframe
src={entry.embedUrl}
height="100%"
width="100%"
frameBorder="0"
scrolling="no"
title={entry.title}
allowFullScreen={true}
/>
);
}
},
[BLOCKS.EMBEDDED_ASSET]: (node, next) => {
// find the asset in the assetMap by ID
const asset = assetMap.get(node.data.target.sys.id);
// render the asset accordingly
return (
<img src={asset.url} height={asset.height} width={asset.width} alt={asset.description} />
);
},
},
};
}
export default function GraphQL(props) {
const { post } = props;
return (
<>
<Head>
<title>Contentful GraphQL API Example</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>{documentToReactComponents(post.body.json, renderOptions(post.body.links))}</main>
</>
);
}
/**
* Construct the GraphQL query
* Define all fields you want to query on the content type
*
* IMPORTANT:
* `body.json` returns a node list (e.g. paragraphs, headings) that also includes REFERENCES nodes to assets and entries.
* These reference nodes will not be returned with the full data set included from the GraphQL API.
* To ensure you query the full asset/entry data, ensure you include the fields you want on the content types for the
* linked entries and assets under body.links.entries, and body.links.assets.
*
* The example below shows how to query body.links.entries and body.links.assets for this particular content model.
*/
export async function getStaticProps() {
const query = `{
blogPostCollection(limit: 1, where: {slug: "the-power-of-the-contentful-rich-text-field"}) {
items {
sys {
id
}
date
title
slug
excerpt
tags
externalUrl
author {
name
description
gitHubUsername
twitchUsername
twitterUsername
websiteUrl
image {
sys {
id
}
url
title
width
height
description
}
}
body {
json
links {
entries {
inline {
sys {
id
}
__typename
... on BlogPost {
title
slug
}
}
block {
sys {
id
}
__typename
... on CodeBlock {
description
language
code
}
... on VideoEmbed {
embedUrl
title
}
}
}
assets {
block {
sys {
id
}
url
title
width
height
description
}
}
}
}
}
}
}`;
// Construct the fetch options
const fetchUrl = `https://graphql.contentful.com/content/v1/spaces/${process.env.NEXT_PUBLIC_CONTENTFUL_SPACE_ID}`;
const fetchOptions = {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.NEXT_PUBLIC_CONTENTFUL_ACCESS_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({ query }),
};
// Make a call to fetch the data
const response = await fetch(fetchUrl, fetchOptions).then((response) => response.json());
const post = response.data.blogPostCollection.items ? response.data.blogPostCollection.items : [];
return {
props: {
post: post.pop(),
},
};
}