@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.
- package/.claude/settings.json +7 -0
- package/.claude/settings.local.json +24 -0
- package/FEATURE_ROADMAP.md +343 -0
- package/README.md +205 -0
- package/TESTING.md +131 -0
- package/bin/create-agility-app.js +48 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/generateApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/generateApiKeys.js +99 -0
- package/dist/agility/api-keys/generateApiKeys.js.map +1 -0
- package/dist/agility/api-keys/getApiKeys.d.ts +9 -0
- package/dist/agility/api-keys/getApiKeys.d.ts.map +1 -0
- package/dist/agility/api-keys/getApiKeys.js +14 -0
- package/dist/agility/api-keys/getApiKeys.js.map +1 -0
- package/dist/agility/index.d.ts +3 -0
- package/dist/agility/index.d.ts.map +1 -0
- package/dist/agility/index.js +8 -0
- package/dist/agility/index.js.map +1 -0
- package/dist/agility/instance/createNewInstance.d.ts +8 -0
- package/dist/agility/instance/createNewInstance.d.ts.map +1 -0
- package/dist/agility/instance/createNewInstance.js +65 -0
- package/dist/agility/instance/createNewInstance.js.map +1 -0
- package/dist/agility/instance/getAvailableInstances.d.ts +8 -0
- package/dist/agility/instance/getAvailableInstances.d.ts.map +1 -0
- package/dist/agility/instance/getAvailableInstances.js +43 -0
- package/dist/agility/instance/getAvailableInstances.js.map +1 -0
- package/dist/agility/instance/manageInstance.d.ts +9 -0
- package/dist/agility/instance/manageInstance.d.ts.map +1 -0
- package/dist/agility/instance/manageInstance.js +82 -0
- package/dist/agility/instance/manageInstance.js.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts +20 -0
- package/dist/agility/utils/getMgmtAPIUrl.d.ts.map +1 -0
- package/dist/agility/utils/getMgmtAPIUrl.js +61 -0
- package/dist/agility/utils/getMgmtAPIUrl.js.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts +6 -0
- package/dist/auth/api-key/authenticateWithApiKey.d.ts.map +1 -0
- package/dist/auth/api-key/authenticateWithApiKey.js +28 -0
- package/dist/auth/api-key/authenticateWithApiKey.js.map +1 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth/authenticate.d.ts +6 -0
- package/dist/auth/oauth/authenticate.d.ts.map +1 -0
- package/dist/auth/oauth/authenticate.js +162 -0
- package/dist/auth/oauth/authenticate.js.map +1 -0
- package/dist/auth/oauth/constants.d.ts +5 -0
- package/dist/auth/oauth/constants.d.ts.map +1 -0
- package/dist/auth/oauth/constants.js +9 -0
- package/dist/auth/oauth/constants.js.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts +7 -0
- package/dist/auth/oauth/exchangeCodeForToken.d.ts.map +1 -0
- package/dist/auth/oauth/exchangeCodeForToken.js +39 -0
- package/dist/auth/oauth/exchangeCodeForToken.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +290 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/promptForMissingOptions.d.ts +8 -0
- package/dist/cli/promptForMissingOptions.d.ts.map +1 -0
- package/dist/cli/promptForMissingOptions.js +92 -0
- package/dist/cli/promptForMissingOptions.js.map +1 -0
- package/dist/config/env/createEnvFile.d.ts +6 -0
- package/dist/config/env/createEnvFile.d.ts.map +1 -0
- package/dist/config/env/createEnvFile.js +31 -0
- package/dist/config/env/createEnvFile.js.map +1 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +6 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp/createMcpConfig.d.ts +5 -0
- package/dist/config/mcp/createMcpConfig.d.ts.map +1 -0
- package/dist/config/mcp/createMcpConfig.js +32 -0
- package/dist/config/mcp/createMcpConfig.js.map +1 -0
- package/dist/config/packages/installAgilityPackages.d.ts +6 -0
- package/dist/config/packages/installAgilityPackages.d.ts.map +1 -0
- package/dist/config/packages/installAgilityPackages.js +61 -0
- package/dist/config/packages/installAgilityPackages.js.map +1 -0
- package/dist/config/setupProject.d.ts +8 -0
- package/dist/config/setupProject.d.ts.map +1 -0
- package/dist/config/setupProject.js +32 -0
- package/dist/config/setupProject.js.map +1 -0
- package/dist/create-next-app/createNextApp.d.ts +9 -0
- package/dist/create-next-app/createNextApp.d.ts.map +1 -0
- package/dist/create-next-app/createNextApp.js +83 -0
- package/dist/create-next-app/createNextApp.js.map +1 -0
- package/dist/create-next-app/index.d.ts +3 -0
- package/dist/create-next-app/index.d.ts.map +1 -0
- package/dist/create-next-app/index.js +8 -0
- package/dist/create-next-app/index.js.map +1 -0
- package/dist/scaffold/components/createPageComponents.d.ts +6 -0
- package/dist/scaffold/components/createPageComponents.d.ts.map +1 -0
- package/dist/scaffold/components/createPageComponents.js +62 -0
- package/dist/scaffold/components/createPageComponents.js.map +1 -0
- package/dist/scaffold/containers/createContainers.d.ts +6 -0
- package/dist/scaffold/containers/createContainers.d.ts.map +1 -0
- package/dist/scaffold/containers/createContainers.js +48 -0
- package/dist/scaffold/containers/createContainers.js.map +1 -0
- package/dist/scaffold/index.d.ts +2 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +6 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts +8 -0
- package/dist/scaffold/instance/createBlankInstance.d.ts.map +1 -0
- package/dist/scaffold/instance/createBlankInstance.js +51 -0
- package/dist/scaffold/instance/createBlankInstance.js.map +1 -0
- package/dist/scaffold/models/createContentModels.d.ts +6 -0
- package/dist/scaffold/models/createContentModels.d.ts.map +1 -0
- package/dist/scaffold/models/createContentModels.js +70 -0
- package/dist/scaffold/models/createContentModels.js.map +1 -0
- package/dist/templates/copyDirectory.d.ts +5 -0
- package/dist/templates/copyDirectory.d.ts.map +1 -0
- package/dist/templates/copyDirectory.js +28 -0
- package/dist/templates/copyDirectory.js.map +1 -0
- package/dist/templates/copyTemplates.d.ts +8 -0
- package/dist/templates/copyTemplates.d.ts.map +1 -0
- package/dist/templates/copyTemplates.js +58 -0
- package/dist/templates/copyTemplates.js.map +1 -0
- package/dist/templates/index.d.ts +2 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +6 -0
- package/dist/templates/index.js.map +1 -0
- package/dist/types/index.d.ts +50 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +71 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/validation.d.ts +45 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +180 -0
- package/dist/utils/validation.js.map +1 -0
- package/package.json +45 -0
- package/src/agility/api-keys/generateApiKeys.ts +100 -0
- package/src/agility/api-keys/getApiKeys.ts +13 -0
- package/src/agility/index.ts +3 -0
- package/src/agility/instance/createNewInstance.ts +67 -0
- package/src/agility/instance/getAvailableInstances.ts +49 -0
- package/src/agility/instance/manageInstance.ts +90 -0
- package/src/agility/utils/getMgmtAPIUrl.ts +68 -0
- package/src/auth/api-key/authenticateWithApiKey.ts +24 -0
- package/src/auth/index.ts +3 -0
- package/src/auth/oauth/authenticate.ts +165 -0
- package/src/auth/oauth/constants.ts +6 -0
- package/src/auth/oauth/exchangeCodeForToken.ts +43 -0
- package/src/cli/index.ts +281 -0
- package/src/cli/promptForMissingOptions.ts +104 -0
- package/src/config/env/createEnvFile.ts +30 -0
- package/src/config/index.ts +2 -0
- package/src/config/mcp/createMcpConfig.ts +30 -0
- package/src/config/packages/installAgilityPackages.ts +63 -0
- package/src/config/setupProject.ts +31 -0
- package/src/create-next-app/createNextApp.ts +75 -0
- package/src/create-next-app/index.ts +3 -0
- package/src/scaffold/components/createPageComponents.ts +74 -0
- package/src/scaffold/containers/createContainers.ts +55 -0
- package/src/scaffold/index.ts +2 -0
- package/src/scaffold/instance/createBlankInstance.ts +55 -0
- package/src/scaffold/models/createContentModels.ts +83 -0
- package/src/templates/copyDirectory.ts +24 -0
- package/src/templates/copyTemplates.ts +57 -0
- package/src/templates/index.ts +2 -0
- package/src/types/index.ts +55 -0
- package/src/utils/git.ts +74 -0
- package/src/utils/validation.ts +184 -0
- package/templates/.claude/QUICK-START.md +230 -0
- package/templates/.claude/README.md +32 -0
- package/templates/.claude/settings.json +8 -0
- package/templates/BLANK-INSTANCE-SETUP.md +375 -0
- package/templates/DEVELOPMENT.md +160 -0
- package/templates/EXAMPLE-PROMPTS.md +643 -0
- package/templates/PROMPTS.md +410 -0
- package/templates/README.md +281 -0
- package/templates/agents.md +429 -0
- package/templates/app/[locale]/[...slug]/error.tsx +17 -0
- package/templates/app/[locale]/[...slug]/not-found.tsx +9 -0
- package/templates/app/[locale]/[...slug]/page.tsx +102 -0
- package/templates/app/[locale]/layout.tsx +22 -0
- package/templates/app/[locale]/page.tsx +12 -0
- package/templates/app/api/dynamic-redirect/route.ts +24 -0
- package/templates/app/api/preview/exit/route.ts +34 -0
- package/templates/app/api/preview/route.ts +63 -0
- package/templates/app/api/revalidate/route.ts +118 -0
- package/templates/components/agility-components/RichTextArea.tsx +66 -0
- package/templates/components/agility-components/index.ts +30 -0
- package/templates/components/agility-pages/MainTemplate.tsx +36 -0
- package/templates/components/agility-pages/index.ts +11 -0
- package/templates/docs/01-agility-cms-overview.md +139 -0
- package/templates/docs/02-page-routing.md +251 -0
- package/templates/docs/03-creating-components.md +462 -0
- package/templates/docs/04-data-fetching.md +484 -0
- package/templates/docs/05-containers-and-lists.md +596 -0
- package/templates/docs/06-localization.md +561 -0
- package/templates/docs/07-caching-strategies.md +410 -0
- package/templates/docs/08-common-components.md +756 -0
- package/templates/docs/09-whats-included.md +279 -0
- package/templates/docs/10-mcp-server-setup.md +153 -0
- package/templates/docs/11-linked-nested-content.md +611 -0
- package/templates/docs/README.md +164 -0
- package/templates/lib/cms/getAgilityContext.ts +28 -0
- package/templates/lib/cms/getAgilityPage.ts +51 -0
- package/templates/lib/cms/getAgilitySDK.ts +22 -0
- package/templates/lib/cms/getContentItem.ts +20 -0
- package/templates/lib/cms/getContentList.ts +19 -0
- package/templates/lib/cms/getRedirections.ts +85 -0
- package/templates/lib/cms/getSitemapFlat.ts +19 -0
- package/templates/lib/cms/getSitemapNested.ts +19 -0
- package/templates/lib/env.ts +99 -0
- package/templates/lib/i18n/config.ts +28 -0
- package/templates/proxy.ts +101 -0
- 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
|