# Resources

So far our example custom coded components and actions had very simple property types: `select`, `boolean`, `string`, etc. But what if your property is a data object like a product, collection or a CMS entry? At Shopstory we call these objects "resources" and you can create as many resource types as you need. They can be connected to any data source (e-commerce platform, CMS, etc) and you're in full control over how data is fetched by your front-end code.

## Custom component with resource property

Let's build a simple `ProductCard` component that takes 2 properties:

1. `product` -> the object with product data like `title`, `description`, `price`, etc. fetched from a mock e-commerce platform service. This property is our **resource**.
2. `hasOverlay` -> simple `boolean` prop controlling appearance

The code of our `ProductCard` looks like this:

```typescript
import React from "react";
import {Product} from "../../products/MockProductsService";
import Image from 'next/image'

export const ProductCard : React.FC<{ product: Product, hasOverlay: boolean }> = ({ product, hasOverlay }) => {
  return <div style={{position: "relative"}}>
    <div style={{position: "relative"}}>
      <div style={{paddingBottom: "66.666%", background: hasOverlay ? "#f3f3f3" : "none"}}>
        <Image src={product.image} alt={product.title} layout={"fill"} style={{position: "absolute", top: 0, left: 0, bottom: 0, right: 0}}/>
      </div>
    </div>
    <div style={{paddingBottom: 6, paddingTop: 20, textAlign: "center", fontSize: 15}}>
      { product.title }
    </div>
    <div style={{fontWeight: 600, textAlign: "center", fontSize: 15}}>
      { product.price }$
    </div>
  </div>
}
```

## Schema - `resource` field

In order to register `ProductCard` in Shopstory and take into account `product` property we must use `resource` field type:

```typescript
export const shopstoryConfig: Config = {
    // ...
  components: [
    {
      id: "ProductCard",
      label: "Product Card",
      type: "card",
      schema: [
        {
          prop: "hasOverlay",
          label: "Has overlay",
          type: "boolean"
        },
        {
          prop: "product",
          label: "Product",
          type: "resource", // resource field
          resourceType: "product" // resource type
        }
      ]
    }
  ],
}
```

Each `resource` field must always define `resourceType` which in our case is `product`.&#x20;

At this point Shopstory has no knowledge of the resource called `product`. When you open Shopstory and add `ProductCard` the editor will crash saying that `product` widget is not defined.

## Registering resource type

Each resource type in Shopstory consists of 2 parts:

* widget
* fetch function

### Widget

Widget defines how resources are browsed and picked by the user in the Shopstory editor. Widget code is run only inside the Shopstory editor and is not involved when you render Shopstory content in your website.&#x20;

Let's create a widget for our `product` resource type. We do this via `resourceTypes` property in the Shopstory configuration object:

```typescript
export const shopstoryConfig: Config = {
    // ...
  resourceTypes: {
    product: {
      widget: {
        type: "item-picker",
        getItems: async (query) => {
          const products = await MockProductsService.searchProducts(query);
          return products.map(product => ({
            id: product.id,
            title: product.title,
            thumbnail: product.image
          }))
        },
        getItemById: async (id) => {
          const product = await MockProductsService.getProductById(id);
          if (!product) {
            throw new Error("can't find product");
          }

          return {
            id: product.id,
            title: product.title,
            thumbnail: product.image
          }
        }
      }
    }
  },
}
```

In this code we tell Shopstory that `product` should use a built-in `item-picker` widget. Item picker is a simple single-select widget that requires 2 functions to work properly:

* `getItems` - searches for items by query
* `getItemById` - standard find-by-id function

After the widget is defined you can add `ProductCard` to the Shopstory canvas and use item picker to pick a product:

{% embed url="<https://player.vimeo.com/video/779589164?h=43686a1a87>" %}

As you can see the widget works properly but after selecting the product, the component still shows a placeholder. It's because we didn't define a second important part of the resource type: **a fetch function.**

### Fetch function

When user picks a resource the only data saved in Shopstory data is resource identifier. In our example it's the product `id` - the `id` property returned by `getItems` and `getItemById` functions. **Shopstory doesn't keep any data copies (like product title, description, price, etc) inside of its content so there is no problem with synchronising the data.** It also means that if you want to render some content, Shopstory must first first fetch all the resources used by that content. How data is fetched is controlled by so called "fetch functions".

In our example we haven't defined a fetch function for our `product` so it's no surprise that the `ProductCard` is not rendered. Unsuccessful fetch will result in the field label turning red:

<figure><img src="https://1906827321-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4j4BoYKV0GXMySXRws9n%2Fuploads%2FXWM8uMMTnM7uyqkyyUit%2FScreenshot%202022-12-06%20at%2015.40.43.png?alt=media&#x26;token=9a158cc0-f3c8-4f68-a78d-6a8bad8f7e63" alt=""><figcaption></figcaption></figure>

If you look at the console you'll see following error message:

<figure><img src="https://1906827321-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F4j4BoYKV0GXMySXRws9n%2Fuploads%2Fs4J4tqkpCwB618xDNua1%2FScreenshot%202022-12-05%20at%2014.35.12.png?alt=media&#x26;token=7d24592a-f6f6-4186-9e6d-c9fb91e9c34b" alt=""><figcaption></figcaption></figure>

To solve this we must add a fetch function to our `product` resource type:

