Skip to main content
Language:

Integrate a Custom Asset Source Into CE.SDK

In this example, we will show you how to integrate your custom asset API into CreativeEditor SDK.

Explore a full code sample of the integration on CodeSandbox or view the code on GitHub.

With CE.SDK you can directly add custom assets with single entries in the configuration. While this works for a couple of entries, you will need a more flexible approach if you have a vast number of images or want to use an external image provider like Getty or Unsplash. With the asset source configuration, you can integrate all assets your customers need in our creative editor. Follow along with this example while we are going to add the Unsplash library.

Adding an asset source is done by adding an entry in our configuration. In assetSources we need to add a unique identifier with an object implementing the interface of the source.

The most important function to implement is findAssets. With this function alone you can define the complete asset source. It receives two arguments to query assets and returns a promise with the results.

  • The argument is the queryData and describes the slice of data the editor wants to display. This includes a query string and pagination information.
  • The result of this query is a promise that, besides the actual asset data, returns information like the current page, the next page and the total number of assets available for this specific query.

Returning a promise gives us great flexibility since we are completely agnostic of how we want to get the assets. We can use fetch, XMLHttpRequest or import a library to return a result.

Let us implement an Unsplash asset source. Please note that this is just for demonstration purposes only and may not be ideal if you want to integrate Unsplash in your production environment. However, the CE.SDK team works on an Unsplash integration in the next release.

We will use the official unsplash-js library to query the Unsplash API (Link to Github page). According to their documentation and guidelines, we have to create an access key and use a proxy to query the API, but this is out of scope for this example. Take a look at Unsplash's documentation for further details. .

Unsplash has different API endpoints for different use cases. If we want to search we need to call a different endpoint as if we just want to display images without any search term provided by the user in the CE.SDK's search bar.

Therefore we need to check if the query data contains a query string. If findAssets was called with a non-empty query we can call the asynchronous method search.getPhotos. As we can see in the example, we are passing the query arguments to this method.

  • queryData.query: The current search string from the search bar in the asset library.
  • queryData.page: The requested page number starts with 1. We do not query all assets at once but by pages. As the user scrolls down more pages will be requested by calls to the findAssets method.
  • queryData.perPage: Determines how many assets we want to have included per page. This might change between calls. For instance, perPage is called with a small number like 3 to display assets in the overall overview as previews, but with a higher number if we want to display more assets in the grid view.

Luckily the arguments in the Unsplash API have the exact same names so we just need to pass them as is.

Once we receive the response and check for success we need to map Unsplash's result to what the asset source API needs as a result. The CE.SDK expects an object with the following properties:

  • assets: An array of assets for the current query. We will take a look at what these have to look like in the next paragraph.
  • total: The total number of assets available for the current query. If we search for "Cat" with perPage set to 30, we will get 30 assets, but total likely will be a much higher number.
  • currentPage: Return the current page that was requested.
  • nextPage: This is the next page that can be requested after the current one. Should be undefined if there is no other page (no more assets). In this case we stop querying for more even if the user has scrolled to the bottom.

Every image we get as a result of Unsplash needs to be translated into an object that is expected by the asset source API. We will describe every mandatory and optional property in the next paragraphs.

id: The id of the asset (mandatory). This has to be unique for this source configuration.

locale (optional): The language locale for this asset is used in label and tags.

label (optional): The label of this asset. It will be displayed in the tooltip as well as in the credits of the asset.

tags (optional): The tags of this asset. It will be displayed in the credits of the asset.

thumbUri: The URI of the asset's thumbnail. It will be displayed in the asset library.

size: The original size of the image. It is an object with width and height.

meta: This depends on the asset. For an image this will contain the uri that will be used to add the image to the scene.

context: Adds contextual information to the asset. Right now, this only includes the source id of the source configuration.

credits (optional): Some image providers require to display credits to the asset's artist. If set, it has to be an object with the artist's name and a url to the artist page.

utm (optional): Some image providers require to add UTM parameters to all links to the source or the artist. If set, it contains a string to the source (added as utm_source) and the medium (added as utm_medium)

After translating the asset to match the interface from the asset source API, the array of assets for the current page can be returned and will be displayed in the asset library.

In case, we've encountered an error, we need to reject the promise. Since we use the async/await language feature this means throwing a new error.

undefined can be returned instead of the result object if for some reason it does not make sense to throw an error but just show an empty result. Nothing will be displayed and this is equivalent to returning

{
assets: [],
currentPage: queryData.page,
total: 0,
nextPage: undefined
}

Going further with our Unsplash integration we need to handle the case when no query was provided. Unsplash requires us to call a different API endpoint (photos.list) with slightly different parameters but the basics are the same. We need to check for success, calculate total and nextPage and translate the assets.

We have already seen that an asset can define credits for the artist. Depending on the image provider you might need to add credits and the license for the source. In case of Unsplash, this includes a link as well as the license of all assets from this source.

File:
import 'https://cdn.img.ly/packages/imgly/cesdk-js/1.8.0/cesdk.umd.js';
import * as unsplash from './vendor/unsplash-js.esm.js';
const unsplashApi = unsplash.createApi({
apiUrl: '...'
});
const findUnsplashAssets = async (queryData) => {
if (queryData.query) {
const response = await unsplashApi.search.getPhotos({
query: queryData.query,
page: queryData.page,
perPage: queryData.perPage
});
if (response.type === 'success') {
const { results, total, total_pages } = response.response;
return {
assets: results.map(translateToAssetResult),
total,
currentPage: queryData.page,
nextPage:
queryData.page + 1 <= total_pages ? queryData.page + 1 : undefined
};
} else if (response.type === 'error') {
throw new Error(response.errors[0]);
} else {
return Promise.resolve(undefined);
}
} else {
const response = await unsplashApi.photos.list({
orderBy: 'popular',
page: queryData.page,
perPage: queryData.perPage
});
if (response.type === 'success') {
const { results, total } = response.response;
const totalFetched =
(queryData.page - 1) * queryData.perPage + results.length;
const nextPage = totalFetched < total ? queryData.page + 1 : undefined;
return {
assets: results.map(translateToAssetResult),
total,
currentPage: queryData.page,
nextPage
};
} else if (response.type === 'error') {
throw new Error(response.errors[0]);
} else {
return Promise.resolve(undefined);
}
}
};
const config = {
baseURL: 'https://cdn.img.ly/packages/imgly/cesdk-js/1.8.0/assets',
assetSources: {
unsplash: {
findAssets: findUnsplashAssets,
credits: {
name: 'Unsplash',
url: 'https://unsplash.com/'
},
license: {
name: 'Unsplash license (free)',
url: 'https://unsplash.com/license'
}
}
}
};
CreativeEditorSDK.init('#cesdk_container', config);
function translateToAssetResult(image) {
const artistName = image?.user?.name;
const artistUrl = image?.user?.links?.html;
return {
id: image.id,
locale: 'en',
//
label: image.description ?? image.alt_description ?? undefined,
tags: image.tags ? image.tags.map((tag) => tag.title) : undefined,
thumbUri: image.urls.thumb,
size: {
width: image.width,
height: image.height
},
meta: {
uri: image.urls.full
},
context: {
sourceId: 'unsplash'
},
credits: artistName
? {
name: artistName,
url: artistUrl
}
: undefined,
utm: {
source: 'CE.SDK Demo',
medium: 'referral'
}
};
}