@contentful/optimization-node 0.1.0-alpha13 → 0.1.0-alpha15

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="https://raw.githubusercontent.com/contentful/optimization/v0.1.0-alpha13/contentful-icon.png" width="150">
3
+ <img alt="Contentful Logo" title="Contentful" src="https://raw.githubusercontent.com/contentful/optimization/v0.1.0-alpha15/contentful-icon.png" width="150">
4
4
  </a>
5
5
  </p>
6
6
 
@@ -11,50 +11,42 @@
11
11
  <div align="center">
12
12
 
13
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/v0.1.0-alpha13/CONTRIBUTING.md)
14
+ [Reference](https://contentful.github.io/optimization) · [Contributing](https://github.com/contentful/optimization/blob/v0.1.0-alpha15/CONTRIBUTING.md)
15
15
 
16
16
  </div>
17
17
 
18
18
  > [!WARNING]
19
19
  >
20
- > The Optimization SDK Suite is pre-release (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](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/packages/universal/core-sdk/README.md). This SDK is part of the
24
- [Contentful Optimization SDK Suite](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/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/v0.1.0-alpha15/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
- - [API Options](#api-options)
35
- - [Event Builder Options](#event-builder-options)
36
- - [Fetch Options](#fetch-options)
37
- - [Optimization Methods](#optimization-methods)
38
- - [Optimization Data Resolution Methods](#optimization-data-resolution-methods)
39
- - [`getFlag`](#getflag)
40
- - [`resolveOptimizedEntry`](#resolveoptimizedentry)
41
- - [`getMergeTagValue`](#getmergetagvalue)
42
- - [Experience API and Insights API Event Methods](#experience-api-and-insights-api-event-methods)
43
- - [`identify`](#identify)
44
- - [`page`](#page)
45
- - [`screen`](#screen)
46
- - [`track`](#track)
47
- - [`trackView`](#trackview)
48
- - [`trackClick`](#trackclick)
49
- - [`trackHover`](#trackhover)
50
- - [`trackFlagView`](#trackflagview)
51
- - [Interceptors](#interceptors)
52
- - [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)
53
45
 
54
46
  <!-- mtoc-end -->
55
47
  </details>
56
48
 
57
- ## Getting Started
49
+ ## Getting started
58
50
 
59
51
  Install using an NPM-compatible package manager, pnpm for example:
60
52
 
@@ -68,343 +60,233 @@ Import the Optimization class; both CJS and ESM module systems are supported, ES
68
60
  import ContentfulOptimization from '@contentful/optimization-node'
69
61
  ```
70
62
 
71
- Configure and initialize the Optimization Node SDK:
63
+ Create the SDK once per module or process, then bind consent and request context in each route:
72
64
 
73
65
  ```ts
74
- const optimization = new ContentfulOptimization({ clientId: 'abc123' })
75
- const requestOptions = { locale: 'en-US' }
76
-
77
- await optimization.page({}, requestOptions)
78
- ```
79
-
80
- Create `optimization` once per module or process, then pass request-scoped Experience options as the
81
- final argument to stateless event methods inside each incoming request.
82
-
83
- ## Caching Guidance
84
-
85
- The Node SDK is stateless, but that does not mean all request work is cacheable.
86
-
87
- - Cache raw Contentful delivery payloads in your application layer.
88
- - Treat cached Contentful entries as immutable.
89
- - Use `resolveOptimizedEntry()` and `getMergeTagValue()` as request-local helpers when rendering.
90
- - Do not memoize `page()`, `identify()`, `screen()`, `track()`, `trackView()`, `trackClick()`,
91
- `trackHover()`, or `trackFlagView()`. Those methods emit outbound events and/or return profile
92
- state for the current request.
93
-
94
- For a step-by-step Express-style walkthrough that covers request context, profile persistence,
95
- Contentful entry resolution, and hybrid Node + browser setups, see
96
- [Integrating the Optimization Node SDK in a Node App](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/documentation/guides/integrating-the-node-sdk-in-a-node-app.md).
97
-
98
- ## Development Harness
99
-
100
- The package-local dev harness runs from `packages/node/node-sdk/dev/` and reads `.env` from this
101
- package directory.
102
-
103
- 1. Start from `.env.example` and create or update `packages/node/node-sdk/.env`.
104
- 2. Prefer the repo-standard `PUBLIC_...` variable names shown in `.env.example`.
105
- 3. Start the harness from the repo root:
106
-
107
- ```sh
108
- pnpm --filter @contentful/optimization-node dev
109
- ```
110
-
111
- ## Reference Implementations
112
-
113
- Reference implementations illustrate how the SDK may be used under common scenarios, as well as
114
- select less-common scenarios, with the most basic example solution possible.
115
-
116
- - [Node SSR Only](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/implementations/node-sdk/README.md): Example application that uses the
117
- Node SDK to render an optimized Web page
118
- - [Node SSR + Web Vanilla](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/implementations/node-sdk+web-sdk/README.md): Example application
119
- demonstrating simple profile synchronization between the Node and
120
- [Web](https://github.com/contentful/optimization/blob/v0.1.0-alpha13/packages/web/web-sdk/README.md) SDKs via cookie
121
-
122
- ## Configuration
123
-
124
- ### Top-level Configuration Options
125
-
126
- | Option | Required? | Default | Description |
127
- | -------------- | --------- | --------------------------- | ------------------------------------------------------------------------------ |
128
- | `api` | No | See "API Options" | Unified configuration for the Experience API and Insights API endpoints |
129
- | `app` | No | `undefined` | The application definition used to attribute events to a specific consumer app |
130
- | `clientId` | Yes | N/A | Shared API key for Experience API and Insights API requests |
131
- | `environment` | No | `'main'` | The environment identifier |
132
- | `eventBuilder` | No | See "Event Builder Options" | Event builder configuration (channel/library metadata, etc.) |
133
- | `fetchOptions` | No | See "Fetch Options" | Configuration for Fetch timeout and retry functionality |
134
- | `logLevel` | No | `'error'` | Minimum log level for the default console sink |
135
-
136
- ### API Options
137
-
138
- | Option | Required? | Default | Description |
139
- | ------------------- | --------- | ------------------------------------------ | ------------------------------------------------------------ |
140
- | `experienceBaseUrl` | No | `'https://experience.ninetailed.co/'` | Base URL for the Experience API |
141
- | `insightsBaseUrl` | No | `'https://ingest.insights.ninetailed.co/'` | Base URL for the Insights API |
142
- | `enabledFeatures` | No | `['ip-enrichment', 'location']` | Enabled features the Experience API may use for each request |
143
-
144
- Request-scoped Experience API options are passed to stateless event methods as their final argument
145
- instead of the SDK constructor:
146
-
147
- | Option | Required? | Default | Description |
148
- | ----------- | --------- | ----------- | ------------------------------------------------------------------------------ |
149
- | `ip` | No | `undefined` | IP address override used by the Experience API for location analysis |
150
- | `locale` | No | `undefined` | Locale used to translate `location.city` and `location.country` |
151
- | `plainText` | No | `undefined` | Sends performance-critical Experience API endpoints in plain text |
152
- | `preflight` | No | `undefined` | Instructs the Experience API to aggregate a new profile state but not store it |
153
-
154
- ### Event Builder Options
155
-
156
- Event builder options should only be supplied when building an SDK on top of the Optimization Node
157
- SDK or any of its descendent SDKs.
158
-
159
- | Option | Required? | Default | Description |
160
- | --------- | --------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
161
- | `channel` | No | `'server'` | The channel that identifies where events originate from (e.g. `'web'`, `'mobile'`) |
162
- | `library` | No | `{ name: '@contentful/optimization-node', version: '0.0.0' }` | The client library metadata that is attached to all events |
163
-
164
- ### Fetch Options
165
-
166
- Fetch options allow for configuration of a Fetch API-compatible fetch method and the retry/timeout
167
- logic integrated into the SDK's bundled API clients. Specify the `fetchMethod` when the host
168
- application environment does not offer a `fetch` method that is compatible with the standard Fetch
169
- API in its global scope.
170
-
171
- | Option | Required? | Default | Description |
172
- | ------------------ | --------- | ----------- | --------------------------------------------------------------------- |
173
- | `fetchMethod` | No | `undefined` | Signature of a fetch method used by the API clients |
174
- | `intervalTimeout` | No | `0` | Delay (in milliseconds) between retry attempts |
175
- | `onFailedAttempt` | No | `undefined` | Callback invoked whenever a retry attempt fails |
176
- | `onRequestTimeout` | No | `undefined` | Callback invoked when a request exceeds the configured timeout |
177
- | `requestTimeout` | No | `3000` | Maximum time (in milliseconds) to wait for a response before aborting |
178
- | `retries` | No | `1` | Maximum number of retry attempts |
179
-
180
- Configuration method signatures:
181
-
182
- - `fetchMethod`: `(url: string | URL, init: RequestInit) => Promise<Response>`
183
- - `onFailedAttempt` and `onRequestTimeout`: `(options: FetchMethodCallbackOptions) => void`
184
-
185
- ## Optimization Methods
186
-
187
- Arguments marked with an asterisk (\*) are always required.
188
-
189
- ### Optimization Data Resolution Methods
190
-
191
- #### `getFlag`
192
-
193
- Get the specified Custom Flag's value from the provided changes array.
194
-
195
- Arguments:
196
-
197
- - `name`\*: The name/key of the Custom Flag
198
- - `changes`: Changes array
199
-
200
- Returns:
201
-
202
- - The resolved value for the specified Custom Flag, or `undefined` if it cannot be found.
203
-
204
- Behavior notes:
205
-
206
- - Node SDK is stateless; `getFlag(...)` does not auto-emit `trackFlagView`.
207
- - If full map resolution is needed for advanced use cases, use
208
- `optimization.flagsResolver.resolve(changes)`.
209
-
210
- > [!NOTE]
211
- >
212
- > If the `changes` argument is omitted, the method will return `undefined`.
213
-
214
- #### `resolveOptimizedEntry`
215
-
216
- Resolve a baseline Contentful entry to an optimized variant using the provided selected
217
- optimizations.
218
-
219
- Type arguments:
220
-
221
- - `S`: Entry skeleton type
222
- - `M`: Chain modifiers
223
- - `L`: Locale code
224
-
225
- Arguments:
226
-
227
- - `entry`\*: The baseline entry to resolve
228
- - `selectedOptimizations`: Selected optimizations
229
-
230
- Returns:
231
-
232
- - The resolved optimized entry variant, or the supplied baseline entry if baseline is the selected
233
- variant or a variant cannot be found.
234
-
235
- > [!NOTE]
236
- >
237
- > If the `selectedOptimizations` argument is omitted, the method will return the baseline entry.
238
-
239
- #### `getMergeTagValue`
240
-
241
- Resolve a "Merge Tag" to a value based on the provided profile. A "Merge Tag" is a special Rich Text
242
- fragment supported by Contentful that specifies a profile data member to be injected into the Rich
243
- Text when rendered.
244
-
245
- Arguments:
246
-
247
- - `embeddedNodeEntryTarget`\*: The merge tag entry node to resolve
248
- - `profile`: The user profile
249
-
250
- > [!NOTE]
251
- >
252
- > If the `profile` argument is omitted, the method will return the merge tag's fallback value.
253
-
254
- ### Experience API and Insights API Event Methods
255
-
256
- Pass request-scoped Experience options as the final argument to stateless event methods:
66
+ const optimization = new ContentfulOptimization({
67
+ clientId: 'your-client-id',
68
+ environment: 'main',
69
+ locale: 'en-US',
70
+ })
257
71
 
258
- ```ts
259
- const requestOptions = {
260
- locale: req.acceptsLanguages()[0] ?? 'en-US',
72
+ function appPolicyAllowsOptimizationEvent(req: { cookies?: Record<string, string> }): boolean {
73
+ return req.cookies?.['app-personalization-consent'] === 'granted'
261
74
  }
262
75
 
263
- await optimization.page({ ...requestContext, profile }, requestOptions)
76
+ async function renderRequest(
77
+ req: { cookies?: Record<string, string>; headers: { 'accept-language'?: string } },
78
+ profileId: string,
79
+ ) {
80
+ const appLocale = getAppLocale(req)
81
+ const requestOptimization = optimization.forRequest({
82
+ consent: {
83
+ events: appPolicyAllowsOptimizationEvent(req),
84
+ persistence: appPolicyAllowsOptimizationEvent(req),
85
+ },
86
+ locale: appLocale,
87
+ eventContext: { locale: appLocale },
88
+ profile: { id: profileId },
89
+ })
90
+
91
+ const { accepted, data } = await requestOptimization.page()
92
+
93
+ return accepted ? data : undefined
94
+ }
264
95
  ```
265
96
 
266
- Request-scoped Experience options stay separate from the event payload. Event context such as page
267
- data, locale metadata on the event, and user agent belong in `payload`, while `ip`, `locale`,
268
- `plainText`, and `preflight` belong in `requestOptions`.
269
-
270
- Only the following methods may return an `OptimizationData` object:
271
-
272
- - `identify`
273
- - `page`
274
- - `screen`
275
- - `track`
276
- - `trackView` (when `payload.sticky` is `true`)
277
-
278
- `trackClick`, `trackHover`, and `trackFlagView` return no data. When returned, `OptimizationData`
279
- contains:
280
-
281
- - `changes`: Currently used for Custom Flags
282
- - `selectedOptimizations`: Selected optimizations for the profile
283
- - `profile`: Profile associated with the evaluated events
284
-
285
- In stateless runtimes, Insights-backed methods require a profile for delivery. Non-sticky
286
- `trackView`, `trackClick`, `trackHover`, and `trackFlagView` require `payload.profile.id`. Sticky
287
- `trackView` may omit `profile`, because the returned Experience profile is reused for the paired
288
- Insights event.
289
-
290
- #### `identify`
291
-
292
- Identify the current profile/visitor to associate traits with a profile.
293
-
294
- Arguments:
295
-
296
- - `payload`\*: Identify event builder arguments object, including an optional `profile` property
297
- with a `PartialProfile` value that requires only an `id`
298
- - `requestOptions`: Optional request-scoped Experience API options passed as the final argument
299
-
300
- #### `page`
301
-
302
- Record an Experience API page view.
303
-
304
- Arguments:
305
-
306
- - `payload`\*: Page view event builder arguments object, including an optional `profile` property
307
- with a `PartialProfile` value that requires only an `id`
308
- - `requestOptions`: Optional request-scoped Experience API options passed as the final argument
97
+ ## When to use this package
309
98
 
310
- #### `screen`
99
+ Use `@contentful/optimization-node` for server-side rendering, server functions, and Node services
100
+ that need stateless profile evaluation or event emission. Use the Web or React Web SDK alongside it
101
+ when browser-side consent, anonymous ID persistence, automatic interaction tracking, or live updates
102
+ are part of the same application.
311
103
 
312
- Record an Experience API screen view.
104
+ ## Common configuration
313
105
 
314
- Arguments:
106
+ The Node SDK is stateless. It does not maintain consent, profile, or browser persistence state
107
+ between requests. For cross-SDK consent guidance, see
108
+ [Consent management in the Optimization SDK Suite](https://github.com/contentful/optimization/blob/v0.1.0-alpha15/documentation/concepts/consent-management-in-the-optimization-sdk-suite.md).
315
109
 
316
- - `payload`\*: Screen view event builder arguments object, including an optional `profile` property
317
- with a `PartialProfile` value that requires only an `id`
318
- - `requestOptions`: Optional request-scoped Experience API options passed as the final argument
110
+ | Option | Required? | Default | Description |
111
+ | ------------------- | --------- | ---------------------- | ----------------------------------------------------------- |
112
+ | `clientId` | Yes | N/A | Shared API key for Experience API and Insights API requests |
113
+ | `environment` | No | `'main'` | Contentful environment identifier |
114
+ | `api` | No | See API options below | Experience API and Insights API endpoint options |
115
+ | `app` | No | `undefined` | Application metadata attached to outgoing event context |
116
+ | `locale` | No | `undefined` | Default SDK Experience API and event locale |
117
+ | `fetchOptions` | No | SDK defaults | Fetch timeout and retry behavior |
118
+ | `allowedEventTypes` | No | `['identify', 'page']` | Event types allowed before request event consent is granted |
119
+ | `eventBuilder` | No | Node SDK defaults | Event metadata overrides for SDK-layer authors |
120
+ | `logLevel` | No | `'error'` | Minimum log level for the default console sink |
319
121
 
320
- #### `track`
122
+ Common `api` options:
321
123
 
322
- Record an Experience API custom track event.
124
+ | Option | Required? | Default | Description |
125
+ | ------------------- | --------- | ------------------------------------------ | ------------------------------------------------ |
126
+ | `experienceBaseUrl` | No | `'https://experience.ninetailed.co/'` | Base URL for the Experience API |
127
+ | `insightsBaseUrl` | No | `'https://ingest.insights.ninetailed.co/'` | Base URL for the Insights API |
128
+ | `enabledFeatures` | No | `['ip-enrichment', 'location']` | Experience API features to apply to each request |
323
129
 
324
- Arguments:
130
+ Request-scoped Experience options belong in `experienceOptions` when creating the request-bound
131
+ client:
325
132
 
326
- - `payload`\*: Track event builder arguments object, including an optional `profile` property with a
327
- `PartialProfile` value that requires only an `id`
328
- - `requestOptions`: Optional request-scoped Experience API options passed as the final argument
133
+ | Option | Description |
134
+ | ----------- | ------------------------------------------------------------- |
135
+ | `ip` | IP address override used by the Experience API |
136
+ | `locale` | Locale query parameter for localized Experience API responses |
137
+ | `plainText` | Sends performance-critical Experience API endpoints as text |
138
+ | `preflight` | Aggregates a new profile state without storing it |
329
139
 
330
- #### `trackView`
140
+ Request-scoped Insights options belong in `insightsOptions`:
331
141
 
332
- Record an Insights API entry view event. When the payload marks the entry as "sticky", an additional
333
- Experience API entry view is recorded. This method only returns `OptimizationData` when the entry is
334
- marked as "sticky".
142
+ | Option | Description |
143
+ | --------------- | --------------------------------------------------- |
144
+ | `beaconHandler` | Custom handler for enqueueing Insights API batches. |
335
145
 
336
- Arguments:
146
+ Use the request-scoped top-level `locale` on `forRequest()` as the promoted path for localized
147
+ Experience API responses and default event context. `experienceOptions.locale` remains available as
148
+ an advanced low-level pass-through when `locale` is not supplied. If both are provided, request
149
+ `locale` wins.
337
150
 
338
- - `payload`\*: Entry view event builder arguments object. When `payload.sticky` is `true`, `profile`
339
- is optional and the returned Experience profile is reused for Insights delivery. Otherwise,
340
- `profile` is required and must contain at least an `id`
341
- - `requestOptions`: Optional request-scoped Experience API options passed as the final argument.
342
- Only used when `payload.sticky` is `true`
151
+ Common `fetchOptions` are `fetchMethod`, `requestTimeout`, `retries`, `intervalTimeout`,
152
+ `onFailedAttempt`, and `onRequestTimeout`. Default retries intentionally apply only to HTTP `503`
153
+ responses.
343
154
 
344
- #### `trackClick`
155
+ Choose the application Contentful locale in your router, i18n layer, or request policy. Pass that
156
+ value directly to Contentful CDA requests, and pass the same value to
157
+ `forRequest({ locale: appLocale })` when Experience API responses and event context should use the
158
+ same language. Merge tags that reference localized profile fields such as `location.city` and
159
+ `location.country` then resolve in a language consistent with the rendered content. See
160
+ [Locale handling in the Optimization SDK Suite](https://contentful.github.io/optimization/documents/Documentation.Concepts.Locale_handling_in_the_Optimization_SDK_Suite.html)
161
+ for the full locale model.
345
162
 
346
- Record an Insights API entry click event.
163
+ For every option, callback payload, and exported type, use the generated
164
+ [Node SDK reference](https://contentful.github.io/optimization/modules/_contentful_optimization-node.html).
347
165
 
348
- Returns:
166
+ ## Core workflows
349
167
 
350
- - `void`
168
+ ### Request-scoped events
351
169
 
352
- Arguments:
170
+ Build event context from the incoming request, bind application-owned consent state with
171
+ `forRequest()`, then call `page()`, `identify()`, `screen()`, `track()`, or sticky `trackView()` on
172
+ the returned request object:
353
173
 
354
- - `payload`\*: Entry click event builder arguments object, including a required `profile` property
355
- with a `PartialProfile` value that requires only an `id`
356
- - `requestOptions`: Optional request-scoped Experience API options accepted for signature
357
- consistency; currently unused
358
-
359
- #### `trackHover`
360
-
361
- Record an Insights API entry hover event.
362
-
363
- Returns:
364
-
365
- - `void`
366
-
367
- Arguments:
368
-
369
- - `payload`\*: Entry hover event builder arguments object, including a required `profile` property
370
- with a `PartialProfile` value that requires only an `id`
371
- - `requestOptions`: Optional request-scoped Experience API options accepted for signature
372
- consistency; currently unused
373
-
374
- #### `trackFlagView`
174
+ ```ts
175
+ import type { Request } from 'express'
176
+
177
+ app.get('/products/:slug', async (req, res) => {
178
+ const appLocale = getAppLocale(req)
179
+ const requestOptimization = optimization.forRequest({
180
+ consent: {
181
+ events: appPolicyAllowsOptimizationEvent(req),
182
+ persistence: appPolicyAllowsOptimizationEvent(req),
183
+ },
184
+ locale: appLocale,
185
+ eventContext: {
186
+ locale: appLocale,
187
+ },
188
+ profile: { id: req.cookies.profileId },
189
+ })
190
+ const { accepted, data: optimizationData } = await requestOptimization.page({
191
+ properties: { path: req.path },
192
+ })
193
+
194
+ if (accepted && requestOptimization.canPersistProfile && optimizationData?.profile.id) {
195
+ persistProfileId(res, optimizationData.profile.id)
196
+ }
197
+
198
+ res.render('product', { optimizationData })
199
+ })
200
+ ```
375
201
 
376
- Track a feature flag view via the Insights API. This is functionally the same as a non-sticky flag
377
- view event.
202
+ For default-on application policies that do not render an end-user consent UI, replace the
203
+ request-specific consent lookup with accepted consent:
378
204
 
379
- Returns:
205
+ ```ts
206
+ const requestOptimization = optimization.forRequest({
207
+ consent: { events: true, persistence: true },
208
+ locale: appLocale,
209
+ eventContext: { locale: appLocale },
210
+ profile,
211
+ })
212
+ ```
380
213
 
381
- - `void`
214
+ Node SDK event calls fail closed except for the default pre-consent allowlist, `identify` and
215
+ `page`. Those allowlisted events are sent with `context.gdpr.isConsentGiven: false` when request
216
+ event consent is not granted. Pass `allowedEventTypes: []` to require strict opt-in for all
217
+ stateless event methods.
382
218
 
383
- Arguments:
219
+ In stateless runtimes, Insights-backed methods require a request-bound profile for delivery.
220
+ Non-sticky `trackView`, `trackClick`, `trackHover`, and `trackFlagView` require a profile ID passed
221
+ to `forRequest()`.
384
222
 
385
- - `payload`\*: Flag view event builder arguments object, including a required `profile` property
386
- with a `PartialProfile` value that requires only an `id`
387
- - `requestOptions`: Optional request-scoped Experience API options accepted for signature
388
- consistency; currently unused
223
+ ### Content resolution
389
224
 
390
- ## Interceptors
225
+ Fetch Contentful entries in your application layer, then resolve variants with returned optimization
226
+ data:
391
227
 
392
- Interceptors may be used to read and/or modify data flowing through the Core SDK.
228
+ ```ts
229
+ const resolvedEntry = optimization.resolveOptimizedEntry(
230
+ baselineEntry,
231
+ optimizationData?.selectedOptimizations,
232
+ )
233
+ ```
393
234
 
394
- ### Life-cycle Interceptors
235
+ Fetch entries with one CDA locale in the app layer. For localized apps, configure your application
236
+ locale and pass it directly to Contentful CDA requests. Pass the same value to
237
+ `forRequest({ locale: appLocale })` when MergeTags that read localized profile fields should match
238
+ the rendered entry language. Do not pass all-locale CDA responses from `withAllLocales` or
239
+ `locale=*` into `resolveOptimizedEntry()`; the resolver expects direct single-locale field values.
240
+ See
241
+ [Entry personalization and variant resolution](https://contentful.github.io/optimization/documents/Documentation.Concepts.Entry_personalization_and_variant_resolution.html#single-locale-cda-entry-contract)
242
+ for the entry contract and
243
+ [Locale handling in the Optimization SDK Suite](https://contentful.github.io/optimization/documents/Documentation.Concepts.Locale_handling_in_the_Optimization_SDK_Suite.html)
244
+ for request locale behavior.
245
+
246
+ Use `getMergeTagValue()` for Contentful Rich Text merge tags and `getFlag()` for Custom Flags. If a
247
+ merge tag references localized profile fields such as `location.city` or `location.country`, its
248
+ resolved value follows the localized profile data returned by the Experience API. The Node SDK is
249
+ stateless, so `getFlag()` does not automatically emit flag-view tracking.
250
+
251
+ ### Caching guidance
252
+
253
+ Cache raw Contentful delivery payloads in your application layer, not profile-evaluated SDK event
254
+ results.
255
+
256
+ | Data or call | Cache across requests? | Reason |
257
+ | --------------------------------------------------------------------------- | ---------------------- | -------------------------------------------------------------------- |
258
+ | Raw Contentful entries fetched from CDA | Yes | They are content snapshots and can be resolved per request |
259
+ | `resolveOptimizedEntry()` and `getMergeTagValue()` results | Request-local only | Results depend on the current profile and optimization data |
260
+ | `page()`, `identify()`, `screen()`, `track()`, and sticky `trackView()` | No | These calls perform side effects and return request-specific profile |
261
+ | Non-sticky `trackView()`, `trackClick()`, `trackHover()`, `trackFlagView()` | No | These calls emit Insights API events |
262
+
263
+ The
264
+ [Node SDK integration guide](https://contentful.github.io/optimization/documents/Documentation.Guides.integrating-the-node-sdk-in-a-node-app.html)
265
+ covers request context, profile persistence, Contentful entry resolution, and hybrid Node + browser
266
+ setups in detail.
267
+
268
+ ## Development harness
395
269
 
396
- - `event`: Intercepts an event's data _before_ it is queued and/or emitted
397
- - `state`: Intercepts state data retrieved from an Experience API call _before_ updating the SDK's
398
- internal state
270
+ The package-local dev harness runs from `packages/node/node-sdk/dev/` and reads `.env` from this
271
+ package directory.
399
272
 
400
- Example interceptor usage:
273
+ 1. Start from `.env.example` and create or update `packages/node/node-sdk/.env`.
274
+ 2. Prefer the repo-standard `PUBLIC_...` variable names shown in `.env.example`.
275
+ 3. Start the harness from the repo root:
401
276
 
402
- ```ts
403
- optimization.interceptors.event((event) => {
404
- event.properties.timestamp = new Date().toISOString()
405
- })
406
- ```
277
+ ```sh
278
+ pnpm --filter @contentful/optimization-node dev
279
+ ```
407
280
 
408
- > [!WARNING]
409
- >
410
- > Interceptors are intended to enable low-level interoperability and should be used with care.
281
+ ## Related
282
+
283
+ - [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) -
284
+ step-by-step server-side integration guide
285
+ - [Node SDK generated reference](https://contentful.github.io/optimization/modules/_contentful_optimization-node.html) -
286
+ exported API reference
287
+ - [Node reference implementation](https://github.com/contentful/optimization/blob/v0.1.0-alpha15/implementations/node-sdk/README.md) - server-rendered
288
+ implementation using request-scoped SDK calls
289
+ - [Node + Web reference implementation](https://github.com/contentful/optimization/blob/v0.1.0-alpha15/implementations/node-sdk+web-sdk/README.md) -
290
+ hybrid SSR and browser implementation
291
+ - [Optimization Web SDK](https://github.com/contentful/optimization/blob/v0.1.0-alpha15/packages/web/web-sdk/README.md) - browser SDK used when the same application
292
+ also needs client-side consent, persistence, tracking, or live updates