```typescript
export const shopstoryConfig: Config = {
    // ...
  resourceTypes: {
    product: {
      fetch: async (resources) => {
        const ids = resources.map(resource => resource.id);
        const products = await MockProductsService.getProductsByIds(ids);

        return resources.map(resource => ({
          ...resource,
          value: products.find(product => product.id === resource.id)
        }))
      },
      widget: {
        // ...widget definition
      }
    }
  },
}
```

The first parameter of `fetch` function is an array of resources that should be fetched. Each resource of this array has a following structure:

```
{
    id: string;
    type: string;
    fetchParams?: ResourceParams;
    info?: ResourceInfo;
}
```

`id` is the resource unique identifier. `type` represents resource type, in this case it's always gonna have value `"product"`. In our example `fetchParams` and `info` are not defined, we'll discuss them later.

The result of the fetch function should be an array of fetched resources. Fetched resource is represented by the same object as input resource with one extra property - either `value` (successfully fetched resource data) or `error`.

For example for the following input:

```
[ 
    { id: "xxx", type: "product" },
    { id: "yyy", type: "product" },
    { id: "zzz", type: "product" }
]
```

The output could look like this:

```
[
    {
        id: "xxx",
        type: "product",
        value: {
            title: "Super xxx product",
            description: "Xxx is awesome.",
            price: "19.99",
            image: { /* image object */ }
        }
    },
    {
        id: "yyy",
        type: "product",
        value: {
            title: "Super yyy product",
            description: "Yyy is even more awesome.",
            price: "229.99",
            image: { /* image object */ }
        }
    },
    {
        id: "zzz",
        type: "product",
        error: /* error */
    }
]
```

The products `xxx` and `yyy` were fetched successfully and in case of `zzz` there was some fetching error that you intercepted and provided info about it to Shopstory.

Let's see the end result after adding the fetch function:

{% embed url="<https://player.vimeo.com/video/779590667?h=6a1ec3e77a>" %}

#### Errors in fetch function

If for some reason your `fetch` function throws an error then all resources will be treated as if they had an `error` property set.

#### Optional resources

Sometimes the resource are not required to render the component. In such case you can set the flag `optional` to `true`:

```typescript
  {
    prop: "product",
    label: "Product",
    type: "resource",
    resourceType: "product",
    optional: true
  }
```

If the resource is not picked or fetching the resource resulted in error, the `product` prop for your custom component will be `undefined`.

## Built-in resource types (connecting CMS data)

Shopstory plugins can register their own resource types. The main application of this feature is connecting CMS data (entries, media, etc) to custom components or actions.

For example, imagine you manage all your website URLs in the CMS (a common pattern when SEO is critical). You've created a content type (or document type) named `UrlRoute` and each website URL is represented by the entry of this type. In this scenario, when editor adds a link action to a button in Shopstory, you obviously want to connect this link to some entry of `UrlRoute` instead of hardcoding link in a text field. You can achieve such effect with built-in resource types.

Each Shopstory plugin can register its own resource types. CMS plugins, like `contentfulPlugin` or `sanityPlugin` always add built-in resource types for connecting to CMS-specific data.

Contentful registers 2 resource types:

* `contentful-entry` (for connecting to entries)
* `contentful-asset` (assets)

Sanity registers 3 resource types:

* `sanity.document` (connects to other documents)
* `sanity.image` (connects to images)
* `sanity.file` (connects to files).

Each resource types can have its parameters. For example you can easily narrow down `contentful-entry` or `sanity.document` resource types to specific content types / document types. You can find in-depth specification of CMS-specific resource types with their parameters in [CMS Guides](https://docs.shopstory.app/cms-guides).

Our custom link example would look like this in Contentful (`transform` function will be described later in this chapter):

```typescript
{
      id: 'MyLink',
      label: 'URL Route',
      schema: [
        {
          params: {
            contentTypeId: 'UrlRoute' // we narrow down entries to UrlRoute content type
          },
          prop: 'link',
          resourceType: 'contentful-entry',
          transform: (link: any) => {
            return {
              path: link.fields.path
            };
          },
          type: 'resource'
        }
      ]
    },
```

### Default fetch function

Built-in resource types can come with a default fetch function. It means that if you don't define your own fetch function they'll be fetched in a default way. The default fetch behaviour depends on the CMS, you learn how it works in [CMS Guides](https://docs.shopstory.app/cms-guides).

#### `transform` property

If you want to keep the default fetch but make some transformation of the result data, you can do it using `transform` property. In the above-mentioned custom link example, only the `{ path: link.fields.path }` will be sent to the link component.

#### Custom fetch

In many cases you will want to completely ignore default `fetch` behaviour and override it with your own data fetching. Built-in resource types are no different than custom resource types and you can easily do it with providing your own `fetch` function.

In the example below we override how `sanity.document` data is fetched:

```typescript
export const shopstoryConfig: Config = {
  // ...
  resourceTypes: {
    // ...
    "sanity.document": {
      fetch: async (resources) => {
        return await fetchSanityDocumentResources(resources); // custom fetch function
      },
    },
  }
};
```

Your custom `fetch` function doesn't have to fetch **all** the resources from `resources` input array. The resource is considered fetched when its `value` or `error` properties are set. For resources where `value` and `error` stay `undefined` the custom fetcher will be applied after custom `fetch` finished running.
