@contentful/optimization-node

中文文档
2.2.1发布于 2026/11/06MITTypeScriptESM + CJS近 30 天下载 3691

Node.js SDK for Contentful Optimization

原始使用文档
文档目录10 节
  1. Optimization Node SDK
  2. Getting started
  3. When to use this package
  4. Common configuration
  5. Core workflows
  6. Request-scoped events
  7. Content resolution
  8. Caching guidance
  9. Development harness
  10. Related

Contentful Logo

Contentful Personalization & Analytics

Optimization Node SDK

The Optimization Node SDK implements stateless server-side optimization behavior on top of the Optimization Core SDK. Use it for server rendering, server functions, and Node services that need request-scoped profile evaluation or event emission.

If you are integrating a Node application, start with Getting Started, then use Integrating the Optimization Node SDK in a Node app for the step-by-step flow. This README keeps the package orientation and common setup options close at hand; generated reference documentation remains the source of truth for exported API signatures.

[!IMPORTANT]

Upgrading from v1? Follow the v1-to-v2 migration guide before changing package versions.

Table of Contents

Getting started

Install using an NPM-compatible package manager, pnpm for example:

pnpm add @contentful/optimization-node

Add contentful too when the SDK will use your app-owned contentful.js client for managed entry fetching.

Import the Optimization class; both CJS and ESM module systems are supported, ESM preferred:

import ContentfulOptimization from '@contentful/optimization-node'

Create the SDK once per module or process, then bind consent and request context in each route:

const optimization = new ContentfulOptimization({
  spaceId: 'your-space-id',
  environment: 'master',
  locale: 'en-US',
})

function appPolicyAllowsOptimizationEvent(req: { cookies?: Record<string, string> }): boolean {
  return req.cookies?.['app-personalization-consent'] === 'granted'
}

async function renderRequest(
  req: { cookies?: Record<string, string>; headers: { 'accept-language'?: string } },
  profileId: string,
) {
  const appLocale = getAppLocale(req)
  const requestOptimization = optimization.forRequest({
    consent: {
      events: appPolicyAllowsOptimizationEvent(req),
      persistence: appPolicyAllowsOptimizationEvent(req),
    },
    locale: appLocale,
    eventContext: { locale: appLocale },
    profileId,
  })

  const { accepted, data } = await requestOptimization.page()

  return accepted ? data : undefined
}

When to use this package

Use @contentful/optimization-node for server-side rendering, server functions, and Node services that need stateless profile evaluation or event emission. Use the Web or React Web SDK alongside it when browser-side consent, anonymous ID persistence, automatic interaction tracking, or live updates are part of the same application.

Common configuration

The Node SDK is stateless. It does not maintain consent, profile, or browser persistence state between requests. For cross-SDK consent guidance, see Consent management in the Optimization SDK Suite.

OptionRequired?DefaultDescription
spaceIdYesN/AContentful Space identifier used for Experience and Insights API requests
environmentNo'master'Contentful environment identifier used for Experience and Insights API requests
apiNoSee API options belowExperience API and Insights API endpoint options
appNoundefinedApplication metadata attached to outgoing event context
contentfulNoundefinedApp-owned contentful.js client, default query, and cache
localeNoundefinedDefault SDK Experience API and event locale
fetchOptionsNoSDK defaultsFetch timeout and retry behavior
allowedEventTypesNo['identify', 'page']Event types allowed before request event consent is granted
eventBuilderNoNode SDK defaultsEvent metadata overrides for SDK-layer authors
logLevelNo'error'Minimum log level for the default console sink

Common api options:

OptionRequired?DefaultDescription
experienceBaseUrlNo'https://experience.ninetailed.co/'Base URL for the Experience API
insightsBaseUrlNo'https://ingest.insights.ninetailed.co/'Base URL for the Insights API
enabledFeaturesNo['ip-enrichment', 'location']Experience API features to apply to each request
locationNoAPI fallbackDefault location for single-profile Experience API mutations

Request-scoped Experience options belong in experienceOptions when creating the request-bound client:

OptionDescription
ipIP address override used by the Experience API
locationRequest location replacing the complete api.location default
localeLocale query parameter for localized Experience API responses
plainTextSends performance-critical Experience API endpoints as text
preflightAggregates a new profile state without storing it

Configure a shared default with api.location, then override it per incoming request with experienceOptions.location:

