@volvo-cars/content-delivery-client v0.6.0
@volvo-cars/content-delivery-client
This package is a wrapper around the Content Delivery API. It currently supports consuming dictionaries.
Quick Start
First add @volvo-cars/content-delivery-client
to your projects dependecies in your package.json
file:
"dependencies": {
"@volvo-cars/content-delivery-client": "workspace:*"
}
Dictionaries
Create Dictionary Client
A single client can be shared across requests to utilize the cache.
import { createClient } from '@volvo-cars/content-delivery-client';
const contentClient = createClient({
applicationId: 'applicationId',
defaultDataSource: 'dotcom-sitecore-prod',
allowedEnvironments: process.env.ALLOW_PREVIEW
? ['authoringPreview', 'live']
: ['live'],
apiKey: process.env.CONTENT_DELIVERY_API_KEY,
// Will only fetch from api when revalidate (in seconds) has passed, defaults to 60
revalidate: 120,
forceLocalData: process.env.DEPLOY_ENV === 'dev',
});
You can also keep most configurations as environment variables:
import { createClient } from '@volvo-cars/content-delivery-client';
const contentClient = createClient({
applicationId: 'applicationId',
defaultDataSource: process.env.DEFAULT_CONTENT_DATA_SOURCE,
allowedDataSources: process.env.ALLOWED_CONTENT_DATA_SOURCES,
defaultEnvironment: process.env.DEFAULT_CONTENT_ENVIRONMENT,
allowedEnvironments: process.env.ALLOWED_CONTENT_ENVIRONMENTS,
apiKey: process.env.CONTENT_DELIVERY_API_KEY,
});
getAllDictionaries
Fetches all dictionaries in an application in a single API call.
try {
await contentClient.getAllDictionaries({
locale: 'en',
});
} catch (error) {
// handle error
}
getDictionary
Fetches a single dictionary.
try {
await contentClient.getDictionary('myNamespace.myDictionaryName', {
locale: 'en',
});
} catch (error) {
// handle error
}
getDictionaries
Convenience method to fetch multiple dictionaries. Sends one HTTP request per dictionary.
If a one of the dictionaries were missing in the given locale, the dictionaries from the
successful requests will be available as the data
property on the thrown error.
let dictionaries;
try {
dictionaries = await contentClient.getDictionaries(
['myNamespace.myDictionaryName', 'myNamespace.otherDictionary'],
{
locale: 'en',
}
);
} catch (error) {
if (error.name === 'DictionaryNotFoundError' && error.data) {
dictionaries = error.data;
}
}
API
createClient(clientConfig): ContentDeliveryClient
clientConfig
applicationId: string
Id of your applicationapiKey: string
Content Delivery API key for your product.defaultDataSource?: string
Default DataSource to fetch from.revalidate?: number | { dictionaries?: number, entries?: number }
An optional amount in seconds after which to revalidate cached content in the background. Defaults to 60 seconds. Set to 0 to always fetch from the network and only use the cache on errors.fetchOptions?: { agent?: Agent | null }
Options to pass through tofetch
. By default includes a Keep-Alive https Agent in Node.js. Passagent: null
or a customhttps.Agent
instance to disable the default agent.path?: string
File system path where local fallback content is available, if local fallbacks are used.forceLocalData?: boolean
Use content from the local (English) master files, no API calls are made. Useful in development. Defaults tofalse
. Only available in Node.js.fallbackToLocalData?: boolean
Use local (English) master content if the data is missing in the datasource. Defaults tofalse
. Only available in Node.js.
ContentDeliveryClient.getAllDictionaries(options: GetOptions): Promise<Dictionaries>
ContentDeliveryClient.getDictionary(canonicalDictionaryName: string, options: GetOptions): Promise<Dictionaries>
ContentDeliveryClient.getDictionaries(canonicalDictionaryNames: string[], options: GetOptions): Promise<Dictionaries>
ContentDeliveryClient.getEntry(canonicalName: string, options: GetOptions): Promise<unknown>
ContentDeliveryClient.listEntries(contentType: ContentType, options: GetOptions): Promise<ListEntriesResponseData>
Dictionaries
All methods return a Dictionaries
object where the key is the Canonical Dictionary Name, and the value is an object of dictionary items.
{
'namespace1.dictionaryName': {
'dictionaryItemKey': 'dictionaryItemValue',
}
}
GetOptions
locale: string
Which locale to fetch content for.environment?: live | authoringPreview | master
: Environment to fetch content from in the CMS. Defaults tolive
.preview?: boolean
Fetch preview content from the CMS. Defaults tofalse
market?: boolean
Fetch content from within a market node.operationId?: string
VCC-Api-OperationId header to send with the request.timeout?: number
Timeout in seconds after which to stop waiting for a response and throw an error instead.dataSource?
Data Source to fetch from. Defaults todefaultDataSource
from the Client Config.
ContentDeliveryError
Base class for all errors and always what's returned from any errors when using the Content Delivery API.
name: string
ContentDeliveryErrorerrors: Error[]
The original runtime error(s).dataSource: string
locale: string
preview: boolean
market?: string
operationId?: string
DictionaryNotFoundError
Thrown if a language version for a dictionary could not be found, or if no dictionaries are available in an application.
name: string
DictionaryNotFoundErrordata?: Dictionaries
Partial data from any sucessful requests.
Caching
Caching follows the Stale While Revalidate and Stale While Error patterns, which means any fetch except the first one will immediately return a response from a local in-memory cache. If the returned content was stale, meaning the revalidate
time has passed since it was last fetched, it will be updated in the background, and on the next fetch the fresh content will be returned.
This means that as long as a single request has ever succeeded in your application the client will continue to return (old, but available) dictionary entries until your server is restarted.
One copy of every entry in every locale in your application will be kept in a memory cache, which should be fine for most use cases. If this becomes a problem you can disable caching by setting revalidate
to 0
. A Least Recently Used cache expiration is on the TODO list.
Usage with React
Nextjs
// ./src/providers/DictionariesProvider.tsx
// Everytime you update dictionaries with items, a DictionaryItemTypes file will be
// written to ./config.path/. This will help with autocompleting and validating useTranslate hook.
import { DictionaryItemTypes } from '../src/content-management/DictionaryItemTypes';
import { getDictionariesProvider } from '@vcc-www/react-translate';
export const {
useTranslate,
DictionariesProvider,
} = getDictionariesProvider<DictionaryItemTypes>();
// ./src/components/MyComponent.tsx
import { useTranslate } from '../src/providers/DictionariesProvider';
export const MyComponent: React.FC = () => {
const translate = useTranslate();
return (
<div>
{translate('item1', {
parameter1: 'Some value',
parameter2: 'Some value',
})}
</div>
);
};
//translate will have typing support and give you hints on what items can be translated and what parameters can be given to that certain item
//pages/index.ts
import {
createClient,
Dictionaries,
} from '@volvo-cars/content-delivery-client';
import { DictionariesProvider } from '../src/providers/DictionariesProvider';
import { MyComponent } from '../src/components/MyComponent';
const contentClient = createClient({
path: './src/content-management',
apiKey: process.env.CONTENT_DELIVERY_API_KEY,
defaultDataSource: 'dotcom-sitecore-prod',
applicationId: 'myApplication',
forceLocalData: process.env.NODE_ENV === 'development',
});
export default function Index({
dictionaries,
}: InferGetStaticPropsType<typeof getStaticProps>) {
return (
<DictionariesProvider locale="en" dictionaries={dictionaries}>
<MyComponent />
</DictionariesProvider>
);
}
export const getStaticProps: GetStaticProps<{
dictionaries: Dictionaries,
}> = async (props) => {
try {
const dictionaries = await contentClient.getAllDictionaries({
locale: 'en',
});
} catch (error) {
console.log(error);
}
return {
props: {
dictionaries,
},
};
};
Usage with React SSR
// ./server.tsx
import React from 'react';
import ReactDomServer from 'react-dom/server';
import express from 'express';
import { createClient } from '@volvo-cars/content-delivery-client';
import App from './components/App.tsx';
import htmlTemplate from './build/index.html';
const app = express();
const port = 3000;
const contentClient = createClient({
path: './src/content-management',
apiKey: process.env.CONTENT_DELIVERY_API_KEY,
defaultDataSource: 'dotcom-sitecore-prod',
applicationId: 'myApplication',
revalidate: 60,
});
app.get('/', (req, res) => {
const content = ReactDomServer.renderToString(<App />);
let dictionaries = {};
try {
dictionaries = await contentClient.getAllDictionaries({
locale: 'en',
});
} catch (error) {
console.error(error);
}
const html = html.replace('<div id="root"></div>', `
<script type="application/json" id="__DICTIONARIES_STATE__">${JSON.stringify(dictionaries)}</script>
<div id="root">${content}</div>
`);
res.send(html)
});
app.listen(port, () => {
console.info(`Listening on port ${port}`);
});
//client.tsx
import React from 'react';
import ReactDOM from 'react-dom'
import App from './components/App.tsx';
import { DictionariesProvider } from './src/providers/DictionariesProvider';
const stateNode = document.getElementById('__DICTIONARIES_STATE__');
const dictionaries = JSON.parse(stateNode.innerHTML);
ReactDOM.hydrate(
<DictionariesProvider locale = "en" dictionaries={dictionaries}>
<App />
</DictionariesProvider>
,document.getElementById('app');
)