@agility/create-next-app 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/.claude/settings.json +7 -0
  2. package/.claude/settings.local.json +24 -0
  3. package/FEATURE_ROADMAP.md +343 -0
  4. package/README.md +205 -0
  5. package/TESTING.md +131 -0
  6. package/bin/create-agility-app.js +48 -0
  7. package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
  8. package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
  9. package/dist/agility/api-keys/generateApiKeys.js +99 -0
  10. package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
  11. package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
  12. package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
  13. package/dist/agility/api-keys/getApiKeys.js +14 -0
  14. package/dist/agility/api-keys/getApiKeys.js.map +1 -0
  15. package/dist/agility/index.d.ts +3 -0
  16. package/dist/agility/index.d.ts.map +1 -0
  17. package/dist/agility/index.js +8 -0
  18. package/dist/agility/index.js.map +1 -0
  19. package/dist/agility/instance/createNewInstance.d.ts +8 -0
  20. package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
  21. package/dist/agility/instance/createNewInstance.js +65 -0
  22. package/dist/agility/instance/createNewInstance.js.map +1 -0
  23. package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
  24. package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
  25. package/dist/agility/instance/getAvailableInstances.js +43 -0
  26. package/dist/agility/instance/getAvailableInstances.js.map +1 -0
  27. package/dist/agility/instance/manageInstance.d.ts +9 -0
  28. package/dist/agility/instance/manageInstance.d.ts.map +1 -0
  29. package/dist/agility/instance/manageInstance.js +82 -0
  30. package/dist/agility/instance/manageInstance.js.map +1 -0
  31. package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
  32. package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
  33. package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
  34. package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
  35. package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
  36. package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
  37. package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
  38. package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
  39. package/dist/auth/index.d.ts +3 -0
  40. package/dist/auth/index.d.ts.map +1 -0
  41. package/dist/auth/index.js +8 -0
  42. package/dist/auth/index.js.map +1 -0
  43. package/dist/auth/oauth/authenticate.d.ts +6 -0
  44. package/dist/auth/oauth/authenticate.d.ts.map +1 -0
  45. package/dist/auth/oauth/authenticate.js +162 -0
  46. package/dist/auth/oauth/authenticate.js.map +1 -0
  47. package/dist/auth/oauth/constants.d.ts +5 -0
  48. package/dist/auth/oauth/constants.d.ts.map +1 -0
  49. package/dist/auth/oauth/constants.js +9 -0
  50. package/dist/auth/oauth/constants.js.map +1 -0
  51. package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
  52. package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
  53. package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
  54. package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
  55. package/dist/cli/index.d.ts +3 -0
  56. package/dist/cli/index.d.ts.map +1 -0
  57. package/dist/cli/index.js +290 -0
  58. package/dist/cli/index.js.map +1 -0
  59. package/dist/cli/promptForMissingOptions.d.ts +8 -0
  60. package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
  61. package/dist/cli/promptForMissingOptions.js +92 -0
  62. package/dist/cli/promptForMissingOptions.js.map +1 -0
  63. package/dist/config/env/createEnvFile.d.ts +6 -0
  64. package/dist/config/env/createEnvFile.d.ts.map +1 -0
  65. package/dist/config/env/createEnvFile.js +31 -0
  66. package/dist/config/env/createEnvFile.js.map +1 -0
  67. package/dist/config/index.d.ts +2 -0
  68. package/dist/config/index.d.ts.map +1 -0
  69. package/dist/config/index.js +6 -0
  70. package/dist/config/index.js.map +1 -0
  71. package/dist/config/mcp/createMcpConfig.d.ts +5 -0
  72. package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
  73. package/dist/config/mcp/createMcpConfig.js +32 -0
  74. package/dist/config/mcp/createMcpConfig.js.map +1 -0
  75. package/dist/config/packages/installAgilityPackages.d.ts +6 -0
  76. package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
  77. package/dist/config/packages/installAgilityPackages.js +61 -0
  78. package/dist/config/packages/installAgilityPackages.js.map +1 -0
  79. package/dist/config/setupProject.d.ts +8 -0
  80. package/dist/config/setupProject.d.ts.map +1 -0
  81. package/dist/config/setupProject.js +32 -0
  82. package/dist/config/setupProject.js.map +1 -0
  83. package/dist/create-next-app/createNextApp.d.ts +9 -0
  84. package/dist/create-next-app/createNextApp.d.ts.map +1 -0
  85. package/dist/create-next-app/createNextApp.js +83 -0
  86. package/dist/create-next-app/createNextApp.js.map +1 -0
  87. package/dist/create-next-app/index.d.ts +3 -0
  88. package/dist/create-next-app/index.d.ts.map +1 -0
  89. package/dist/create-next-app/index.js +8 -0
  90. package/dist/create-next-app/index.js.map +1 -0
  91. package/dist/scaffold/components/createPageComponents.d.ts +6 -0
  92. package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
  93. package/dist/scaffold/components/createPageComponents.js +62 -0
  94. package/dist/scaffold/components/createPageComponents.js.map +1 -0
  95. package/dist/scaffold/containers/createContainers.d.ts +6 -0
  96. package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
  97. package/dist/scaffold/containers/createContainers.js +48 -0
  98. package/dist/scaffold/containers/createContainers.js.map +1 -0
  99. package/dist/scaffold/index.d.ts +2 -0
  100. package/dist/scaffold/index.d.ts.map +1 -0
  101. package/dist/scaffold/index.js +6 -0
  102. package/dist/scaffold/index.js.map +1 -0
  103. package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
  104. package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
  105. package/dist/scaffold/instance/createBlankInstance.js +51 -0
  106. package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
  107. package/dist/scaffold/models/createContentModels.d.ts +6 -0
  108. package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
  109. package/dist/scaffold/models/createContentModels.js +70 -0
  110. package/dist/scaffold/models/createContentModels.js.map +1 -0
  111. package/dist/templates/copyDirectory.d.ts +5 -0
  112. package/dist/templates/copyDirectory.d.ts.map +1 -0
  113. package/dist/templates/copyDirectory.js +28 -0
  114. package/dist/templates/copyDirectory.js.map +1 -0
  115. package/dist/templates/copyTemplates.d.ts +8 -0
  116. package/dist/templates/copyTemplates.d.ts.map +1 -0
  117. package/dist/templates/copyTemplates.js +58 -0
  118. package/dist/templates/copyTemplates.js.map +1 -0
  119. package/dist/templates/index.d.ts +2 -0
  120. package/dist/templates/index.d.ts.map +1 -0
  121. package/dist/templates/index.js +6 -0
  122. package/dist/templates/index.js.map +1 -0
  123. package/dist/types/index.d.ts +50 -0
  124. package/dist/types/index.d.ts.map +1 -0
  125. package/dist/types/index.js +3 -0
  126. package/dist/types/index.js.map +1 -0
  127. package/dist/utils/git.d.ts +9 -0
  128. package/dist/utils/git.d.ts.map +1 -0
  129. package/dist/utils/git.js +71 -0
  130. package/dist/utils/git.js.map +1 -0
  131. package/dist/utils/validation.d.ts +45 -0
  132. package/dist/utils/validation.d.ts.map +1 -0
  133. package/dist/utils/validation.js +180 -0
  134. package/dist/utils/validation.js.map +1 -0
  135. package/package.json +45 -0
  136. package/src/agility/api-keys/generateApiKeys.ts +100 -0
  137. package/src/agility/api-keys/getApiKeys.ts +13 -0
  138. package/src/agility/index.ts +3 -0
  139. package/src/agility/instance/createNewInstance.ts +67 -0
  140. package/src/agility/instance/getAvailableInstances.ts +49 -0
  141. package/src/agility/instance/manageInstance.ts +90 -0
  142. package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
  143. package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
  144. package/src/auth/index.ts +3 -0
  145. package/src/auth/oauth/authenticate.ts +165 -0
  146. package/src/auth/oauth/constants.ts +6 -0
  147. package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
  148. package/src/cli/index.ts +281 -0
  149. package/src/cli/promptForMissingOptions.ts +104 -0
  150. package/src/config/env/createEnvFile.ts +30 -0
  151. package/src/config/index.ts +2 -0
  152. package/src/config/mcp/createMcpConfig.ts +30 -0
  153. package/src/config/packages/installAgilityPackages.ts +63 -0
  154. package/src/config/setupProject.ts +31 -0
  155. package/src/create-next-app/createNextApp.ts +75 -0
  156. package/src/create-next-app/index.ts +3 -0
  157. package/src/scaffold/components/createPageComponents.ts +74 -0
  158. package/src/scaffold/containers/createContainers.ts +55 -0
  159. package/src/scaffold/index.ts +2 -0
  160. package/src/scaffold/instance/createBlankInstance.ts +55 -0
  161. package/src/scaffold/models/createContentModels.ts +83 -0
  162. package/src/templates/copyDirectory.ts +24 -0
  163. package/src/templates/copyTemplates.ts +57 -0
  164. package/src/templates/index.ts +2 -0
  165. package/src/types/index.ts +55 -0
  166. package/src/utils/git.ts +74 -0
  167. package/src/utils/validation.ts +184 -0
  168. package/templates/.claude/QUICK-START.md +230 -0
  169. package/templates/.claude/README.md +32 -0
  170. package/templates/.claude/settings.json +8 -0
  171. package/templates/BLANK-INSTANCE-SETUP.md +375 -0
  172. package/templates/DEVELOPMENT.md +160 -0
  173. package/templates/EXAMPLE-PROMPTS.md +643 -0
  174. package/templates/PROMPTS.md +410 -0
  175. package/templates/README.md +281 -0
  176. package/templates/agents.md +429 -0
  177. package/templates/app/[locale]/[...slug]/error.tsx +17 -0
  178. package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
  179. package/templates/app/[locale]/[...slug]/page.tsx +102 -0
  180. package/templates/app/[locale]/layout.tsx +22 -0
  181. package/templates/app/[locale]/page.tsx +12 -0
  182. package/templates/app/api/dynamic-redirect/route.ts +24 -0
  183. package/templates/app/api/preview/exit/route.ts +34 -0
  184. package/templates/app/api/preview/route.ts +63 -0
  185. package/templates/app/api/revalidate/route.ts +118 -0
  186. package/templates/components/agility-components/RichTextArea.tsx +66 -0
  187. package/templates/components/agility-components/index.ts +30 -0
  188. package/templates/components/agility-pages/MainTemplate.tsx +36 -0
  189. package/templates/components/agility-pages/index.ts +11 -0
  190. package/templates/docs/01-agility-cms-overview.md +139 -0
  191. package/templates/docs/02-page-routing.md +251 -0
  192. package/templates/docs/03-creating-components.md +462 -0
  193. package/templates/docs/04-data-fetching.md +484 -0
  194. package/templates/docs/05-containers-and-lists.md +596 -0
  195. package/templates/docs/06-localization.md +561 -0
  196. package/templates/docs/07-caching-strategies.md +410 -0
  197. package/templates/docs/08-common-components.md +756 -0
  198. package/templates/docs/09-whats-included.md +279 -0
  199. package/templates/docs/10-mcp-server-setup.md +153 -0
  200. package/templates/docs/11-linked-nested-content.md +611 -0
  201. package/templates/docs/README.md +164 -0
  202. package/templates/lib/cms/getAgilityContext.ts +28 -0
  203. package/templates/lib/cms/getAgilityPage.ts +51 -0
  204. package/templates/lib/cms/getAgilitySDK.ts +22 -0
  205. package/templates/lib/cms/getContentItem.ts +20 -0
  206. package/templates/lib/cms/getContentList.ts +19 -0
  207. package/templates/lib/cms/getRedirections.ts +85 -0
  208. package/templates/lib/cms/getSitemapFlat.ts +19 -0
  209. package/templates/lib/cms/getSitemapNested.ts +19 -0
  210. package/templates/lib/env.ts +99 -0
  211. package/templates/lib/i18n/config.ts +28 -0
  212. package/templates/proxy.ts +101 -0
  213. package/tsconfig.json +21 -0
