Work with events and types common to all Ninetailed implementations.
The Shared SDK exposes Ninetailed data objects, types, and methods applicable across runtimes. The Shared SDK allows you to create an API Client that facilitates constructing and sending Experience API events. It also handles retrying requests and request errors.
Elements of the shared SDK are composed to create the JavaScript SDK, which is then composed into Ninetailed's React-based SDKs.
The organization ID/API key of a Ninetailed account.
environment
environment?: string
The environment key of a Ninetailed account. Typically either main or development, depending on the environment in which you have configured your content source connections. your you have configured Defaults to main if unsupplied.
url
url?: string
Specify the base URL of the Experience API. This should usually be left unspecified. Defaults to the production Experience API base URL of https://experience.ninetailed.co when unspecified.
fetchImpl
fetchImpl?: FetchImpl
A NinetailedApiClient can be used in different JavaScript runtimes including the browser, Node.js, and edge workers. However, a implementation of the fetch() method available in the browser may not be available within all of those run times. This option allows you to provide a fetch() implementation of your own should the runtime not expose one automatically.
Profile Methods
A NinetailedAPIClient maps one function for each Experience API endpoint. upsertProfile is also provided to conveniently switch between create and update functions.
Supply an array of events to update many profiles at once. Responds with the representation of each upserted profile. Interfaces with the batch upsert profile endpoint.
The returned response of each method is the same as that of the Experience API; a data structure indicating the complete representation of the profile(s) and the Ninetailed Experiences & variants that the Experience API has selected for the profile(s).
Request Options
All of profile methods accept request options in addition to the supplied events. These are used to control request timeout & retry behaviour, performance, localization, and location resolution.
typeRequestOptions= {/** * A timeout after which a request will get cancelled */ timeout?:number;/** * Return a profile as though events have been submitted without actually storing the profile state * Useful in ESR or SSR contexts */ preflight?:boolean;/** * Determines the locale to which to localize location.city & location.country properties */ locale?:string;/** * An IP address to override the IP lookup behaviour, if using also supplying "ip-enrichment" as an enabled feature * Used in ESR or SSR environments to proxy a client's IP instead of using the server's IP */ ip?:string;/** * Sending requests as plain-text bypasses the need for a CORS preflight request, making for a faster request. */ plainText?:boolean;/** * The maximum amount of retries for a request. * Only 503 errors will be retried. The Ninetailed API is aware of which requests are retryable and send a 503 error.
* * @default 1 */ retries?:number;/** * The maximum amount of time in ms to wait between retries. * By default the retry will be sent immediately as the Ninetailed API is serverless and sends a 503 error if it could not recover a request internally.
* * @default 0 (no delay) */ minRetryTimeout?:number;/** * Activated features which the API should use for this request. */ enabledFeatures?:Feature[];};/** * "location" = Attempt to resolve the location of the user based on the IP of the request and where it ingresses to the Experience API
* "ip-enrichment" = Attempt to resolve firmographic data with a connected Albacross API Key. See Setup => Customer Data => Albacross
*/typeFeature="location"|"ip-enrichment"
Event Building Functions
Each of the profile methods above accepts an array of events. Event building helper functions facilitate generating the required payload for page, track, and identify events. These builder methods take care of parsing the supplied ctx.url and populating context properties on events commonly used by Audience rules, including context.page and context.campaign.
functionbuildPageEvent(data: { messageId:string; // A UUID timestamp:number; // Typically assigned as Date.now() ctx: { url:string; // Supply the whole URL, including the protocol, domain, path, and any query parameters referrer:string; locale:string; userAgent:string; document?: { title:string; } |undefined; }// Optionally supply an object to merge with API-resolved location location?:Geolocation; properties:Record<string,any>; // JSON})functionbuildTrackEvent(data: { messageId:string; // A UUID timestamp:number; // Typically assigned as Date.now() ctx: { url:string; // Supply the whole URL, including the protocol, domain, path, and any query parameters referrer:string; locale:string; userAgent:string; document?: { title:string; } |undefined; }// Optionally supply an object to merge with API-resolved location location?:Geolocation; event:string; // the name of the event properties:Record<string,any>; // JSON})functionbuildIdentifyEvent(data: { messageId:string; // A UUID timestamp:number; // Typically assigned as Date.now() ctx: { url:string; // Supply the whole URL, including the protocol, domain, path, and any query parameters referrer:string; locale:string; userAgent:string; document?: { title:string; } |undefined; }// Optionally supply an object to merge with API-resolved location location?:Geolocation; userId:string; // An alias. If you don't want to set one, use an empty string "" traits:Record<string,any>; // JSON})typeGeolocation= { coordinates?: { latitude:number; longitude:number; }, city?:string; // Use proper capitalization of the city name postalCode?:string; region?:string; regionCode?:string; // ISO 3166-2 country?:string; countryCode?:string; // ISO 3166-2 continent?:string; timezone?:string;}
Example Shared SDK Usage
This is a short example of using the buildPageEvent helper to create a well-formatted event of type page and using it to upsert a profile.
Middleware Example
import {buildPageEvent, buildTrackEvent, buildIdentifyEvent, NinetailedApiClient, NINETAILED_ANONYMOUS_ID_COOKIE } from "@ninetailed/experience.js-shared"
import { v4 as uuidv4 } from'uuid';constclientID="YOUR_NINETAILED_CLIENT_ID"||undefined;constapiClient=newNinetailedApiClient({ clientId });constmiddleware= (req:Request) => {buildPageEvent({// Hardcoded strings are for example purposes// You'd populate these from the incoming Request object ctx: { url:'https://www.example.com/path/?query=test' locale: 'en-US', referrer:'https://www.google.com/', userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
} messageId: uuidv4(), timestamp:Date.now(),// Resolve your original request geolocation info and pass it along location: { city:request.geo?.city, region:request.geo?.region, country:request.geo?.country, continent:requqest.geo?.continent, } properties: {}, });constapiResponse=awaitapiClient.upsertProfile( {// Use cookies to store and read a Ninetailed profile ID// `upsertProfile` takes care of case where this isn't present profileId:req.cookies.get(NINETAILED_ANONYMOUS_ID_COOKIE)?.value events: [{ pageEvent }], }, { ip:req.ip } // Pass original IP address );// If no profile ID cookie present, be sure to write it in a responsereturn apiResponse;}