@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,410 @@
|
|
|
1
|
+
# Caching Strategies
|
|
2
|
+
|
|
3
|
+
This document explains caching strategies for Agility CMS content in Next.js.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The project uses Next.js Data Cache with Agility CMS for optimal performance:
|
|
8
|
+
- **Time-based Revalidation**: Content refreshes after a set time
|
|
9
|
+
- **Tag-based Revalidation**: Specific content updates via webhooks
|
|
10
|
+
- **Preview Mode**: Bypass cache for draft content
|
|
11
|
+
|
|
12
|
+
## Cache Configuration
|
|
13
|
+
|
|
14
|
+
### Time-based Revalidation
|
|
15
|
+
|
|
16
|
+
Set in page route:
|
|
17
|
+
```typescript
|
|
18
|
+
// app/[locale]/[...slug]/page.tsx
|
|
19
|
+
export const revalidate = 60 // Revalidate every 60 seconds
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Set via environment variable:
|
|
23
|
+
```env
|
|
24
|
+
AGILITY_PATH_REVALIDATE_DURATION=60
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### How It Works
|
|
28
|
+
|
|
29
|
+
1. **First Request**: Page generated statically at build time
|
|
30
|
+
2. **Subsequent Requests**: Cached version served (fast)
|
|
31
|
+
3. **After 60 seconds**: Next request triggers background regeneration
|
|
32
|
+
4. **Updated Content**: New version cached for next 60 seconds
|
|
33
|
+
|
|
34
|
+
## Cache Tags
|
|
35
|
+
|
|
36
|
+
Every piece of content from Agility CMS has a cache tag:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
// lib/cms/getContentItem.ts
|
|
40
|
+
agilityClient.config.fetchConfig = {
|
|
41
|
+
next: {
|
|
42
|
+
tags: [`agility-content-${contentID}-${locale}`],
|
|
43
|
+
revalidate: 60,
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Tag Format
|
|
49
|
+
|
|
50
|
+
- **Content Item**: `agility-content-{contentID}-{locale}`
|
|
51
|
+
- **Content List**: `agility-content-{referenceName}-{locale}`
|
|
52
|
+
- **Sitemap**: `agility-sitemap-flat-{locale}`
|
|
53
|
+
- **Page**: `agility-page-{pageID}-{locale}`
|
|
54
|
+
|
|
55
|
+
### Why Cache Tags?
|
|
56
|
+
|
|
57
|
+
Cache tags allow targeted revalidation:
|
|
58
|
+
```typescript
|
|
59
|
+
// Revalidate only blog post 123 in English
|
|
60
|
+
revalidateTag("agility-content-123-en-us");
|
|
61
|
+
|
|
62
|
+
// Revalidate all posts list
|
|
63
|
+
revalidateTag("agility-content-posts-en-us");
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## On-Demand Revalidation
|
|
67
|
+
|
|
68
|
+
### Webhook Setup
|
|
69
|
+
|
|
70
|
+
1. Create API route for webhooks:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
// app/api/revalidate/route.ts
|
|
74
|
+
|
|
75
|
+
import { revalidateTag } from "next/cache";
|
|
76
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
77
|
+
|
|
78
|
+
export async function POST(request: NextRequest) {
|
|
79
|
+
const body = await request.json();
|
|
80
|
+
const securityKey = request.headers.get("x-agility-security-key");
|
|
81
|
+
|
|
82
|
+
// Verify security key
|
|
83
|
+
if (securityKey !== process.env.AGILITY_SECURITY_KEY) {
|
|
84
|
+
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const { tag, contentID, locale } = body;
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
if (tag) {
|
|
91
|
+
// Revalidate specific tag
|
|
92
|
+
revalidateTag(tag);
|
|
93
|
+
} else if (contentID && locale) {
|
|
94
|
+
// Revalidate content by ID
|
|
95
|
+
revalidateTag(`agility-content-${contentID}-${locale}`);
|
|
96
|
+
} else {
|
|
97
|
+
return NextResponse.json({ error: "Missing parameters" }, { status: 400 });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return NextResponse.json({ revalidated: true, now: Date.now() });
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return NextResponse.json({ error: "Error revalidating" }, { status: 500 });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
2. Configure webhook in Agility CMS:
|
|
108
|
+
- Go to **Settings > Webhooks**
|
|
109
|
+
- Add webhook URL: `https://yourdomain.com/api/revalidate`
|
|
110
|
+
- Add security header: `x-agility-security-key: your-security-key`
|
|
111
|
+
- Select events: Content Published, Content Unpublished
|
|
112
|
+
|
|
113
|
+
### Manual Revalidation
|
|
114
|
+
|
|
115
|
+
You can also revalidate programmatically:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { revalidateTag } from "next/cache";
|
|
119
|
+
|
|
120
|
+
// Revalidate a specific content item
|
|
121
|
+
revalidateTag("agility-content-123-en-us");
|
|
122
|
+
|
|
123
|
+
// Revalidate a content list
|
|
124
|
+
revalidateTag("agility-content-posts-en-us");
|
|
125
|
+
|
|
126
|
+
// Revalidate all pages
|
|
127
|
+
revalidateTag("agility-sitemap-flat-en-us");
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Preview Mode
|
|
131
|
+
|
|
132
|
+
Preview mode bypasses caching to show draft content.
|
|
133
|
+
|
|
134
|
+
### Enable Preview
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// proxy.ts
|
|
138
|
+
if (request.nextUrl.searchParams.has("agilitypreviewkey")) {
|
|
139
|
+
const agilityPreviewKey = request.nextUrl.searchParams.get("agilitypreviewkey");
|
|
140
|
+
const locale = request.nextUrl.searchParams.get("lang");
|
|
141
|
+
const slug = request.nextUrl.pathname;
|
|
142
|
+
|
|
143
|
+
return NextResponse.redirect(
|
|
144
|
+
`/api/preview?locale=${locale}&slug=${slug}&agilitypreviewkey=${agilityPreviewKey}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Preview API Route
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// app/api/preview/route.ts
|
|
153
|
+
|
|
154
|
+
import { cookies } from "next/headers";
|
|
155
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
156
|
+
|
|
157
|
+
export async function GET(request: NextRequest) {
|
|
158
|
+
const searchParams = request.nextUrl.searchParams;
|
|
159
|
+
const agilityPreviewKey = searchParams.get("agilitypreviewkey");
|
|
160
|
+
const locale = searchParams.get("locale");
|
|
161
|
+
const slug = searchParams.get("slug");
|
|
162
|
+
|
|
163
|
+
// Verify preview key
|
|
164
|
+
if (agilityPreviewKey !== process.env.AGILITY_API_PREVIEW_KEY) {
|
|
165
|
+
return NextResponse.json({ error: "Invalid preview key" }, { status: 401 });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Set preview cookie
|
|
169
|
+
(await cookies()).set("agilitypreview", "true", {
|
|
170
|
+
httpOnly: true,
|
|
171
|
+
secure: process.env.NODE_ENV === "production",
|
|
172
|
+
sameSite: "strict",
|
|
173
|
+
maxAge: 60 * 60 * 24, // 24 hours
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// Redirect to the page
|
|
177
|
+
const redirectUrl = locale === "en-us" ? slug : `/${locale}${slug}`;
|
|
178
|
+
return NextResponse.redirect(new URL(redirectUrl, request.url));
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Exit Preview
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
// app/api/preview/exit/route.ts
|
|
186
|
+
|
|
187
|
+
import { cookies } from "next/headers";
|
|
188
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
189
|
+
|
|
190
|
+
export async function GET(request: NextRequest) {
|
|
191
|
+
const searchParams = request.nextUrl.searchParams;
|
|
192
|
+
const locale = searchParams.get("locale");
|
|
193
|
+
const slug = searchParams.get("slug");
|
|
194
|
+
|
|
195
|
+
// Delete preview cookie
|
|
196
|
+
(await cookies()).delete("agilitypreview");
|
|
197
|
+
|
|
198
|
+
// Redirect to the page
|
|
199
|
+
const redirectUrl = locale === "en-us" ? slug : `/${locale}${slug}`;
|
|
200
|
+
return NextResponse.redirect(new URL(redirectUrl, request.url));
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Using Preview in SDK
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// lib/cms/getAgilitySDK.ts
|
|
208
|
+
|
|
209
|
+
import { cookies } from "next/headers";
|
|
210
|
+
import agilitySDK from "@agility/content-fetch";
|
|
211
|
+
|
|
212
|
+
export async function getAgilitySDK({ locale }: { locale: string }) {
|
|
213
|
+
const isPreview = (await cookies()).get("agilitypreview")?.value === "true";
|
|
214
|
+
const apiKey = isPreview
|
|
215
|
+
? process.env.AGILITY_API_PREVIEW_KEY
|
|
216
|
+
: process.env.AGILITY_API_FETCH_KEY;
|
|
217
|
+
|
|
218
|
+
return agilitySDK.getApi({
|
|
219
|
+
guid: process.env.AGILITY_GUID,
|
|
220
|
+
apiKey,
|
|
221
|
+
isPreview,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Cache Strategies
|
|
227
|
+
|
|
228
|
+
### Strategy 1: High-Frequency Updates
|
|
229
|
+
|
|
230
|
+
For content that changes frequently (e.g., live scores, stock prices):
|
|
231
|
+
|
|
232
|
+
```typescript
|
|
233
|
+
export const revalidate = 10 // 10 seconds
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### Strategy 2: Moderate Updates
|
|
237
|
+
|
|
238
|
+
For typical websites (e.g., blogs, marketing sites):
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
export const revalidate = 60 // 1 minute
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Strategy 3: Infrequent Updates
|
|
245
|
+
|
|
246
|
+
For static content (e.g., documentation, about pages):
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
export const revalidate = 3600 // 1 hour
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Strategy 4: On-Demand Only
|
|
253
|
+
|
|
254
|
+
For content that should only update via webhook:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
export const revalidate = false // Only revalidate on-demand
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
## Cache Debugging
|
|
261
|
+
|
|
262
|
+
### Check Cache Status
|
|
263
|
+
|
|
264
|
+
Add headers to see cache status:
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// proxy.ts
|
|
268
|
+
import { cookies } from "next/headers";
|
|
269
|
+
|
|
270
|
+
export async function proxy(request: NextRequest) {
|
|
271
|
+
const response = NextResponse.next();
|
|
272
|
+
|
|
273
|
+
// Add cache debugging headers
|
|
274
|
+
if (process.env.NODE_ENV === "development") {
|
|
275
|
+
response.headers.set("x-agility-preview", (await cookies()).get("agilitypreview")?.value || "false");
|
|
276
|
+
response.headers.set("x-cache-status", "HIT"); // or MISS, STALE, etc.
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return response;
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Log Cache Keys
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// lib/cms/getContentItem.ts
|
|
287
|
+
const cacheTag = `agility-content-${contentID}-${locale}`;
|
|
288
|
+
console.log("[Cache] Fetching with tag:", cacheTag);
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Cache Warming
|
|
292
|
+
|
|
293
|
+
Pre-generate pages at build time:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
// app/[locale]/[...slug]/page.tsx
|
|
297
|
+
|
|
298
|
+
export async function generateStaticParams() {
|
|
299
|
+
const allPaths = [];
|
|
300
|
+
|
|
301
|
+
for (const locale of locales) {
|
|
302
|
+
const sitemap = await agilityClient.getSitemapFlat({
|
|
303
|
+
channelName: "website",
|
|
304
|
+
languageCode: locale,
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
const localePaths = Object.values(sitemap)
|
|
308
|
+
.filter((node: any) => !node.redirect && !node.isFolder)
|
|
309
|
+
.map((node: any) => ({
|
|
310
|
+
locale,
|
|
311
|
+
slug: node.path.split("/").slice(1),
|
|
312
|
+
}));
|
|
313
|
+
|
|
314
|
+
allPaths.push(...localePaths);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return allPaths;
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Best Practices
|
|
322
|
+
|
|
323
|
+
1. **Use Appropriate Revalidation**: Don't over-cache or under-cache
|
|
324
|
+
2. **Tag Everything**: Always add cache tags to CMS fetches
|
|
325
|
+
3. **Webhook Integration**: Set up webhooks for instant updates
|
|
326
|
+
4. **Monitor Performance**: Track cache hit rates
|
|
327
|
+
5. **Preview Mode**: Use for content editors to see drafts
|
|
328
|
+
6. **Incremental Updates**: Only revalidate what changed
|
|
329
|
+
7. **Build-Time Generation**: Pre-render critical pages
|
|
330
|
+
|
|
331
|
+
## Common Caching Patterns
|
|
332
|
+
|
|
333
|
+
### Pattern 1: Page-Level Caching
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
// app/[locale]/[...slug]/page.tsx
|
|
337
|
+
export const revalidate = 60
|
|
338
|
+
|
|
339
|
+
export default async function Page({ params }: PageProps) {
|
|
340
|
+
const agilityData = await getAgilityPage({ params });
|
|
341
|
+
return <div>{/* ... */}</div>;
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### Pattern 2: Component-Level Caching
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// components/Header.tsx
|
|
349
|
+
import { unstable_cache } from "next/cache";
|
|
350
|
+
import { getContentItem } from "@/lib/cms/getContentItem";
|
|
351
|
+
|
|
352
|
+
async function getHeaderData(locale: string) {
|
|
353
|
+
return unstable_cache(
|
|
354
|
+
async () => {
|
|
355
|
+
return await getContentItem({
|
|
356
|
+
referenceName: "headerSettings",
|
|
357
|
+
locale,
|
|
358
|
+
});
|
|
359
|
+
},
|
|
360
|
+
["header-data", locale],
|
|
361
|
+
{ revalidate: 300, tags: [`agility-header-${locale}`] }
|
|
362
|
+
)();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export default async function Header({ locale }: { locale: string }) {
|
|
366
|
+
const headerData = await getHeaderData(locale);
|
|
367
|
+
return <header>{/* ... */}</header>;
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Pattern 3: API Route Caching
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// app/api/posts/route.ts
|
|
375
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
376
|
+
import { getContentList } from "@/lib/cms/getContentList";
|
|
377
|
+
|
|
378
|
+
export async function GET(request: NextRequest) {
|
|
379
|
+
const locale = request.nextUrl.searchParams.get("locale") || "en-us";
|
|
380
|
+
|
|
381
|
+
const posts = await getContentList({
|
|
382
|
+
referenceName: "posts",
|
|
383
|
+
locale,
|
|
384
|
+
take: 10,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
return NextResponse.json(posts, {
|
|
388
|
+
headers: {
|
|
389
|
+
"Cache-Control": "s-maxage=60, stale-while-revalidate",
|
|
390
|
+
},
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
## Environment-Specific Caching
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
// lib/cms/getAgilitySDK.ts
|
|
399
|
+
|
|
400
|
+
const revalidate =
|
|
401
|
+
process.env.NODE_ENV === "production"
|
|
402
|
+
? parseInt(process.env.AGILITY_FETCH_CACHE_DURATION || "60")
|
|
403
|
+
: 0; // No cache in development
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Next Steps
|
|
407
|
+
|
|
408
|
+
- Read [04-data-fetching.md](./04-data-fetching.md) for data fetching patterns
|
|
409
|
+
- Read [10-deployment.md](./10-deployment.md) for production caching setup
|
|
410
|
+
- Read [11-troubleshooting.md](./11-troubleshooting.md) for cache debugging
|