const optimization = new ContentfulOptimization({
  spaceId: 'your-space-id',
  api: { location: { countryCode: 'DE', city: 'Berlin' } },
})
const requestOptimization = optimization.forRequest({
  consent: true,
  experienceOptions: { location: { countryCode: 'FR', city: 'Paris' } },
})
await requestOptimization.page()

The request value replaces the whole default object; it does not merge fields. Pass experienceOptions: { location: {} } to replace the configured default with an explicit empty location instead of API geolocation. Event context.location takes precedence over matching request fields. An empty location does not clear an existing profile location, and request location does not lock location across later requests. See Location inputs and profile updates.

Request-scoped Insights options belong in insightsOptions:

OptionDescription
beaconLast-chance sender for serialized Insights API batches if needed.

Use the request-scoped top-level locale on forRequest() as the promoted path for localized Experience API responses and default event context. experienceOptions.locale remains available as an advanced low-level pass-through when locale is not supplied. If both are provided, request locale wins.

Common fetchOptions are fetchMethod, requestTimeout, retries, intervalTimeout, onFailedAttempt, and onRequestTimeout. Default retries intentionally apply only to HTTP 503 responses.

Choose the application Contentful locale in your router, i18n layer, or request policy. Pass that value directly to Contentful CDA requests, and pass the same value to forRequest({ locale: appLocale }) when Experience API responses and event context should use the same language. Merge tags that reference localized profile fields such as location.city and location.country then resolve in a language consistent with the rendered content. See Locale handling in the Optimization SDK Suite for the full locale model.

For every option, callback payload, and exported type, use the generated Node SDK reference.

Core workflows

Request-scoped events

Build event context from the incoming request, bind application-owned consent state with forRequest(), then call page(), identify(), screen(), track(), or sticky trackView() on the returned request object:

import type { Request } from 'express'

app.get('/products/:slug', async (req, res) => {
  const appLocale = getAppLocale(req)
  const requestOptimization = optimization.forRequest({
    consent: {
      events: appPolicyAllowsOptimizationEvent(req),
      persistence: appPolicyAllowsOptimizationEvent(req),
    },
    locale: appLocale,
    eventContext: {
      locale: appLocale,
    },
    profileId: req.cookies.profileId,
  })
  const { accepted, data: optimizationData } = await requestOptimization.page({
    properties: { path: req.path },
  })

  if (accepted && requestOptimization.canPersistProfile && optimizationData?.profile.id) {
    persistProfileId(res, optimizationData.profile.id)
  }

  res.render('product', { optimizationData })
})

For default-on application policies that do not render an end-user consent UI, replace the request-specific consent lookup with accepted consent:

const requestOptimization = optimization.forRequest({
  consent: { events: true, persistence: true },
  locale: appLocale,
  eventContext: { locale: appLocale },
  profile,
})

Node SDK event calls fail closed except for the default pre-consent allowlist, identify and page. Those allowlisted events are sent with context.gdpr.isConsentGiven: false when request event consent is not granted. Pass allowedEventTypes: [] to require strict opt-in for all stateless event methods.

In stateless runtimes, non-sticky trackView, trackClick, trackHover, and trackFlagView require a full Profile supplied through forRequest({ profile }) or returned by an Experience call on the same request client. Use profileId for Experience API continuity.

For custom integrations that need an event payload before submission, use the request client's eventBuilder. It exposes buildIdentify, buildPageView, buildScreenView, and buildTrack with request event context and locale defaults. In this example, appConsent, appLocale, appUserAgent, and appUserId come from your application's request and identity handling:

const requestOptimization = optimization.forRequest({
  consent: appConsent,
  locale: appLocale,
  eventContext: { userAgent: appUserAgent },
})
const identifyEvent = requestOptimization.eventBuilder.buildIdentify({ userId: appUserId })

Construction does not send the event, check request consent, invoke interceptors, or update the request profile or selections. Built events use the shared builder's context.gdpr.isConsentGiven, which is false in the Node SDK, even when request consent is granted. Sending identifyEvent through a low-level API does not automatically apply request consent or SDK submission processing. For normal submission, use request-bound identify(), page(), screen(), or track(). The singleton's optimization.eventBuilder remains unbound to any request.

Content resolution

When a contentful.js client is available, prefer SDK-managed fetching by entry ID or content type and slug. Configure the client once, then call fetchOptimizedEntry() on a request-bound client after an accepted Experience API call. forRequest() clients use the latest selected optimizations when omitted; singleton calls require explicit selectedOptimizations.

const optimization = new ContentfulOptimization({
  spaceId: 'space-id',
  contentful: { client: contentfulClient },
  environment: 'master',
  locale: appLocale,
})

