> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://contentful.com/developers/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/developers/docs/_mcp/server.

# Resource Entities

> Learn how to use Resource Entities to extend Contentful with native external references. Understand how resource providers and resource types work together to connect your app with external services.

### Entities overview

With Native external references, we introduce the following new entity types that allow us to model data from third-party systems, such as [MockShop](https://mock.shop/), in Contentful:

* `Resource Provider` - A third-party system that provides resources. Each provider can have multiple resource types. In our [tutorial example](/extensibility/app-framework/native-external-references-example-tutorial), this is a MockShop provider.

* `Resource Type` - A specific type of resource (not the resource itself) provided by a resource provider. In our [tutorial example](/extensibility/app-framework/native-external-references-example-tutorial), this is Product.

Below is a representation of how a Resource Provider is structured, using the [MockShop app](https://github.com/contentful/apps/tree/master/examples/native-external-references-mockshop) as an illustrative example.

```json
{
  "sys": { "id": "MockShop" },
  "type": "function",
  "function": {
    "sys": {
      "type": "Link",
      "linkType": "Function",
      "id": "externalResources"
    }
  }
}
```

We are representing *Resource Types* in a similar structure:

```json
{
  "sys": { "id": "MockShop:Product" },
  "name": "Product",
  "defaultFieldMapping": {
    "title": "{ /title }",
    "subtitle": "Product ID: { /id }",
    "image": {
      "url": "{ /featuredImage/url }",
      "altText": "{ /featuredImage/altText }"
    }
  }
}
```

### Code walkthrough

The [example app](https://github.com/contentful/apps/tree/master/examples/native-external-references-mockshop) is using [Functions](/extensibility/app-framework/functions) to provide a connection between Contentful and the MockShop API. In the `functions/mockShop.ts` file we are defining 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)
* `graphql.resourcetype.mapping` - retrieval of resource type mappings, which determines what fields in Contentful map to an external type in Graph API.
* `graphql.query` - handles GraphQL queries for the external third-party API.

#### Search request

Search handler expects the following shape for outgoing requests:

```typescript
type ResourcesSearchRequest = {
  type: 'resources.search';
  resourceType: string;
  query?: string;
  locale?: string;
  referencingEntryId?: string;
  limit: number;
  pages?: {
    nextCursor: string;
  };
};
```

| Property             | Type                          | Description                                                                                                                                                                             |
| -------------------- | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`               | `resources.search` (required) | Identifier for the type of the event.                                                                                                                                                   |
| `resourceType`       | `string` (required)           | String consisting of the name of the provider and the resource within the provider, in a format `{Provider}:{Type}`, eg. `MockShop:Product`.                                            |
| `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.                                                                    |
| `locale`             | `string` (optional)           | The locale code that is relevant to the context of where the results will be shown in the web app. For example, the locale of the field where the **+ Add content** button was pressed. |
| `referencingEntryId` | `string` (optional)           | The ID of the entry that references the resource. For example, the entry currently open in the Entry Editor.                                                                            |
| `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.                                                                                  |

#### Lookup request

Lookup handler expects the following shape for outgoing requests:

```typescript
type Scalar = string | number | boolean;

type ResourcesLookupRequest<
  L extends Record<string, Scalar[]> = Record<string, Scalar[]>
> = {
  type: 'resources.lookup';
  resourceType: string;
  lookupBy: L;
  locale?: string;
  referencingEntryId?: string;
  limit?: number;
  pages?: {
    nextCursor: string;
  };
};
```

| Property             | Type                          | Description                                                                                                                                                        |
| -------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `type`               | `resources.lookup` (required) | Identifier for the type of the event.                                                                                                                              |
| `resourceType`       | `string` (required)           | String consisting of the name of the provider and the resource within the provider, in a format `{Provider}:{Type}`, eg. `MockShop:Product`.                       |
| `lookupBy`           | `object` (optional)           |                                                                                                                                                                    |
| `lookupBy.urns[]`    | `string[]` (required)         | List of IDs of entities to be fetched.                                                                                                                             |
| `locale`             | `string` (optional)           | The locale code that is relevant to the context of where the results will be shown in the web app. For example, the locale of the field where the resource linked. |
| `referencingEntryId` | `string` (optional)           | The ID of the entry that references the resource. For example, the entry currently open in the Entry Editor.                                                       |
| `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.                                                             |

This event is triggered to render preview cards in the Entry editor and when resolving Native External References in delivery. See [CDA documentation](/references/content-delivery-api/overview).

Differentiate the context by checking `context.originalRequest?.headers['contentful-api']`: `cda` for Content Delivery API, `cpa` for Content Preview API, or `undefined` for CMA/WebApp requests.

> **Info**
>
> **NOTE:** In delivery (CDA/CPA), `referencingEntryId` is not provided because there is no single `referencingEntryId` as lookups can batch and deduplicate URNs referenced by multiple entries.

#### Lookup and Search response

Both events return the same shape of the response:

```typescript
type Product = {
  urn: string;
  title: string;
  featuredImage?: {
    url: string;
    altText?: string;
  };
  badge: {
    variant: string;
    label: string;
  };
};

type ResourcesSearchResponse = {
  items: Product[];
  pages: {
    nextCursor?: string;
  };
};

type ResourcesLookupResponse = {
  items: Product[];
  pages: {
    nextCursor?: string;
  };
};
```

| Property           | Type                   | Description                                               |
| ------------------ | ---------------------- | --------------------------------------------------------- |
| `items`            | `Product[]` (required) | List of returned products.                                |
| `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 `Product`s. This transformation is carried out by the `withBadge` and `withUrn` functions located in the `function/utils.ts` file. In our [example](https://github.com/contentful/apps/tree/master/examples/native-external-references-mockshop), we are expecting Resource to conform to a particular shape:

```typescript
type Product = {
  urn: string;
  title: string;
  featuredImage?: {
    url: string;
    altText?: string;
  };
  badge: {
    variant: string;
    label: string;
  };
};
```

#### GraphQL query request

GraphQL Query handler expects the following shape for outgoing requests:

```typescript
type GraphQLQueryRequest = {
  type: 'graphql.query';
  query: string;
  isIntrospectionQuery: boolean;
  variables: Record<string, unknown>;
  operationName?: string;
};
```

| Property               | Type                       | Description                                                                                                                          |
| ---------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `type`                 | `graphql.query` (required) | Identifier for the type of the event.                                                                                                |
| `query`                | `string` (required)        | String consisting the graphql query to be passed to Contentful Function, which it turn passes it down to third party system.         |
| `isIntrospectionQuery` | `boolean` (required)       | boolean indicating whether the query is an introspection request (to retrieve the schema) or a normal GraphQL query (to fetch data). |
| `variables`            | `Object` (required)        | Variables required to run the graphql query to fetch the data from third party system.                                               |
| `operationName`        | `string`(optional)         | Operation name is an optional, descriptive identifier for graphql query.                                                             |

#### GraphQL query response

The GraphQL query response adheres to the standard GraphQL specification.

```typescript
/**
 * @see https://spec.graphql.org/October2021/#sec-Response
 */
type GraphQLQueryResponse = {
  data?: Record<string, any> | null;
  errors?: readonly Record<string, any>[];
  extensions?: Record<string, unknown>;
};
```

| **Value**    | **Description**                                                                                |
| ------------ | ---------------------------------------------------------------------------------------------- |
| `data`       | The data entry in the response will be the result of the execution of the requested operation. |
| `errors`     | The errors entry in the response is a non-empty list of errors, where each error is a map.     |
| `extensions` | GraphQL services may provide an additional entry to errors with key extensions.                |

#### GraphQL resourcetype mapping request

GraphQL resourcetype mapping handler expects the following shape for outgoing requests:

```typescript
type GraphQLResourceTypeMappingRequest = {
  type: 'graphql.resourcetype.mapping';
  resourceTypes: {
    resourceTypeId: string;
  }[];
};
```

| Property        | Type                                           | Description                                                                                            |
| --------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `type`          | `graphql.resourcetype.mapping` (required)      | Identifier for the type of the event.                                                                  |
| `resourceTypes` | `Array<{ resourceTypeId: string }>` (required) | Array of combination of external resource provider and resource types, for example `MockShop:Product`. |

#### GraphQL resourcetype mapping response

Here is the typing for `GraphQLResourceTypeMapping`:

```typescript
type GraphQLQueryArguments = Record<string, GraphQLQueryArguments | string>;

type GraphQLResourceTypeMapping = {
  resourceTypeId: string;
  graphQLOutputType: string;
  graphQLQueryField: string;
  graphQLQueryArguments: GraphQLQueryArguments;
};
```

`GraphQLResourceTypeMapping` would have the following values for the `product` field:

| **Value**                               | **Description**                                                                                                                                                                                              |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `resourceTypeId: 'MockShop:Product'`    | Resource Provider (a third-party system) combined with Resource Type (a specific type from the third-party system).                                                                                          |
| `graphQLQueryField: 'product'`          | Field name in the query object of the given GraphQL.schema.                                                                                                                                                  |
| `graphQLOutputType: 'Product'`          | The external GraphQL type from the third-party API.                                                                                                                                                          |
| `graphQLQueryArguments: { id: '/urn' }` | Arguments that match for the field. Supports nested objects (e.g., `{ where: { id: '/urn' } }`). `id` matches the argument for the product field. If left empty, an error for invalid arguments is returned. |

#### 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](https://datatracker.ietf.org/doc/html/rfc6901). 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) |
| `image`     | `object` (optional) |
| `image.url` | `string` (required) |

The definitions of `Product` **Resource Type** representations can be found in the `src/tools/entities/product.json` file, respectively.

### Create additional resource type

In our [example](https://github.com/contentful/apps/tree/master/examples/native-external-references-mockshop), we focus on a single *Resource Type*: `Product`, because the MockShop API provides only one type: `product`. If you want to include additional resource types, consider the following:

* Discover more entities available in the third party system.
* Create a new entity file in `src/tools/entities/`, similar to `src/entities/product.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 resources by running the creation script: `npm run create-resource-entities`.
* Build the function and update the bundled version stored in Contentful:

```bash
npm run build
npm run upload
```