Create an app to support Native External References
Last updated: September 2nd, 2024.
1. Set up project
- Run
npx create-contentful-app@latest <name-of-your-app> --example native-external-references
cd <name-of-your-app>
2. Create an app definition
- Run
npm run create-app-definition
Answer the questions:
- App name: can be left as default
- Select where your app can be rendered: press Space to choose App configuration screen
- Contentful CMA endpoint URL: can be left as default
- Would you like to specify App Parameter schemas?: select Y
- Is this an Instance or an Installation parameter? select Installation
- Parameter name: enter
TMDB access token
- Parameter ID: enter
tmdbAccessToken
- Parameter description: can be left empty
- Parameter type: select Symbol
- Is this parameter required?: select Y
- Default value: can be left empty
- Do you want to add another parameter?: select N
- Please paste your access token: paste a Contentful token from the newly opened browser tab
- Select an organization for your app: select the organization you want to use
3. Build an upload your app
- Run
npm run build
npm run upload
Answer the questions:
- Add a comment to the created bundle: can be left empty
- Do you want to activate the bundle after upload?: select Y
- Contentful CMA endpoint URL: can be left as default
4. Create resource provider and resource type
- Run
npm run create-resource-entities
5. Install the app into an existing space
- Run
npm run install-app
- Select the space and environment in which you would prefer to install the app.
- Grant access to the space the app will be installed in.
- Input your TMDB API token into the form and save the changes.
6. Configure your content model
- In the Contentful web app, navigate to the space where you have installed the TMDB app.
- Create a new content type and add a new field of type Reference. Provide the field name and select Different spaces and external sources.
- Under the Validation section, set the Source dropdown field to TMDB and select
Movie
andPerson
Entity type. Save the changes.
7. Add external references in an entry
Create a new entry of the content type you created in the previous step.
Click the button to add content and select one or more displayed
TMDB
entities. You can choose fromMovie
andPerson
types. You can use the search bar to find specific entities.Click Add to confirm your selection and publish the entry.
Table of contents
- What are external references?
- The example app
- Prerequisites
- Step 1: Copy the example project
- Step 2: Create an app definition
- Step 3: Build and upload your app
- Step 4: Create resource provider and resource type
- Step 5: Install the app into an existing space
- Step 6: Configure your content model
- Step 7: See your app in action
- Let’s take a step back
What are external references?
Contentful provides a method for integrating content from external sources using the App Framework. The App Marketplace currently offers complete solutions tailored for specific systems such as Shopify or commercetools. However, connecting to other systems requires developing custom frontend apps with bespoke implementation.
To overcome these challenges, we offer a more streamlined and cohesive approach to linking third-party systems through existing content model Reference Fields. This upgraded version of fields is referred to as Native external references.
The example app
This guide is designed to walk you through the beta version of the extended linking feature. Currently, the application setup primarily revolves around command-line operations. However, you can also view the connected content displayed in the user interface. For the purpose of this example, we will be connecting to the Movie Database external system.
With Native external references we introduce the following new entity types that allow us to model the data from third-party systems in Contentful:
Resource Provider
- a third-party system that provides resources. Each provider can have multiple resource types. In our example: aTMDB
provider.Resource Type
- a specific type of resource (not the resource itself) that is provided by a resource provider. In our example:Movie
andPerson
.Resource
- a representation of real data in an external system. For instance,Jaws
.
Prerequisites
- We are assuming you are familiar with the following concepts:
- App Framework, including App Definition and App Installation
- Functions
- Your organization has a Premium or Premium+ plan.
- The space where you will install the application has the Orchestration feature enabled.
- A valid API token for the TMDB API is required to run this app. You can get one by signing up at TMDB.
Step 1: Copy the example project
To get started with your application, you need to make a local copy of the example code by running the following command in your terminal.
<name-of-your-app>
with the name of your choice.
npx create-contentful-app@latest <name-of-your-app> --example native-external-references
Once the process finishes, navigate to the directory that was created by running this command:
cd <name-of-your-app>
Step 2: Create an app definition
To see your app within Contentful, you must first set it up. To do that, we will create an app definition, which is an entity that represents an app in Contentful and stores general information about it.
To create the app definition, run this command:
npm run create-app-definition
You will need to answer the following questions on the terminal. Feel free to proceed with the default options provided.
Name of your application. This is how your app will be named and it will be displayed in a few places throughout the UI. The default is the name of the folder you created.
Select where your app can be rendered. This shows potential app locations where an app can be rendered within the Contentful Web app. Select App configuration screen, as we will utilize the configuration screen to provide the API token for the app.
Contentful CMA endpoint URL. This refers to the URL used to interact with Contentful's Management APIs. In most cases the proposed CMA URL should be selected.
App Parameters. These are configurable values that can be used to set default values or define custom validation rules. As we need to define these for the API token:
- Opt for Y to advance with the process of defining the parameters.
- Choose Installation.
- Input
TMDB access token
as Parameter name andtmdbAccessToken
as ID. - Select Secret as type and mark the parameter as required.
- Opt for N in the question about another parameter to end the process of defining the parameters.
The next steps will lead you through the process of providing a Contentful access token to the application and specifying the organization to which the application should be assigned.
NOTE: Make sure the organization ID you select here is the same as the ID you shared with us to enable the Native external references.
You now have a basic application that can be enriched with additional information that will enable the example project you downloaded to function properly.
Step 3: Build and upload your app
After creating the app definition, we can take care of uploading the code by running these commands:
npm run build
npm run upload
The interactive CLI will prompt you to provide additional details, such as a CMA endpoint URL. Select Yes when prompted if you’d like to activate the bundle after upload.
Step 4: Create resource provider and resource type
Executing the provided command in your terminal will generate three extra entities within the app definition, which are described in more detail under the Entities Overview step.
npm run create-resource-entities
This will tell Contentful that we want to connect to TMDB
via the function we uploaded in Step 3 and that the same function can provide TMDB:Movie
and TMDB:Person
Resource Types.
create-resource-entities
script generates new entities within the system, and prints out a minimal log when the operation has succeeded.
If you would like to list all the previously created entities, you can utilize a similar script that prints out more verbose details: npm run show-resource-entities
.
Step 5: Install the app into an existing space
After you successfully upload your app, install it to an existing space by running the command:
npm run install-app
Select the space with the Orchestration feature enabled and environment in which you would prefer to install the example app from the dialog that appears:
You will have to grant access to the space the app will be installed in:
After granting access, the configuration screen will be displayed. Input your TMDB API token into the form and click Install to selected environments button to save the changes:
Your example app is now configured and installed.
Step 6: Configure your content model
To render content from TMDB, we must first create and configure the content model. To do so:
- In the Contentful web app, navigate to the space where you have installed the TMDB app in Step 5.
- Create a new content type with your preferred name for it.
- Click Add field and select Reference as the field type.
- In the following screen, enter a field name and select the Different spaces and external sources option. When selecting the Type, you can choose between having a single link or a group of links. Confirm your choices by clicking Add and configure.
- Under the Validation section, specify the sources that are allowed as potential sources of data in the field being configured.
Set the Source dropdown field to TMDB. Once selected, the Entity type dropdown allows you to choose the
Movie
andPerson
Resource Types we previously configured at Step 4. Select theTMDB
source and at least one of the Entity types. Confirm your choices and click the Save button.
Step 7: See your app in action
Now that the content model has been configured to display resources from TMDB, an entry can be created to incorporate data from particular external sources. To create an entry:
In the Contentful web app, navigate to the space where you configured the content model in the previous step. Go to the "Content" tab and click Add entry.
The configured field for linking to external sources will feature a button for adding content. Please click on it.
In the displayed entity picker, you can select the content source you have configured in the content model, which includes
TMDB
.Type a search query, and if TMDB finds any relevant matches, they will be displayed below the search bar.
If your field is a single reference field, select one of the entries (the picker will close itself automatically). For multiple references fields, you can select as many entries as needed and then click the Add button once you are satisfied with your choices.
NOTE: If you are using a field with multiple references, you are not limited to only one source. You can combine cross-space links with resources from TMDB.Your first entry with Native external references is ready! Click Publish once you are done with the editing.
Let’s take a step back
Now let’s go back and take a detailed look at what we did.
Entities overview
Below is a representation of how a Resource Provider is structured, using the TMDB app as an illustrative example.
{
"sys": { "id": "TMDB" },
"type": "function",
"function": {
"sys": {
"type": "Link",
"linkType": "Function",
"id": "externalResources"
}
}
}
We are representing Resource Types in a similar structure:
{
"sys": { "id": "TMDB:Movie" },
"name": "Movie",
"defaultFieldMapping": {
"title": "{ /name }",
"subtitle": "Movie ID: { /urn }",
"externalUrl": "{ /externalUrl }",
"image": {
"url": "{ /image/url }",
"altText": "{ /name }"
}
}
}
{
"sys": { "id": "TMDB:Person" },
"name": "Person",
"defaultFieldMapping": {
"title": "{ /name }",
"subtitle": "Person ID: { /urn }",
"externalUrl": "{ /externalUrl }",
"image": {
"url": "{ /image/url }",
"altText": "{ /name }"
}
}
}
Code walkthrough
The example app is using Functions to provide a connection between Contentful and the TMDB API. In the functions/index.ts
file we are defining two events that are necessary for Native external references to function properly:
search
- retrieval of specific content based on search querieslookup
- retrieval of specific content based on URNs (IDs)
The example code for these handlers can be found in the functions/lookupHandler.ts
and functions/searchHandler.ts
files.
Search request
Search handler expects the following shape for outgoing requests:
type ResourcesSearchRequest = {
type: 'resources.search';
resourceType: string;
query: string;
limit?: number;
pages?: {
nextCursor: string;
};
};
Property | Type | Description |
---|---|---|
limit |
number (required) |
Number defining the maximum items that should be returned |
pages |
object (optional) |
|
pages.nextCursor |
string (required) |
Cursor string pointing to the specific page of results to be used as a starting point for the request |
resourceType |
string (required) |
String consisting of the name of the provider and the resource within the provider, in a format {Provider}:{Type} , eg. TMDB:Movie |
type |
resources.search (required) |
Identifier for the type of the event |
query |
string (optional) |
Search query to be passed to Contentful Function, which in turn passes it down to the 3rd party system's search API |
Lookup request
Lookup handler expects the following shape for outgoing requests:
type Scalar = string | number | boolean;
type ResourcesLookupRequest<
L extends Record<string, Scalar[]> = Record<string, Scalar[]>
> = {
type: 'resources.lookup';
lookupBy: L;
resourceType: string;
limit?: number;
pages?: {
nextCursor: string;
};
};
Property | Type | Description |
---|---|---|
limit |
number (required) |
Number defining the maximum items that should be returned |
pages |
object (optional) |
|
pages.nextCursor |
string (required) |
Cursor string pointing to the specific page of results to be used as a starting point for the request |
resourceType |
string (required) |
String consisting of the name of the provider and the resource within the provider, in a format {Provider}:{Type} , eg. TMDB:Movie |
type |
resources.lookup (required) |
Identifier for the type of the event |
lookupBy |
object (optional)(optional) |
|
lookupBy.urns[] |
string[] (required) |
List of IDs of entities to be fetched |
Response
Both events return the same shape of the response:
type Resource = {
urn: string;
name: string;
image?: {
url: string;
};
externalUrl: string;
};
type ResourcesSearchResponse = {
items: Resource[];
pages: {
nextCursor?: string;
};
};
type ResourcesLookupResponse = {
items: Resource[];
pages: {
nextCursor?: string;
};
};
Property | Type | Description |
---|---|---|
items |
Resource[] (required) |
List of returned resources |
pages |
object (required) |
|
pages.nextCursor |
string (optional) |
Cursor string to be used to request next page of results |
Functions are designed without prior knowledge of the response data structure from third-party systems. Therefore, an additional transformation is required to ensure that all Function events return responses with a consistent shape of Resource
s. This transformation is carried out by the transformResult
function located in the function/helpers.ts
file. In our example, we are expecting Resource to conform to a particular shape:
type Resource = {
urn: string;
name: string;
image?: {
url: string;
};
externalUrl: string;
};
Property mapping for rendering in the Web app
The Contentful web app displays entries using components that require specific data structures to fill their UI elements.
The mapping between these components and external system data is established using JSON pointers. This mapping is defined in the defaultFieldMapping
property of each Resource Type and must adhere to the structure used for mapping the values shown in the entry component:
Property | Type |
---|---|
title |
string (required) |
subtitle |
string (optional) |
description |
string (optional) |
externalUrl |
string (optional) |
image |
object (optional) |
image.url |
string (required) |
image.altText |
string (optional) |
badge |
object (optional) |
badge.label |
string (required) |
badge.variant |
string (required) |
The definitions of Movie
and Person
Resource Type representations can be found in the src/tools/entities/movie.json
and src/tools/entities/person.json
files, respectively.
Example
Let’s assume we fetched the following movie entity from TMDB
and we defined the following mapping for the Movie
resource:
Movie data from the TMDB API
{
"id": 578,
"original_language": "en",
"original_title": "Jaws",
"poster_path": "/lxM6kqilAdpdhqUl2biYp5frUxE.jpg",
"release_date": "1975-06-20",
"overview": "The terrifying motion picture from the terrifying No.1 best seller.",
"title": "Jaws"
}
Movie resource
{
"sys": { "id": "TMDB:Movie" },
"defaultFieldMapping": {
"title": "{ /name }",
"subtitle": "Movie ID: { /urn }",
"externalUrl": "{ /externalUrl }",
"image": {
"url": "{ /image/url }",
"altText": "{ /name }"
}
}
}
Visually, we could combine these transformations as follows:
Adjust the view to your needs
If you prefer different data to be displayed in the web app entry, you can adjust the view to your preference. For example, if you’d rather display an overview as a movie description
instead of the Movie ID
:
- Modify the Resource type defined in the
functions/types/tmdb.ts
file to add a new property that will be used to store new information, for exampleoverview: string
. - Adjust the transformation logic in the
fuctions/helpers.ts
file to accommodate to the new property, for example:
return {
urn: String(result.urn),
// ... other existing transformations for the return object
overview: 'overview' in result ? result.overview : '',
};
- In your code editor, open the
src/tools/entities/movie.json
file. Modify the pointer values indefaultFieldMapping
to point to your desired TMDB property. In our example: replace thesubtitle
value"Movie ID: { /urn }"
with"{ /overview }"
. - Update the resources by running the
npm run create-resource-entities
creation script. - Build the function and update the bundled version stored in Contentful:
npm run build
npm run upload
6.Refresh your entry in the web app. You should be seeing an updated version with the overview present.
Create additional resource type
In our example we have focused on one particular Resource Type: Movie
, as well as provided types and implementation logic for your reference for a Person
Resource Type. If you want to add more resource types, consider the following:
Discover more entities available in the TMDB API. A good candidate for another Resource Type could be
TV Series
.Based on the TMDB documentation, add the shape of TMDB resource to the
functions/types/tmdb.ts
file and extend theTmdbItem
type with the new type, for example:type TvSeries = { id: number; name: string; poster_path: string; overview: string; origin_country: string[]; }; type TmdbItem = Movie | Person | TvSeries;
Adjust the transformation logic in the
transformResult
function from thefunctions/helpers.ts
file, to take into account differentTvSeries
properties (eg. the countries of origin). Depending on the additional fields from theTvSeries
type you’d like to include in the Entry mapping, you will have to adjust theResource
definition as well, for example:type Resource = { urn: string; name: string; image?: { url: string; }; externalUrl: string; overview: string; country?: string; };
Create a new entity file in
src/tools/entities/
, for exampletv-series.json
with a Resource Type definition.Update the
src/tools/import.ts
file to import the newly created JSON file.Update the resource entities creations script in
src/tools/create-resource-entities.ts
to include the new type.Update the
getUrls
helper infunctions/helpers.ts
file to properly create the TMDB endpoint URLs.Update the resources by running the creation script:
npm run create-resource-entities
.Build the function and update the bundled version stored in Contentful:
npm run build
npm run upload
Feedback
Our Product team highly welcomes feedback for all our features. To share your feedback, fill in this form.