@apptus/esales-api
esales-api
Read the section below for info on upgrading from v2 to v3.
This version of the library will only work with Elevate 4 Storefront API v3. Support for Elevate API v2 is limited to
v1.x.xof this package. For information about upgrading to thev2version, see the upgrade section
A helper library for making requests to the Elevate Storefront API v3. It includes type definitions for all HTTPS responses and the library API.
Requirements
Install the package from npm:
npm install @apptus/esales-api
- Supports Browsers, Deno and Node v18+ by using
globalThis.fetch()to send requests cross-platform - Ships code bundled into in ESM and CJS format
- Ships with modern ES2022 code - tooling like
esbuildcan be used to support older platforms/environments (see tooling) - See polyfills for required global API's
Usage
The full API and all options/return-values are described by the Typescript
definitions (dist/mod.d.ts) that is bundled with this library.
import { elevate } from '@apptus/esales-api';
const api = elevate({
market: 'GB',
locale: 'en-GB',
clusterId: '<ID>',
touchpoint: 'desktop',
session: { customerKey: '<UUID>', sessionKey: '<UUID>' }
});
async function main() {
const result = await api.query.searchPage({ q: 'jeans', limit: 60 });
// Use the search result
}
main().catch(err => console.warn(err));
A CHANGELOG.md is included in the package, but npm can't show
individual files from a package online. The changelog can be read
on e.g. esm.sh or unpkg.com, or any other npm mirror.
Providing session information
The session property ties each request to a specific visitor via their
customerKey and sessionKey. When these values are already known when the
API is initialized, pass them directly as an object:
const api = elevate({
...
session: { customerKey: '<UUID>', sessionKey: '<UUID>' }
});
If the session isn't available yet, or you want the values re-read on every query and notification, provide a callback instead. It's invoked for each request, so it always sees the latest values:
const api = elevate({
...
session: () => ({
customerKey: readCookie('customerKey'),
sessionKey: readCookie('sessionKey')
})
});
Both the object and the callback may also be asynchronous; either passing a promise directly or returning it from the callback.
const api = elevate({
...
session: async () => {
const { customerKey, sessionKey } = await loadSession();
return { customerKey, sessionKey };
}
});
User context
By default every request is treated as PERSONALIZED — tied to
the visitor's customerKey/sessionKey for personalization and
behavioral tracking. A session can opt into a different level of consent
by carrying a userContext:
// Fully anonymous — no keys, no personalization or tracking
elevate({ ..., session: { userContext: 'ANONYMOUS' } });
// Session-scoped but not personalized — sessionKey only
elevate({ ..., session: { userContext: 'UNPERSONALIZED', sessionKey: '<UUID>' } });
The four contexts and the keys they accept:
userContext |
customerKey |
sessionKey |
Notes |
|---|---|---|---|
PERSONALIZED |
required | required | Default when userContext is omitted |
UNPERSONALIZED |
— | required | |
ANONYMOUS |
— | — | No keys sent |
SYNTHETIC |
optional | optional | Keys accepted but ignored by the server |
Because the keyless contexts (ANONYMOUS and SYNTHETIC) carry no keys,
the context string can be passed directly as a shorthand for
{ userContext: '<context>' }:
elevate({ ..., session: 'ANONYMOUS' }); // same as { userContext: 'ANONYMOUS' }
Since it lives on the session metadata, userContext works with every form —
object, promise, or callback (and the shorthand works wherever a keyless
context is allowed) — so it can be decided per request, e.g. from a
cookie-consent choice:
const api = elevate({
...
session: async () => {
const { consented, customerKey, sessionKey } = await loadUserInfo();
return consented ? { customerKey, sessionKey } : 'ANONYMOUS';
}
});
For client-only SPAs that persisted session data in LocalStorage
prior to v3, the localStorageBackedSession() helper provides a drop-in
callback (see Upgrading). It manages only
customerKey/sessionKey and does not handle userContext.
To pair it with a context decision, wrap the callback:
For integrations rendering the site on the server (Next.js, SvelteKit, etc.), the
localStorageBackedSession()utility should be avoided. On the serverLocalStoragewill not exist, and if the Storefront API is used on both client and server, there are better ways to keep thecustomerKey+sessionKeycombo in sync between them.
const stored = localStorageBackedSession();
const session = () => (hasConsent() ? stored() : 'ANONYMOUS');
Upgrading
From v2 to v3
The CHANGELOG.md includes the full list of changes. For integrators
using this package, the changes listed below are required when upgrading
to version 3.0.0 of the package:
A new property
sessionis now required when initializing the API. Prior to v3, session information was automatically handled viaLocalStorage. The same behavior as before can be achieved via the methodlocalStorageBackedSession().// Before import { esales } from '@apptus/esales-api'; const api = esales({ clusterId: 'wABCD1234', market: 'UK', locale: 'en-GB', touchpoint: 'desktop' }); // After import { esales, localStorageBackedSession } from '@apptus/esales-api'; const api = esales({ clusterId: 'wABCD1234', market: 'UK', locale: 'en-GB', touchpoint: 'desktop', session: localStorageBackedSession() });The
sessionproperty on the configured API object has been removed. Functionality related to reading/updating session metadata toLocalStoragecan be found on the object returned bylocalStorageBackedSession().// Before const api = esales({ ... }); api.session.sessionKey; api.session.customerKey; api.session.customerKey = user.id; api.session.reset(); // After const session = localStorageBackedSession(); const { customerKey, sessionKey } = session(); session.updateCustomerKey(user.id); session.reset();All
esales().notify.*methods are now returning promises, and should thus be handled to avoid uncaught promises. Previously, these POST messages were sent viaNavigator.sendBeacon(). This has been replaced withfetch()with thekeepaliveflag, to make the library supported cross platform.// Before try { api.notify.click(); } catch { // failed to queue POST request with notification } // After await api.notify.click().catch(() => {}); // to ignore errors, or: try { await api.notify.click(); } catch { // Network error or a non-OK HTTP status code }
From v1 to v2
The CHANGELOG.md includes the full list of changes. For integrators
using this package, the changes listed below are required when upgrading
to version 2.0.0 of the package:
- A new property
locale, must be added to the API initialization. This field should be one of the locales that is used together withmarketwhen importing products with the Admin API.// Before const api = esales({ ..., market: 'SE' }); // After const api = esales({ ..., market: 'SE', locale: 'sv-SE' }); - Rename the paramater
pageIdtopageReference, for thequery.landingPage()request.const api = esales(options); // Before api.query.landingPage({ ...params, pageId: id }); // After api.query.landingPage({ ...params, pageReference: id }); - Change
webApiIdtoclusterIdfor initialization configuration.// Before const api = esales({ ..., webApiId: 'w00000000' }); // After const api = esales({ ..., clusterId: 'w00000000' });
We've also cleaned up the public API surface of the package slightly, by removing some internal/private features. There should not be any reason for using them, and thus should not affect upgrades:
- Removed
@apptus/esales-api/mocksubmodule. It exposed some helper functions for creating mocked objects of API responses. - Removed
api.query.settings()method, which used a private/undocumented API endpoint - Removed
webApiUrl. It's possible to provide a URL toclusterIdif necessary. Any Integrators should prefer using an ID over URL forclusterId. When providing a URL toclusterIdthere should not be a/api/v2/suffix, but instead simply end the path with'/'or''.
Future work
This library is in active development. Highest on the agenda:
- Open source the repository on Github
- Make
CHANGELOG.mdmore accessible (currently included in NPM package)
Tooling
This library is packaged as an ES Module with zero dependencies. This requires the use of a pre-processor step since bare-imports are not natively supported in browsers (without import maps), but most build tools should handle this.
Additionally, in order to ship smallest possible bundle, the code is using a
modern ES2022 syntax. This may not work with the desired browser support
out of the box, and will likely require transpiling before use.
Transpilation
An example of how to transpile this library when building with Webpack is shown
below. Something similar can be done with Rollup by using the include property with
@rollup/babel-plugin.
// webpack.config.js
const { resolve } = require('path');
module.exports = {
module: {
rules: [
{
test: /\.m?js$/,
type: 'javascript/auto',
include: [resolve(__dirname, 'node_modules/@apptus/esales-api')],
use: ['babel-loader']
}
]
}
};
Polyfills
Depending on the level of required browser support, polyfills may need to be included before using this library. It is assumed that the following API's are available:
- All
ES2015API's globalThis.fetchglobalThis.URLglobalThis.crypto.randomUUID()(only whenlocalStorageBackedSession()is used)Object.entriesObject.values
License
MIT