@@ -0,0 +1,484 @@
1
+ # Data Fetching from Agility CMS
2
+
3
+ This document explains how to fetch content from Agility CMS in your Next.js application.
4
+
5
+ ## Helper Functions
6
+
7
+ The project includes helper functions in `src/lib/cms/` for fetching data:
8
+
9
+ ### 1. `getAgilitySDK()`
10
+ Initializes the Agility SDK with proper configuration.
11
+
12
+ ```typescript
13
+ import { getAgilitySDK } from "@/lib/cms/getAgilitySDK";
14
+
15
+ const agilityClient = getAgilitySDK({ locale: "en-us" });
16
+ ```
17
+
18
+ ### 2. `getAgilityPage()`
19
+ Fetches complete page data including modules and zones.
20
+
21
+ ```typescript
22
+ import { getAgilityPage } from "@/lib/cms/getAgilityPage";
23
+
24
+ const pageData = await getAgilityPage({
25
+ params: { locale: "en-us", slug: ["about"] }
26
+ });
27
+
28
+ // Returns:
29
+ // {
30
+ // page: { pageID, title, seo, zones, ... },
31
+ // sitemap: [...],
32
+ // sitemapNode: { path, title, ... },
33
+ // pageTemplateName: "MainTemplate",
34
+ // globalData: { ... }
35
+ // }
36
+ ```
37
+
38
+ ### 3. `getContentItem()`
39
+ Fetches a single content item by ID or reference name.
40
+
41
+ ```typescript
42
+ import { getContentItem } from "@/lib/cms/getContentItem";
43
+
44
+ // By contentID
45
+ const post = await getContentItem({
46
+ contentID: 123,
47
+ locale: "en-us",
48
+ });
49
+
50
+ // Returns:
51
+ // {
52
+ // contentID: 123,
53
+ // fields: {
54
+ // title: "My Blog Post",
55
+ // content: "<p>...</p>",
56
+ // date: "2024-01-15",
57
+ // ...
58
+ // }
59
+ // }
60
+ ```
61
+
62
+ ### 4. `getContentList()`
63
+ Fetches a collection of content items.
64
+
65
+ ```typescript
66
+ import { getContentList } from "@/lib/cms/getContentList";
67
+
68
+ const posts = await getContentList({
69
+ referenceName: "posts",
70
+ locale: "en-us",
71
+ sort: "fields.date desc",
72
+ take: 10,
73
+ skip: 0,
74
+ });
75
+
76
+ // Returns array:
77
+ // [
78
+ // { contentID: 1, fields: { ... } },
79
+ // { contentID: 2, fields: { ... } },
80
+ // ...
81
+ // ]
82
+ ```
83
+
84
+ ### 5. `getSitemapFlat()`
85
+ Fetches the flat sitemap (all pages as a dictionary).
86
+
87
+ ```typescript
88
+ import { getSitemapFlat } from "@/lib/cms/getSitemapFlat";
89
+
90
+ const sitemap = await getSitemapFlat({ locale: "en-us" });
91
+
92
+ // Returns:
93
+ // {
94
+ // "/": { pageID: 1, path: "/", title: "Home", ... },
95
+ // "/about": { pageID: 2, path: "/about", title: "About", ... },
96
+ // "/blog/post-1": { pageID: 3, path: "/blog/post-1", title: "Post 1", ... },
97
+ // }
98
+ ```
99
+
100
+ ### 6. `getSitemapNested()`
101
+ Fetches the nested sitemap (hierarchical structure).
102
+
103
+ ```typescript
104
+ import { getSitemapNested } from "@/lib/cms/getSitemapNested";
105
+
106
+ const sitemap = await getSitemapNested({ locale: "en-us" });
107
+
108
+ // Returns nested structure:
109
+ // [
110
+ // {
111
+ // pageID: 1,
112
+ // path: "/",
113
+ // title: "Home",
114
+ // children: [
115
+ // { pageID: 2, path: "/about", title: "About", children: [] },
116
+ // { pageID: 3, path: "/blog", title: "Blog", children: [...] }
117
+ // ]
118
+ // }
119
+ // ]
120
+ ```
121
+
122
+ ### 7. `getRedirections()`
123
+ Fetches redirects configured in Agility CMS.
124
+
125
+ ```typescript
126
+ import { getRedirections } from "@/lib/cms/getRedirections";
127
+
128
+ const redirects = await getRedirections({ locale: "en-us" });
129
+
130
+ // Returns:
131
+ // [
132
+ // { url: "/old-page", redirectUrl: "/new-page", statusCode: 301 },
133
+ // ...
134
+ // ]
135
+ ```
136
+
137
+ ## Caching
138
+
139
+ All CMS functions automatically cache data using Next.js caching with tags:
140
+
141
+ ```typescript
142
+ // Example from getContentItem.ts
143
+ agilityClient.config.fetchConfig = {
144
+ next: {
145
+ tags: [`agility-content-${contentID}-${locale}`],
146
+ revalidate: 60, // Revalidate every 60 seconds
147
+ },
148
+ };
149
+ ```
150
+
151
+ ### Cache Tags Format
152
+
153
+ - **Content Item**: `agility-content-{contentID}-{locale}`
154
+ - **Content List**: `agility-content-{referenceName}-{locale}`
155
+ - **Sitemap**: `agility-sitemap-flat-{locale}` or `agility-sitemap-nested-{locale}`
156
+ - **Page**: `agility-page-{pageID}-{locale}`
157
+
158
+ ### On-Demand Revalidation
159
+
160
+ Use the `/api/revalidate` endpoint with a webhook:
161
+
162
+ ```typescript
163
+ // Webhook from Agility CMS
164
+ POST /api/revalidate
165
+ {
166
+ "tag": "agility-content-123-en-us",
167
+ "securityKey": "your-security-key"
168
+ }
169
+ ```
170
+
171
+ This immediately revalidates the cached content.
172
+
173
+ ## Common Patterns
174
+
175
+ ### Pattern 1: Fetch in Server Component
176
+
177
+ ```tsx
178
+ import { getContentList } from "@/lib/cms/getContentList";
179
+
180
+ export default async function BlogPage({ params }: any) {
181
+ const { locale } = await params;
182
+
183
+ const posts = await getContentList({
184
+ referenceName: "posts",
185
+ locale,
186
+ take: 20,
187
+ });
188
+
189
+ return (
190
+ <div>
191
+ {posts.map((post) => (
192
+ <article key={post.contentID}>
193
+ <h2>{post.fields.title}</h2>
194
+ <p>{post.fields.excerpt}</p>
195
+ </article>
196
+ ))}
197
+ </div>
198
+ );
199
+ }
200
+ ```
201
+
202
+ ### Pattern 2: Nested Content Fetching
203
+
204
+ When a module references a content list by reference name (not contentID):
205
+
206
+ ```tsx
207
+ import { getContentList } from "@/lib/cms/getContentList";
208
+
209
+ export default async function CardGrid({ module, locale }: any) {
210
+ const { title, cards } = module.fields;
211
+
212
+ // cards.referenceName is the list reference name
213
+ const cardItems = await getContentList({
214
+ referenceName: cards.referenceName, // e.g., "testimonials"
215
+ locale,
216
+ });
217
+
218
+ return (
219
+ <div>
220
+ <h2>{title}</h2>
221
+ <div className="grid grid-cols-3 gap-4">
222
+ {cardItems.map((card) => (
223
+ <div key={card.contentID}>
224
+ <h3>{card.fields.title}</h3>
225
+ <p>{card.fields.description}</p>
226
+ </div>
227
+ ))}
228
+ </div>
229
+ </div>
230
+ );
231
+ }
232
+ ```
233
+
234
+ **Important**: Always use `referenceName`, not `contentID` when fetching lists.
235
+
236
+ ### Pattern 3: Pagination
237
+
238
+ ```tsx
239
+ import { getContentList } from "@/lib/cms/getContentList";
240
+
241
+ interface BlogListingProps {
242
+ searchParams?: {
243
+ page?: string;
244
+ };
245
+ }
246
+
247
+ export default async function BlogListing({ searchParams }: BlogListingProps) {
248
+ const page = parseInt(searchParams?.page || "1");
249
+ const perPage = 10;
250
+
251
+ const posts = await getContentList({
252
+ referenceName: "posts",
253
+ locale: "en-us",
254
+ take: perPage,
255
+ skip: (page - 1) * perPage,
256
+ sort: "fields.date desc",
257
+ });
258
+
259
+ return (
260
+ <div>
261
+ {posts.map((post) => (
262
+ <article key={post.contentID}>{/* ... */}</article>
263
+ ))}
264
+ <nav>
265
+ <a href={`?page=${page - 1}`}>Previous</a>
266
+ <a href={`?page=${page + 1}`}>Next</a>
267
+ </nav>
268
+ </div>
269
+ );
270
+ }
271
+ ```
272
+
273
+ ### Pattern 4: Filtering and Sorting
274
+
275
+ ```tsx
276
+ import { getContentList } from "@/lib/cms/getContentList";
277
+
278
+ export default async function PostsByCategory({ categoryId, locale }: any) {
279
+ // Fetch all posts
280
+ const allPosts = await getContentList({
281
+ referenceName: "posts",
282
+ locale,
283
+ sort: "fields.date desc",
284
+ });
285
+
286
+ // Filter by category (client-side filtering)
287
+ const filteredPosts = allPosts.filter(
288
+ (post) => post.fields.category?.contentID === categoryId
289
+ );
290
+
291
+ // Or use OData-style filter (server-side)
292
+ const posts = await getContentList({
293
+ referenceName: "posts",
294
+ locale,
295
+ filter: `fields.category.contentID eq ${categoryId}`,
296
+ sort: "fields.date desc",
297
+ });
298
+
299
+ return (
300
+ <div>
301
+ {posts.map((post) => (
302
+ <article key={post.contentID}>{/* ... */}</article>
303
+ ))}
304
+ </div>
305
+ );
306
+ }
307
+ ```
308
+
309
+ ### Pattern 5: Fetch Related Content
310
+
311
+ ```tsx
312
+ import { getContentItem } from "@/lib/cms/getContentItem";
313
+
314
+ export default async function BlogPost({ postId, locale }: any) {
315
+ // Fetch the blog post
316
+ const post = await getContentItem({
317
+ contentID: postId,
318
+ locale,
319
+ });
320
+
321
+ // Fetch related content
322
+ const author = post.fields.author?.contentID
323
+ ? await getContentItem({
324
+ contentID: post.fields.author.contentID,
325
+ locale,
326
+ })
327
+ : null;
328
+
329
+ const category = post.fields.category?.contentID
330
+ ? await getContentItem({
331
+ contentID: post.fields.category.contentID,
332
+ locale,
333
+ })
334
+ : null;
335
+
336
+ return (
337
+ <article>
338
+ <h1>{post.fields.title}</h1>
339
+ {author && <p>By {author.fields.name}</p>}
340
+ {category && <span className="badge">{category.fields.name}</span>}
341
+ <div dangerouslySetInnerHTML={{ __html: post.fields.content }} />
342
+ </article>
343
+ );
344
+ }
345
+ ```
346
+
347
+ ### Pattern 6: Parallel Fetching
348
+
349
+ ```tsx
350
+ import { getContentList } from "@/lib/cms/getContentList";
351
+
352
+ export default async function Homepage({ locale }: any) {
353
+ // Fetch multiple lists in parallel
354
+ const [posts, testimonials, stats] = await Promise.all([
355
+ getContentList({ referenceName: "posts", locale, take: 3 }),
356
+ getContentList({ referenceName: "testimonials", locale, take: 5 }),
357
+ getContentList({ referenceName: "stats", locale }),
358
+ ]);
359
+
360
+ return (
361
+ <div>
362
+ <section>
363
+ <h2>Latest Posts</h2>
364
+ {posts.map((post) => (/* ... */))}
365
+ </section>
366
+ <section>
367
+ <h2>Testimonials</h2>
368
+ {testimonials.map((t) => (/* ... */))}
369
+ </section>
370
+ <section>
371
+ <h2>Company Stats</h2>
372
+ {stats.map((s) => (/* ... */))}
373
+ </section>
374
+ </div>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ## Error Handling
380
+
381
+ Always handle potential errors:
382
+
383
+ ```tsx
384
+ import { getContentItem } from "@/lib/cms/getContentItem";
385
+
386
+ export default async function ContentDisplay({ contentID, locale }: any) {
387
+ try {
388
+ const content = await getContentItem({ contentID, locale });
389
+
390
+ if (!content) {
391
+ return <div>Content not found</div>;
392
+ }
393
+
394
+ return <div>{content.fields.title}</div>;
395
+ } catch (error) {
396
+ console.error("Error fetching content:", error);
397
+ return <div>Error loading content</div>;
398
+ }
399
+ }
400
+ ```
401
+
402
+ ## Preview Mode
403
+
404
+ In preview mode, the SDK automatically uses the preview API key:
405
+
406
+ ```typescript
407
+ // In getAgilitySDK.ts
408
+ import { cookies } from "next/headers";
409
+
410
+ export async function getAgilitySDK({ locale }: { locale: string }) {
411
+ const isPreview = (await cookies()).get("agilitypreview")?.value === "true";
412
+ const apiKey = isPreview
413
+ ? process.env.AGILITY_API_PREVIEW_KEY
414
+ : process.env.AGILITY_API_FETCH_KEY;
415
+
416
+ return agilitySDK.getApi({
417
+ guid: process.env.AGILITY_GUID,
418
+ apiKey,
419
+ isPreview,
420
+ });
421
+ }
422
+ ```
423
+
424
+ This allows editors to preview draft content before publishing.
425
+
426
+ ## TypeScript Types
427
+
428
+ Define types for your content:
429
+
430
+ ```typescript
431
+ // types/agility.ts
432
+
433
+ export interface BlogPost {
434
+ contentID: number;
435
+ fields: {
436
+ title: string;
437
+ slug: string;
438
+ excerpt: string;
439
+ content: string;
440
+ date: string;
441
+ author: {
442
+ contentID: number;
443
+ fields: {
444
+ name: string;
445
+ bio: string;
446
+ avatar: { url: string };
447
+ };
448
+ };
449
+ category: {
450
+ contentID: number;
451
+ fields: {
452
+ name: string;
453
+ slug: string;
454
+ };
455
+ };
456
+ image: {
457
+ url: string;
458
+ label: string;
459
+ };
460
+ };
461
+ }
462
+
463
+ // Usage
464
+ const posts = await getContentList<BlogPost>({
465
+ referenceName: "posts",
466
+ locale: "en-us",
467
+ });
468
+ ```
469
+
470
+ ## Best Practices
471
+
472
+ 1. **Always Specify Locale**: Pass locale to all CMS functions
473
+ 2. **Use Cache Tags**: Leverage automatic cache tagging for revalidation
474
+ 3. **Parallel Fetching**: Use `Promise.all()` for independent fetches
475
+ 4. **Error Boundaries**: Wrap components in error boundaries
476
+ 5. **Loading States**: Use Suspense for streaming
477
+ 6. **Type Safety**: Define TypeScript interfaces for content
478
+ 7. **Optimize Queries**: Only fetch what you need (use `take` parameter)
479
+
480
+ ## Next Steps
481
+
482
+ - Read [05-containers-and-lists.md](./05-containers-and-lists.md) for content list patterns
483
+ - Read [06-localization.md](./06-localization.md) for multi-locale data
484
+ - Read [07-caching-strategies.md](./07-caching-strategies.md) for advanced caching