Skip to content
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
### Version: 5.5.0
#### Date: Jul-27-2026
Enhancement: Entry variants support an optional branch name as the second argument to `variants()` on `Entry` and `Entries`. When provided, the branch is sent as the `branch` request header together with `x-cs-variant-uid`. Existing `variants(uid)` and `variants(uids)` calls remain backward compatible. Added unit and API tests for variant + branch requests.

### Version: 5.4.0
#### Date: Jul-16-2026
Enhancement: Removed `locale?` parameter from `Taxonomy.fetch()` and `Term.fetch()` — locale is now set via the chainable `.param('locale', value)` API, consistent with other query modifiers.
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@contentstack/delivery-sdk",
"version": "5.4.0",
"version": "5.5.0",
"type": "module",
"license": "MIT",
"engines": {
Expand Down
21 changes: 21 additions & 0 deletions src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,24 @@ export function encodeQueryParams(params: params): params {

return encodedParams;
}

/**
* Builds request headers for entry variant requests.
* @param variants - Comma-separated variant UID(s)
* @param branch - Optional branch name to scope the variant request
*/
export function buildVariantRequestHeaders(
variants: string,
branch?: string
): Record<string, string> | undefined {
const headers: Record<string, string> = {};

if (variants) {
headers['x-cs-variant-uid'] = variants;
}
if (branch) {
headers.branch = branch;
}

return Object.keys(headers).length > 0 ? headers : undefined;
}
24 changes: 17 additions & 7 deletions src/entries/entries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { AxiosInstance, getData } from '@contentstack/core';
import { Query } from '../query';
import { BaseQuery } from '../query';
import { FindResponse } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import { ErrorMessages } from '../common/error-messages';

export class Entries extends BaseQuery {
Expand All @@ -14,6 +14,7 @@ export class Entries extends BaseQuery {
this._contentTypeUid = contentTypeUid;
this._urlPath = `/content_types/${this._contentTypeUid}/entries`;
this._variants = '';
this._variantsBranch = '';
}

/**
Expand Down Expand Up @@ -252,28 +253,36 @@ export class Entries extends BaseQuery {
* const query = stack.contentType("contentTypeUid").entry().query();
*/
query(queryObj?: { [key: string]: any }) {
if (queryObj) return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, queryObj);
if (queryObj) {
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch, queryObj);
}

return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid);
return new Query(this._client, this._parameters, this._queryParams, this._variants, this._contentTypeUid, this._variantsBranch);
}

