Was this page helpful?

Create an app to support Native External References

Last updated: September 2nd, 2024.

Follow the steps below to get your app up and running almost immediately. For more detailed walkthrough, continue with the tutorial below.

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 and Person 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 from Movie and Person types. You can use the search bar to find specific entities.

  • Click Add to confirm your selection and publish the entry.

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: a TMDB provider.
  • Resource Type - a specific type of resource (not the resource itself) that is provided by a resource provider. In our example: Movie and Person .
  • Resource - a representation of real data in an external system. For instance, Jaws.

Prerequisites

  • We are assuming you are familiar with the following concepts:
  • 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.
NOTE: TMDB does not generate the API token instantly. Make sure you initiate the signup process in advance to prevent any hindrance to your progress.
  • Your system has installed:
NOTE: The Native external references functionality has been activated in the organization that you have designated for participation in the beta program. Please make sure you consistently choose the same Organization ID during any setup steps that require you to select the organization.

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.

NOTE: Make sure you replace <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.

  1. 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.

  2. 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.

  3. 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.

  4. 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 and tmdbAccessToken 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.
  5. 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.

NOTE: 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:

Installation destination dialog

You will have to grant access to the space the app will be installed in:

Permissions dialog

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:

App configuration screen

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:

  1. In the Contentful web app, navigate to the space where you have installed the TMDB app in Step 5.
  2. Create a new content type with your preferred name for it.
  3. Click Add field and select Reference as the field type.
  4. 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.
  5. 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 and Person Resource Types we previously configured at Step 4. Select the TMDB source and at least one of the Entity types. Confirm your choices and click the Save button.

    Add a new content type with validation for external resources

NOTE: If you choose Contentful as the source, you can combine cross-space Contentful resources with TMDB entities when dealing with fields that have multiple references.
NOTE: You may consider adding a Symbol field type to denote the entry's title, as illustrated in the GIF in the subsequent steps. It is important to note that this step is optional and not a prerequisite for successfully finishing the guide.

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:

  1. 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.

  2. The configured field for linking to external sources will feature a button for adding content. Please click on it.

  3. In the displayed entity picker, you can select the content source you have configured in the content model, which includes TMDB.

  4. Type a search query, and if TMDB finds any relevant matches, they will be displayed below the search bar.

  5. 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.
  6. Your first entry with Native external references is ready! Click Publish once you are done with the editing. Create an entry with external resources

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 queries
  • lookup - 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 Resources. 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:

Resource mapping visualization

Adjust the view to your needs

NOTE: This section is intended to be an optional part of the exercise, depending on what you would like to cover and how much time you would like to spend on it. Therefore, the following instructions should be regarded as general guidance, considering that some additional code changes not mentioned may be necessary for these steps to work.

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:

  1. 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 example overview: string.
  2. 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 : '',
};
  1. In your code editor, open the src/tools/entities/movie.json file. Modify the pointer values in defaultFieldMapping to point to your desired TMDB property. In our example: replace the subtitle value "Movie ID: { /urn }" with "{ /overview }".
  2. Update the resources by running the npm run create-resource-entities creation script.
  3. 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. External resource with an overview

Create additional resource type

NOTE: This section is intended to be an optional part of the exercise, depending on what you would like to cover in the exercise and how much time you would like to spend on it. Therefore, the following instructions should be regarded as general guidance, considering that some additional code changes not mentioned may be necessary for these steps to work.

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 the TmdbItem 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 the functions/helpers.ts file, to take into account different TvSeries properties (eg. the countries of origin). Depending on the additional fields from the TvSeries type you’d like to include in the Entry mapping, you will have to adjust the Resource 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 example tv-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 in functions/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.