const requestOptimization = optimization.forRequest({ consent: true, profile })
await requestOptimization.page()
const { baselineEntry, entry } = await requestOptimization.fetchOptimizedEntry({
  contentType: 'productPage',
  slug: req.params.slug,
  entryQuery: { locale: appLocale },
})

Use fetchContentfulEntries() or prefetchManagedEntries() when a route knows several managed entry sources. A slug source accepts slugField when the content model does not use the default slug field. Slug lookup enforces its content-type, field selector, and two-result uniqueness limit after merging entryQuery; successful resolution uses the fetched entry's sys.id. Slug handoffs nest the normalized descriptor under managedEntry and retain that ID in entryId. Equivalent ID sources retain same-query batching and 100-ID chunking.

If your application already fetched the baseline entry, keep using the manual resolver:

const pageResult = await requestOptimization.page()
const resolvedEntry = optimization.resolveOptimizedEntry(
  baselineEntry,
  pageResult.accepted ? pageResult.data.selectedOptimizations : undefined,
)

Use one CDA locale in either path. For localized apps, configure your application locale. A request-bound client uses forRequest({ locale: appLocale }) as the managed Contentful query locale when neither contentful.defaultQuery nor the per-call query sets locale. Pass the same value to app-owned Contentful CDA requests and to forRequest({ locale: appLocale }) when MergeTags that read localized profile fields match the rendered entry language. Do not pass all-locale CDA responses from withAllLocales or locale=*; the resolver expects direct single-locale field values. See Entry personalization and variant resolution for the entry contract and Locale handling in the Optimization SDK Suite for request locale behavior.

Use getMergeTagValue() for Contentful Rich Text merge tags and getFlag() for Custom Flags. If a merge tag references localized profile fields such as location.city or location.country, its resolved value follows the localized profile data returned by the Experience API. The Node SDK is stateless, so getFlag() does not automatically emit flag-view tracking.

Caching guidance

Cache raw Contentful delivery payloads in your application layer or with the SDK-managed entry cache, not profile-evaluated SDK event results. Use clearContentfulEntryCache() when an SDK instance must drop cached CDA entries.

Data or callCache across requests?Reason
Raw Contentful entries fetched from CDA or the SDK-managed entry cacheYesThey are content snapshots and can be resolved per request
resolveOptimizedEntry() and getMergeTagValue() resultsRequest-local onlyResults depend on the current profile and optimization data
page(), identify(), screen(), track(), and sticky trackView()NoThese calls perform side effects and return request-specific profile
Non-sticky trackView(), trackClick(), trackHover(), trackFlagView()NoThese calls emit Insights API events

The Node SDK integration guide covers request context, profile persistence, Contentful entry resolution, and hybrid Node + browser setups in detail.

Development harness

The package-local dev harness runs from packages/node/node-sdk/dev/ and reads .env from this package directory.

  1. Start from .env.example and create or update packages/node/node-sdk/.env.

  2. Prefer the repo-standard PUBLIC_... variable names shown in .env.example.

  3. Start the harness from the repo root:

    pnpm --filter @contentful/optimization-node dev
    

版本与发布记录

@contentful/optimization-node 共发布 28 个版本,首次发布于 2026/02/03;过去 12 个月发布了 28 个版本。

版本发布日期距今
2.2.1latest2026/11/064 天前
2.2.02026/11/046 天前
2.1.12026/10/2220 天前
2.1.02026/10/1922 天前
2.0.22026/10/1625 天前
2.0.12026/09/231 个月前

其他发布通道:alpha → 0.1.0-alpha。

关于 @contentful/optimization-node 的常见问题

@contentful/optimization-node 是什么?

Node.js SDK for Contentful Optimization

如何安装 @contentful/optimization-node?

在项目目录运行 npm install @contentful/optimization-node,或使用 pnpm add / yarn add / bun add。

@contentful/optimization-node 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@contentful/optimization-node 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@contentful/optimization-node 可以商用吗?

@contentful/optimization-node 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@contentful/optimization-node 的下载量有多少?

根据 npm 官方下载统计,@contentful/optimization-node 在 2026/10/11 – 2026/11/09 这 30 天内被下载 3,691 次,日均约 123 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@contentful/optimization-node 还在维护吗?

最新版本 2.2.1 发布于 2026/11/06(4 天前);至今共发布 28 个版本,其中过去 12 个月发布了 28 个;首次发布于 2026/02/03。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。