/**
* @method variants
* @memberof Entries
* @description The variant header will be added to axios client
* @description Stores the variant UID(s) and optional branch name, which are sent as the `x-cs-variant-uid` and `branch` headers on the request when find() is called.
* @param {string | string[]} variants - Variant UID or UIDs
* @param {string} [branchName] - Optional branch name sent as the `branch` header
* @returns {Entries}
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType('abc').entry().variants('xyz').find();
* const resultWithBranch = await stack.contentType('abc').entry().variants('xyz', 'branch_name').find();
*/
variants(variants: string | string[]): Entries {
variants(variants: string | string[], branchName?: string): Entries {
if (Array.isArray(variants) && variants.length > 0) {
this._variants = variants.join(',');
} else if (typeof variants == 'string' && variants.length > 0) {
this._variants = variants;
}
if (typeof branchName === 'string' && branchName.length > 0) {
this._variantsBranch = branchName;
}
return this;
}

Expand Down Expand Up @@ -320,10 +329,11 @@ export class Entries extends BaseQuery {
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
18 changes: 14 additions & 4 deletions src/entries/entry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { ErrorMessages } from '../common/error-messages';
import { buildVariantRequestHeaders } from '../common/utils';

interface EntryResponse<T> {
entry: T;
Expand All @@ -10,13 +11,15 @@ export class Entry {
private _entryUid: string;
private _urlPath: string;
protected _variants: string;
protected _variantsBranch: string;
_queryParams: { [key: string]: string | number | string[] } = {};
constructor(client: AxiosInstance, contentTypeUid: string, entryUid: string) {
this._client = client;
this._contentTypeUid = contentTypeUid;
this._entryUid = entryUid;
this._urlPath = `/content_types/${this._contentTypeUid}/entries/${this._entryUid}`;
this._variants = '';
this._variantsBranch = '';
}

/**
Expand All @@ -39,20 +42,26 @@ export class Entry {
/**
* @method variants
* @memberof Entry
* @description The variant header will be added to axios client
* @description Stores the variant UID(s) and optional branch name, which are sent as the `x-cs-variant-uid` and `branch` headers on the request when fetch() is called.
* @param {string | string[]} variants - Variant UID or UIDs
* @param {string} [branchName] - Optional branch name sent as the `branch` header
* @returns {Entry}
* @example
* import contentstack from '@contentstack/delivery-sdk'
*
* const stack = contentstack.stack({ apiKey: "apiKey", deliveryToken: "deliveryToken", environment: "environment" });
* const result = await stack.contentType('abc').entry('entry_uid').variants('xyz').fetch();
* const resultWithBranch = await stack.contentType('abc').entry('entry_uid').variants('xyz', 'branch_name').fetch();
*/
variants(variants: string | string[]): this {
variants(variants: string | string[], branchName?: string): this {
if (Array.isArray(variants) && variants.length > 0) {
this._variants = variants.join(',');
} else if (typeof variants == 'string' && variants.length > 0) {
this._variants = variants;
}
if (typeof branchName === 'string' && branchName.length > 0) {
this._variantsBranch = branchName;
}

return this;
}
Expand Down Expand Up @@ -189,10 +198,11 @@ export class Entry {
contentTypeUid: this._contentTypeUid,
entryUid: this._entryUid
};
if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}

Expand Down
8 changes: 5 additions & 3 deletions src/query/base-query.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { Pagination } from '../common/pagination';
import { FindResponse, params } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import type { Query } from './query';

export class BaseQuery extends Pagination {
Expand All @@ -10,6 +10,7 @@ export class BaseQuery extends Pagination {
protected _client!: AxiosInstance;
protected _urlPath!: string;
protected _variants!: string;
protected _variantsBranch!: string;

/**
* Helper method to cast this instance to Query type
Expand Down Expand Up @@ -231,10 +232,11 @@ export class BaseQuery extends Pagination {
contentTypeUid: this.extractContentTypeUidFromUrl()
};

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
50 changes: 44 additions & 6 deletions src/query/query.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,38 @@
import { AxiosInstance, getData } from '@contentstack/core';
import { BaseQuery } from './base-query';
import { BaseQueryParameters, QueryOperation, QueryOperator, TaxonomyQueryOperation, params, queryParams, FindResponse } from '../common/types';
import { encodeQueryParams } from '../common/utils';
import { buildVariantRequestHeaders, encodeQueryParams } from '../common/utils';
import { ErrorMessages } from '../common/error-messages';

export class Query extends BaseQuery {
private _contentTypeUid?: string;

constructor(client: AxiosInstance, params: params, queryParams: queryParams, variants?: string, uid?: string, queryObj?: { [key: string]: any }) {
constructor(
client: AxiosInstance,
params: params,
queryParams: queryParams,
variants?: string,
uid?: string,
queryObj?: { [key: string]: any }
);
constructor(
client: AxiosInstance,
params: params,
queryParams: queryParams,
variants?: string,
uid?: string,
variantsBranch?: string,
queryObj?: { [key: string]: any }
);
constructor(
client: AxiosInstance,
params: params,
queryParams: queryParams,
variants?: string,
uid?: string,
variantsBranchOrQueryObj?: string | { [key: string]: any },
queryObj?: { [key: string]: any }
) {
super();
this._client = client;
this._contentTypeUid = uid;
Expand All @@ -16,11 +41,23 @@ export class Query extends BaseQuery {
this._queryParams = queryParams || {};
this._variants = variants || '';

let variantsBranch = '';
let resolvedQueryObj: { [key: string]: any } | undefined;

if (typeof variantsBranchOrQueryObj === 'string') {
variantsBranch = variantsBranchOrQueryObj;
resolvedQueryObj = queryObj;
} else if (variantsBranchOrQueryObj) {
resolvedQueryObj = variantsBranchOrQueryObj;
}

this._variantsBranch = variantsBranch;

if (!uid) {
this._urlPath = `/assets`;
}
if (queryObj) {
this._parameters = { ...this._parameters, ...queryObj };
if (resolvedQueryObj) {
this._parameters = { ...this._parameters, ...resolvedQueryObj };
}
}
// Validate if input is alphanumeric
Expand Down Expand Up @@ -651,10 +688,11 @@ export class Query extends BaseQuery {
contentTypeUid: this._contentTypeUid
};

if (this._variants) {
const variantHeaders = buildVariantRequestHeaders(this._variants, this._variantsBranch);
if (variantHeaders) {
getRequestOptions.headers = {
...getRequestOptions.headers,
'x-cs-variant-uid': this._variants
...variantHeaders
};
}
const response = await getData(this._client, this._urlPath, getRequestOptions);
Expand Down
Loading
Loading