@contentful/optimization-node 0.1.0-alpha8 → 1.0.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://www.contentful.com/developers/docs/personalization/">
3
- <img alt="Contentful Logo" title="Contentful" src="../../../contentful-icon.png" width="150">
3
+ <img alt="Contentful Logo" title="Contentful" src="https://raw.githubusercontent.com/contentful/optimization/optimization-node-v1.0.0/contentful-icon.png" width="150">
4
4
  </a>
5
5
  </p>
6
6
 
@@ -10,280 +10,311 @@
10
10
 
11
11
  <div align="center">
12
12
 
13
- [Readme](./README.md) · [Reference](https://contentful.github.io/optimization) ·
14
- [Contributing](/CONTRIBUTING.md)
13
+ [Guides](https://contentful.github.io/optimization/documents/Documentation.Guides.html) ·
14
+ [Reference](https://contentful.github.io/optimization) · [Contributing](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/CONTRIBUTING.md)
15
15
 
16
16
  </div>
17
17
 
18
18
  > [!WARNING]
19
19
  >
20
- > The Optimization SDK Suite is currently ALPHA! Breaking changes may be published at any time.
20
+ > The Optimization SDK Suite is pre-release (alpha). Breaking changes can be published at any time.
21
21
 
22
- The Optimization Node SDK implements functionality specific to Node environments, based on the
23
- [Optimization Core Library](/universal/core/README.md). This SDK is part of the
24
- [Contentful Optimization SDK Suite](/README.md).
22
+ The Optimization Node SDK implements stateless server-side optimization behavior on top of the
23
+ [Optimization Core SDK](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/packages/universal/core-sdk/README.md). Use it for server rendering, server
24
+ functions, and Node services that need request-scoped profile evaluation or event emission.
25
+
26
+ If you are integrating a Node application, start with [Getting Started](#getting-started), then use
27
+ [Integrating the Optimization Node SDK in a Node app](https://contentful.github.io/optimization/documents/Documentation.Guides.integrating-the-node-sdk-in-a-node-app.html)
28
+ for the step-by-step flow. This README keeps the package orientation and common setup options close
29
+ at hand; generated [reference documentation](https://contentful.github.io/optimization) remains the
30
+ source of truth for exported API signatures.
25
31
 
26
32
  <details>
27
33
  <summary>Table of Contents</summary>
28
34
  <!-- mtoc-start -->
29
35
 
30
- - [Getting Started](#getting-started)
31
- - [Reference Implementations](#reference-implementations)
32
- - [Configuration](#configuration)
33
- - [Top-level Configuration Options](#top-level-configuration-options)
34
- - [Analytics Options](#analytics-options)
35
- - [Event Builder Options](#event-builder-options)
36
- - [Fetch Options](#fetch-options)
37
- - [Personalization Options](#personalization-options)
38
- - [Optimization Methods](#optimization-methods)
39
- - [Personalization Data Resolution Methods](#personalization-data-resolution-methods)
40
- - [`getCustomFlag`](#getcustomflag)
41
- - [`personalizeEntry`](#personalizeentry)
42
- - [`getMergeTagValue`](#getmergetagvalue)
43
- - [Personalization and Analytics Event Methods](#personalization-and-analytics-event-methods)
44
- - [`identify`](#identify)
45
- - [`page`](#page)
46
- - [`track`](#track)
47
- - [`trackComponentView`](#trackcomponentview)
48
- - [`trackFlagView`](#trackflagview)
49
- - [Interceptors](#interceptors)
50
- - [Life-cycle Interceptors](#life-cycle-interceptors)
36
+ - [Getting started](#getting-started)
37
+ - [When to use this package](#when-to-use-this-package)
38
+ - [Common configuration](#common-configuration)
39
+ - [Core workflows](#core-workflows)
40
+ - [Request-scoped events](#request-scoped-events)
41
+ - [Content resolution](#content-resolution)
42
+ - [Caching guidance](#caching-guidance)
43
+ - [Development harness](#development-harness)
44
+ - [Related](#related)
51
45
 
52
46
  <!-- mtoc-end -->
53
47
  </details>
54
48
 
55
- ## Getting Started
49
+ ## Getting started
56
50
 
57
51
  Install using an NPM-compatible package manager, pnpm for example:
58
52
 
59
53
  ```sh
60
- pnpm install @contentful/optimization-node
54
+ pnpm add @contentful/optimization-node
61
55
  ```
62
56
 
57
+ Add `contentful` too when the SDK will use your app-owned `contentful.js` client for managed entry
58
+ fetching.
59
+
63
60
  Import the Optimization class; both CJS and ESM module systems are supported, ESM preferred:
64
61
 
65
62
  ```ts
66
- import Optimization from '@contentful/optimization-node'
63
+ import ContentfulOptimization from '@contentful/optimization-node'
67
64
  ```
68
65
 
69
- Configure and initialize the Optimization Node SDK:
66
+ Create the SDK once per module or process, then bind consent and request context in each route:
70
67
 
71
68
  ```ts
72
- const optimization = new Optimization({ clientId: 'abc123' })
73
- ```
74
-
75
- ## Reference Implementations
76
-
77
- Reference implementations illustrate how the SDK may be used under common scenarios, as well as
78
- select less-common scenarios, with the most basic example solution possible.
79
-
80
- - [Node SSR Only](/implementations/node-ssr-only/README.md): Example application that uses the Node
81
- SDK to render a personalized Web page
82
- - [Node SSR + Web Vanilla](/implementations/node-ssr-web-vanilla/README.md): Example application
83
- demonstrating simple profile synchronization between the Node and [Web](../web/README.md) SDKs via
84
- cookie
85
-
86
- ## Configuration
87
-
88
- ### Top-level Configuration Options
89
-
90
- | Option | Required? | Default | Description |
91
- | ----------------- | --------- | ----------------------------- | ------------------------------------------------------------------------------ |
92
- | `analytics` | No | See "Analytics Options" | Configuration specific to the Analytics/Insights API |
93
- | `app` | No | `undefined` | The application definition used to attribute events to a specific consumer app |
94
- | `clientId` | Yes | N/A | The Ninetailed API Key which can be found in the Ninetailed Admin app |
95
- | `environment` | No | `'main'` | The Ninetailed environment configured in the Ninetailed Admin app |
96
- | `eventBuilder` | No | See "Event Builder Options" | Event builder configuration (channel/library metadata, etc.) |
97
- | `fetchOptions` | No | See "Fetch Options" | Configuration for Fetch timeout and retry functionality |
98
- | `logLevel` | No | `'error'` | Minimum log level for the default console sin |
99
- | `personalization` | No | See "Personalization Options" | Configuration specific to the Personalization/Experience API |
100
-
101
- ### Analytics Options
102
-
103
- | Option | Required? | Default | Description |
104
- | --------- | --------- | ------------------------------------------ | ----------------------------- |
105
- | `baseUrl` | No | `'https://ingest.insights.ninetailed.co/'` | Base URL for the Insights API |
106
-
107
- ### Event Builder Options
108
-
109
- Event builder options should only be supplied when building an SDK on top of the Optimization Node
110
- SDK or any of its descendent SDKs.
111
-
112
- | Option | Required? | Default | Description |
113
- | --------- | --------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------- |
114
- | `channel` | No | `'server'` | The channel that identifies where events originate from (e.g. `'web'`, `'mobile'`) |
115
- | `library` | No | `{ name: 'Optimization Node API', version: '0.0.0' }` | The client library metadata that is attached to all events |
116
-
117
- ### Fetch Options
118
-
119
- Fetch options allow for configuration of a Fetch API-compatible fetch method and the retry/timeout
120
- logic integrated into the Optimization API Client. Specify the `fetchMethod` when the host
121
- application environment does not offer a `fetch` method that is compatible with the standard Fetch
122
- API in its global scope.
123
-
124
- | Option | Required? | Default | Description |
125
- | ------------------ | --------- | ----------- | --------------------------------------------------------------------- |
126
- | `fetchMethod` | No | `undefined` | Signature of a fetch method used by the API clients |
127
- | `intervalTimeout` | No | `0` | Delay (in milliseconds) between retry attempts |
128
- | `onFailedAttempt` | No | `undefined` | Callback invoked whenever a retry attempt fails |
129
- | `onRequestTimeout` | No | `undefined` | Callback invoked when a request exceeds the configured timeout |
130
- | `requestTimeout` | No | `3000` | Maximum time (in milliseconds) to wait for a response before aborting |
131
- | `retries` | No | `1` | Maximum number of retry attempts |
132
-
133
- Configuration method signatures:
134
-
135
- - `fetchMethod`: `(url: string | URL, init: RequestInit) => Promise<Response>`
136
- - `onFailedAttempt` and `onRequestTimeout`: `(options: FetchMethodCallbackOptions) => void`
137
-
138
- ### Personalization Options
139
-
140
- | Option | Required? | Default | Description |
141
- | ----------------- | --------- | ------------------------------------- | ------------------------------------------------------------------- |
142
- | `baseUrl` | No | `'https://experience.ninetailed.co/'` | Base URL for the Experience API |
143
- | `enabledFeatures` | No | `['ip-enrichment', 'location']` | Enabled features which the API may use for each request |
144
- | `ip` | No | `undefined` | IP address to override the API behavior for IP analysis |
145
- | `locale` | No | `'en-US'` (in API) | Locale used to translate `location.city` and `location.country` |
146
- | `plainText` | No | `false` | Sends performance-critical endpoints in plain text |
147
- | `preflight` | No | `false` | Instructs the API to aggregate a new profile state but not store it |
148
-
149
- ## Optimization Methods
150
-
151
- Arguments marked with an asterisk (\*) are always required.
152
-
153
- ### Personalization Data Resolution Methods
154
-
155
- #### `getCustomFlag`
156
-
157
- Get the specified Custom Flag's value from the provided changes array.
158
-
159
- Arguments:
160
-
161
- - `name`\*: The name/key of the Custom Flag
162
- - `changes`: Changes array
163
-
164
- Returns:
165
-
166
- - The resolved value for the specified Custom Flag, or `undefined` if it cannot be found.
167
-
168
- > [!NOTE]
169
- >
170
- > If the `changes` argument is omitted, the method will return `undefined`.
171
-
172
- #### `personalizeEntry`
173
-
174
- Resolve a baseline Contentful entry to a personalized variant using the provided selected
175
- personalizations.
176
-
177
- Type arguments:
178
-
179
- - `S`: Entry skeleton type
180
- - `M`: Chain modifiers
181
- - `L`: Locale code
182
-
183
- Arguments:
184
-
185
- - `entry`\*: The entry to personalize
186
- - `personalizations`: Selected personalizations
187
-
188
- Returns:
189
-
190
- - The resolved personalized entry variant, or the supplied baseline entry if baseline is the
191
- selected variant or a variant cannot be found.
192
-
193
- > [!NOTE]
194
- >
195
- > If the `personalizations` argument is omitted, the method will return the baseline entry.
196
-
197
- #### `getMergeTagValue`
198
-
199
- Resolve a "Merge Tag" to a value based on the provided profile. A "Merge Tag" is a special Rich Text
200
- fragment supported by Contentful that specifies a profile data member to be injected into the Rich
201
- Text when rendered.
202
-
203
- Arguments:
69
+ const optimization = new ContentfulOptimization({
70
+ clientId: 'your-client-id',
71
+ environment: 'main',
72
+ locale: 'en-US',
73
+ })
204
74
 
205
- - `embeddedNodeEntryTarget`\*: The merge tag entry node to resolve
206
- - `profile`: The user profile
75
+ function appPolicyAllowsOptimizationEvent(req: { cookies?: Record<string, string> }): boolean {
76
+ return req.cookies?.['app-personalization-consent'] === 'granted'
77
+ }
78
+
79
+ async function renderRequest(
80
+ req: { cookies?: Record<string, string>; headers: { 'accept-language'?: string } },
81
+ profileId: string,
82
+ ) {
83
+ const appLocale = getAppLocale(req)
84
+ const requestOptimization = optimization.forRequest({
85
+ consent: {
86
+ events: appPolicyAllowsOptimizationEvent(req),
87
+ persistence: appPolicyAllowsOptimizationEvent(req),
88
+ },
89
+ locale: appLocale,
90
+ eventContext: { locale: appLocale },
91
+ profile: { id: profileId },
92
+ })
93
+
94
+ const { accepted, data } = await requestOptimization.page()
95
+
96
+ return accepted ? data : undefined
97
+ }
98
+ ```
207
99
 
208
- > [!NOTE]
209
- >
210
- > If the `profile` argument is omitted, the method will return the merge tag's fallback value.
100
+ ## When to use this package
211
101
 
212
- ### Personalization and Analytics Event Methods
102
+ Use `@contentful/optimization-node` for server-side rendering, server functions, and Node services
103
+ that need stateless profile evaluation or event emission. Use the Web or React Web SDK alongside it
104
+ when browser-side consent, anonymous ID persistence, automatic interaction tracking, or live updates
105
+ are part of the same application.
213
106
 
214
- Each method except `trackFlagView` may return an `OptimizationData` object containing:
107
+ ## Common configuration
215
108
 
216
- - `changes`: Currently used for Custom Flags
217
- - `personalizations`: Selected personalizations for the profile
218
- - `profile`: Profile associated with the evaluated events
109
+ The Node SDK is stateless. It does not maintain consent, profile, or browser persistence state
110
+ between requests. For cross-SDK consent guidance, see
111
+ [Consent management in the Optimization SDK Suite](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/documentation/concepts/consent-management-in-the-optimization-sdk-suite.md).
219
112
 
220
- #### `identify`
113
+ | Option | Required? | Default | Description |
114
+ | ------------------- | --------- | ---------------------- | ----------------------------------------------------------- |
115
+ | `clientId` | Yes | N/A | Shared API key for Experience API and Insights API requests |
116
+ | `environment` | No | `'main'` | Contentful environment identifier |
117
+ | `api` | No | See API options below | Experience API and Insights API endpoint options |
118
+ | `app` | No | `undefined` | Application metadata attached to outgoing event context |
119
+ | `contentful` | No | `undefined` | App-owned `contentful.js` client, default query, and cache |
120
+ | `locale` | No | `undefined` | Default SDK Experience API and event locale |
121
+ | `fetchOptions` | No | SDK defaults | Fetch timeout and retry behavior |
122
+ | `allowedEventTypes` | No | `['identify', 'page']` | Event types allowed before request event consent is granted |
123
+ | `eventBuilder` | No | Node SDK defaults | Event metadata overrides for SDK-layer authors |
124
+ | `logLevel` | No | `'error'` | Minimum log level for the default console sink |
221
125
 
222
- Identify the current profile/visitor to associate traits with a profile.
126
+ Common `api` options:
223
127
 
224
- Arguments:
128
+ | Option | Required? | Default | Description |
129
+ | ------------------- | --------- | ------------------------------------------ | ------------------------------------------------ |
130
+ | `experienceBaseUrl` | No | `'https://experience.ninetailed.co/'` | Base URL for the Experience API |
131
+ | `insightsBaseUrl` | No | `'https://ingest.insights.ninetailed.co/'` | Base URL for the Insights API |
132
+ | `enabledFeatures` | No | `['ip-enrichment', 'location']` | Experience API features to apply to each request |
225
133
 
226
- - `payload`\*: Identify event builder arguments object, including an optional `profile` property
227
- with a `PartialProfile` value that requires only an `id`
134
+ Request-scoped Experience options belong in `experienceOptions` when creating the request-bound
135
+ client:
228
136
 
229
- #### `page`
137
+ | Option | Description |
138
+ | ----------- | ------------------------------------------------------------- |
139
+ | `ip` | IP address override used by the Experience API |
140
+ | `locale` | Locale query parameter for localized Experience API responses |
141
+ | `plainText` | Sends performance-critical Experience API endpoints as text |
142
+ | `preflight` | Aggregates a new profile state without storing it |
230
143
 
231
- Record a personalization page view.
144
+ Request-scoped Insights options belong in `insightsOptions`:
232
145
 
233
- Arguments:
146
+ | Option | Description |
147
+ | -------- | ----------------------------------------------------------------- |
148
+ | `beacon` | Last-chance sender for serialized Insights API batches if needed. |
234
149
 
235
- - `payload`\*: Page view event builder arguments object, including an optional `profile` property
236
- with a `PartialProfile` value that requires only an `id`
150
+ Use the request-scoped top-level `locale` on `forRequest()` as the promoted path for localized
151
+ Experience API responses and default event context. `experienceOptions.locale` remains available as
152
+ an advanced low-level pass-through when `locale` is not supplied. If both are provided, request
153
+ `locale` wins.
237
154
 
238
- #### `track`
155
+ Common `fetchOptions` are `fetchMethod`, `requestTimeout`, `retries`, `intervalTimeout`,
156
+ `onFailedAttempt`, and `onRequestTimeout`. Default retries intentionally apply only to HTTP `503`
157
+ responses.
239
158
 
240
- Record a personalization custom track event.
159
+ Choose the application Contentful locale in your router, i18n layer, or request policy. Pass that
160
+ value directly to Contentful CDA requests, and pass the same value to
161
+ `forRequest({ locale: appLocale })` when Experience API responses and event context should use the
162
+ same language. Merge tags that reference localized profile fields such as `location.city` and
163
+ `location.country` then resolve in a language consistent with the rendered content. See
164
+ [Locale handling in the Optimization SDK Suite](https://contentful.github.io/optimization/documents/Documentation.Concepts.Locale_handling_in_the_Optimization_SDK_Suite.html)
165
+ for the full locale model.
241
166
 
242
- Arguments:
167
+ For every option, callback payload, and exported type, use the generated
168
+ [Node SDK reference](https://contentful.github.io/optimization/modules/_contentful_optimization-node.html).
243
169
 
244
- - `payload`\*: Track event builder arguments object, including an optional `profile` property with a
245
- `PartialProfile` value that requires only an `id`
170
+ ## Core workflows
246
171
 
247
- #### `trackComponentView`
172
+ ### Request-scoped events
248
173
 
249
- Record an analytics component view event. When the payload marks the component as "sticky", an
250
- additional personalization component view is recorded. This method only returns `OptimizationData`
251
- when the component is marked as "sticky".
174
+ Build event context from the incoming request, bind application-owned consent state with
175
+ `forRequest()`, then call `page()`, `identify()`, `screen()`, `track()`, or sticky `trackView()` on
176
+ the returned request object:
252
177
 
253
- Arguments:
178
+ ```ts
179
+ import type { Request } from 'express'
180
+
181
+ app.get('/products/:slug', async (req, res) => {
182
+ const appLocale = getAppLocale(req)
183
+ const requestOptimization = optimization.forRequest({
184
+ consent: {
185
+ events: appPolicyAllowsOptimizationEvent(req),
186
+ persistence: appPolicyAllowsOptimizationEvent(req),
187
+ },
188
+ locale: appLocale,
189
+ eventContext: {
190
+ locale: appLocale,
191
+ },
192
+ profile: { id: req.cookies.profileId },
193
+ })
194
+ const { accepted, data: optimizationData } = await requestOptimization.page({
195
+ properties: { path: req.path },
196
+ })
197
+
198
+ if (accepted && requestOptimization.canPersistProfile && optimizationData?.profile.id) {
199
+ persistProfileId(res, optimizationData.profile.id)
200
+ }
201
+
202
+ res.render('product', { optimizationData })
203
+ })
204
+ ```
254
205
 
255
- - `payload`\*: Component view event builder arguments object, including an optional `profile`
256
- property with a `PartialProfile` value that requires only an `id`
206
+ For default-on application policies that do not render an end-user consent UI, replace the
207
+ request-specific consent lookup with accepted consent:
257
208
 
258
- #### `trackFlagView`
209
+ ```ts
210
+ const requestOptimization = optimization.forRequest({
211
+ consent: { events: true, persistence: true },
212
+ locale: appLocale,
213
+ eventContext: { locale: appLocale },
214
+ profile,
215
+ })
216
+ ```
259
217
 
260
- Track a feature flag view via analytics. This is functionally the same as a non-sticky component
261
- view event.
218
+ Node SDK event calls fail closed except for the default pre-consent allowlist, `identify` and
219
+ `page`. Those allowlisted events are sent with `context.gdpr.isConsentGiven: false` when request
220
+ event consent is not granted. Pass `allowedEventTypes: []` to require strict opt-in for all
221
+ stateless event methods.
262
222
 
263
- Arguments:
223
+ In stateless runtimes, Insights-backed methods require a request-bound profile for delivery.
224
+ Non-sticky `trackView`, `trackClick`, `trackHover`, and `trackFlagView` require a profile ID passed
225
+ to `forRequest()`.
264
226
 
265
- - `payload`\*: Component view event builder arguments object, including an optional `profile`
266
- property with a `PartialProfile` value that requires only an `id`
227
+ ### Content resolution
267
228
 
268
- ## Interceptors
229
+ When a `contentful.js` client is available, prefer SDK-managed fetching by entry ID. Configure the
230
+ client once, then call `fetchOptimizedEntry()` on a request-bound client after an accepted
231
+ Experience API call. `forRequest()` clients use the latest selected optimizations when omitted;
232
+ singleton calls require explicit `selectedOptimizations`.
269
233
 
270
- Interceptors may be used to read and/or modify data flowing through the Core SDK.
234
+ ```ts
235
+ const optimization = new ContentfulOptimization({
236
+ clientId: 'client-id',
237
+ contentful: { client: contentfulClient },
238
+ environment: 'main',
239
+ locale: appLocale,
240
+ })
271
241
 
272
- ### Life-cycle Interceptors
242
+ const requestOptimization = optimization.forRequest({ consent: true, profile })
243
+ await requestOptimization.page()
244
+ const { baselineEntry, entry } = await requestOptimization.fetchOptimizedEntry('hero-entry')
245
+ ```
273
246
 
274
- - `event`: Intercepts an event's data _before_ it is queued and/or emitted
275
- - `state`: Intercepts state data retrieved from an Experience API call _before_ updating the SDK's
276
- internal state
247
+ Use `fetchContentfulEntries()` or `prefetchManagedEntries()` when a route knows several managed
248
+ entry IDs. Entries with the same normalized query share one `getEntries()` call; same-tick
249
+ single-entry calls can join that batch. Large `getEntries()` fetches are split into 100-ID chunks.
277
250
 
278
- Example interceptor usage:
251
+ If your application already fetched the baseline entry, keep using the manual resolver:
279
252
 
280
253
  ```ts
281
- optimization.interceptors.event((event) => {
282
- event.properties.timestamp = new Date().toISOString()
283
- })
254
+ const pageResult = await requestOptimization.page()
255
+ const resolvedEntry = optimization.resolveOptimizedEntry(
256
+ baselineEntry,
257
+ pageResult.accepted ? pageResult.data.selectedOptimizations : undefined,
258
+ )
284
259
  ```
285
260
 
286
- > [!WARNING]
287
- >
288
- > Interceptors are intended to enable low-level interoperability; to simply read and react to
289
- > Optimization SDK events, use the `states` observables.
261
+ Use one CDA locale in either path. For localized apps, configure your application locale. A
262
+ request-bound client uses `forRequest({ locale: appLocale })` as the managed Contentful query locale
263
+ when neither `contentful.defaultQuery` nor the per-call query sets `locale`. Pass the same value to
264
+ app-owned Contentful CDA requests and to `forRequest({ locale: appLocale })` when MergeTags that
265
+ read localized profile fields match the rendered entry language. Do not pass all-locale CDA
266
+ responses from `withAllLocales` or `locale=*`; the resolver expects direct single-locale field
267
+ values. See
268
+ [Entry personalization and variant resolution](https://contentful.github.io/optimization/documents/Documentation.Concepts.Entry_personalization_and_variant_resolution.html#single-locale-cda-entry-contract)
269
+ for the entry contract and
270
+ [Locale handling in the Optimization SDK Suite](https://contentful.github.io/optimization/documents/Documentation.Concepts.Locale_handling_in_the_Optimization_SDK_Suite.html)
271
+ for request locale behavior.
272
+
273
+ Use `getMergeTagValue()` for Contentful Rich Text merge tags and `getFlag()` for Custom Flags. If a
274
+ merge tag references localized profile fields such as `location.city` or `location.country`, its
275
+ resolved value follows the localized profile data returned by the Experience API. The Node SDK is
276
+ stateless, so `getFlag()` does not automatically emit flag-view tracking.
277
+
278
+ ### Caching guidance
279
+
280
+ Cache raw Contentful delivery payloads in your application layer or with the SDK-managed entry
281
+ cache, not profile-evaluated SDK event results. Use `clearContentfulEntryCache()` when an SDK
282
+ instance must drop cached CDA entries.
283
+
284
+ | Data or call | Cache across requests? | Reason |
285
+ | --------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------------------- |
286
+ | Raw Contentful entries fetched from CDA or the SDK-managed entry cache | Yes | They are content snapshots and can be resolved per request |
287
+ | `resolveOptimizedEntry()` and `getMergeTagValue()` results | Request-local only | Results depend on the current profile and optimization data |
288
+ | `page()`, `identify()`, `screen()`, `track()`, and sticky `trackView()` | No | These calls perform side effects and return request-specific profile |
289
+ | Non-sticky `trackView()`, `trackClick()`, `trackHover()`, `trackFlagView()` | No | These calls emit Insights API events |
290
+
291
+ The
292
+ [Node SDK integration guide](https://contentful.github.io/optimization/documents/Documentation.Guides.integrating-the-node-sdk-in-a-node-app.html)
293
+ covers request context, profile persistence, Contentful entry resolution, and hybrid Node + browser
294
+ setups in detail.
295
+
296
+ ## Development harness
297
+
298
+ The package-local dev harness runs from `packages/node/node-sdk/dev/` and reads `.env` from this
299
+ package directory.
300
+
301
+ 1. Start from `.env.example` and create or update `packages/node/node-sdk/.env`.
302
+ 2. Prefer the repo-standard `PUBLIC_...` variable names shown in `.env.example`.
303
+ 3. Start the harness from the repo root:
304
+
305
+ ```sh
306
+ pnpm --filter @contentful/optimization-node dev
307
+ ```
308
+
309
+ ## Related
310
+
311
+ - [Integrating the Optimization Node SDK in a Node app](https://contentful.github.io/optimization/documents/Documentation.Guides.integrating-the-node-sdk-in-a-node-app.html) -
312
+ step-by-step server-side integration guide
313
+ - [Node SDK generated reference](https://contentful.github.io/optimization/modules/_contentful_optimization-node.html) -
314
+ exported API reference
315
+ - [Node reference implementation](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/implementations/node-sdk/README.md) - server-rendered
316
+ implementation using request-scoped SDK calls
317
+ - [Node + Web reference implementation](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/implementations/node-sdk+web-sdk/README.md) -
318
+ hybrid SSR and browser implementation
319
+ - [Optimization Web SDK](https://github.com/contentful/optimization/blob/optimization-node-v1.0.0/packages/web/web-sdk/README.md) - browser SDK used when the same application
320
+ also needs client-side consent, persistence, tracking, or live updates