> ## Documentation Index
> Fetch the complete documentation index at: https://dokumentation.websale.de/llms.txt
> Use this file to discover all available pages before exploring further.

# $wsExternalData - External data

> Load file-based external JSON data from the shop's own S3 bucket into templates, list entries, and handle errors – for CMS content, feeds, and third-party data.

With the `$wsExternalData` module, you load file-based external data (JSON) from the shop's own S3 into your templates and process it there. Typical use cases are additional information that does not come from the standard shop data, for example PDF lists for a product or content from WEBSALE components such as a [Strapi](/en/strapi-cms) instance.

This page covers read access to stored files. Populating the buckets (upload, Strapi export) is done via the [data interface](/en/schnittstellen/externe-datenschnittstelle-datei-bucket-basiert), not via this module.

***

## Basic concept

`$wsExternalData` has no variables of its own. Instead, you load data via a method into a [template variable](/en/frontend/referenz/variablen) of your own and then continue working with it. The flow is always the same:

1. **Load** - `load()` reads a file and returns its contents; you assign the result to a variable (e.g. `$data`).
2. **Check** - you check with `{{ if $data }}` whether the loading succeeded.
3. **Output** - only then do you iterate over or display the contents.

### Always check for success

External data may be missing or unreachable (wrong path, empty bucket, access problem). Therefore, always wrap the output with `{{ if $data }}` – otherwise empty placeholders or "dead" HTML structures arise. In the error case, [`getLastError()`](#wsexternaldata-getlasterror) returns a diagnostic message (for development only, not for the frontend).

### How the three methods work together

* [`load()`](#wsexternaldata-load) loads the contents of a file (map or list, depending on the JSON).
* [`read()`](#wsexternaldata-read) lists the files of a directory without loading their contents – useful to dynamically determine which files are present and then load them with `load()`.
* [`getLastError()`](#wsexternaldata-getlasterror) returns the last error message.

### Data sources (`source`)

Both loading methods know two sources: `user` (bucket `external-data`, default) for your own project-related files, and `system` (bucket `system`) for files provided by WEBSALE components (e.g. Strapi exports).

### Note on timing

`load()` and `read()` access S3 during page rendering. Each call is a network request. Therefore, load only what the page needs, instead of unnecessarily loading many files per page view.

***

## Module overview

**Example / excerpt of** `$wsExternalData`

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{= $wsExternalData | json }}
```

**JSON output**

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "load": "ƒ()",
  "read": "ƒ()",
  "getLastError": "ƒ()"
}
```

Note: `"ƒ()"` denotes a function.

**Methods overview**

| **Method**       | **Return type** | **Description**                                    |
| ---------------- | --------------- | -------------------------------------------------- |
| `load()`         | map \| list     | Loads the contents of a JSON file.                 |
| `read()`         | list            | Lists the files of a directory (without contents). |
| `getLastError()` | string          | Last error message from `load()` / `read()`.       |

***

## Templates

External data can be loaded into any template, depending on the use case for example on product detail pages (additional data), category pages (additional lists), or content pages.

***

## Variables

`$wsExternalData` does not provide any variables of its own. The loaded data is stored in the template variable to which you assign the result of `load()` (see [Basic concept](#basic-concept)).

***

## Methods

### \$wsExternalData.load()

Loads the contents of a file from the [external data interface](/en/schnittstellen/externe-datenschnittstelle-datei-bucket-basiert#2-datenablage-im-s3) and returns it, depending on the JSON structure, as a map (for an object) or a list (for an array), so that it can be output in the template (e.g. PDFs, additional attributes, CMS content).

**Signature**\
`$wsExternalData.load(file, options)`

**Return value**\
`map` | `list` – depending on the JSON structure, empty/null in the error case.

**Parameters**

| **Name**  | **Type** | **Required** | **Description**                                                        |
| --------- | -------- | ------------ | ---------------------------------------------------------------------- |
| `file`    | string   | yes          | Path to the file within the source (e.g. `products/product_123.json`). |
| `options` | map      | yes          | Format and source options (see table).                                 |

**Options (`options`)**

| **Key**    | **Type** | **Required** | **Default** | **Description**                                                               |
| ---------- | -------- | ------------ | ----------- | ----------------------------------------------------------------------------- |
| `type`     | string   | yes          | –           | File format. Currently allowed: `json`.                                       |
| `source`   | string   | no           | `user`      | Data source: `user` (bucket `external-data`) or `system` (bucket `system`).   |
| `maxDepth` | int      | no           | `7`         | Limits the read depth of nested JSON structures; deeper levels are truncated. |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $data = $wsExternalData.load("products/product_123.json", { type: "json" }) }}
{{ if $data }}
  {{= $data | json }}
{{ /if }}
```

### \$wsExternalData.read()

Lists all files of a directory of the external data interface (optionally including subfolders) and returns their paths as a list. The file content is not loaded; use `load()` for that afterwards. Helpful when you need to determine dynamically which files are present in a directory.

**Signature**\
`$wsExternalData.read(path, options)`

**Return value**\
`list` – paths of the files found.

**Parameters**

| **Name**  | **Type** | **Required** | **Description**                                          |
| --------- | -------- | ------------ | -------------------------------------------------------- |
| `path`    | string   | yes          | Directory path within the source (e.g. `json/Deutsch/`). |
| `options` | map      | no           | Source and filter options (see table).                   |

**Options (`options`)**

| **Key**     | **Type** | **Required** | **Default** | **Description**                                                                                         |
| ----------- | -------- | ------------ | ----------- | ------------------------------------------------------------------------------------------------------- |
| `source`    | string   | no           | `user`      | Data source: `user` or `system`.                                                                        |
| `recursive` | bool     | no           | `false`     | `true`: also search subfolders.                                                                         |
| `glob`      | bool     | no           | –           | Switches the pattern interpretation on/off. Expects a **boolean**, **not** a pattern string (see note). |

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $files = $wsExternalData.read("json/Deutsch/", { source: "system" }) }}
{{ if $files }}
  <ul>
    {{ foreach $file in $files }}
      <li>{{= $file }}</li>
    {{ /foreach }}
  </ul>
{{ /if }}
```

### \$wsExternalData.getLastError()

Returns the last error message as a string if an error occurred during `load()` or `read()` (e.g. file not present, access not possible). The message is intended for troubleshooting and should not be output to customers in the frontend.

**Signature**\
`$wsExternalData.getLastError()`

**Return value**\
`string` – error description, or empty/null if no error is present.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $error = $wsExternalData.getLastError() }}
{{ if $error }}
  <script>
    {{ autoescape "js" }}
      console.error("$wsExternalData Error: {{= $error }}");
    {{ /autoescape }}
  </script>
{{ /if }}
```

<Note>
  Output the error message only to the console (development), never visibly in the frontend. The `autoescape "js"` block ensures that special characters in the message do not break the surrounding script.
</Note>

***

## Actions

No actions are available for `$wsExternalData`.

***

## Examples

### Load and display product additional data (PDF list)

A common case: PDF links are maintained for a product via a JSON file. For product `137497`, the file under `products/137497.json` contains:

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "pdf": [
    { "link": "https://medienserver.example/faq.pdf", "name": "Frequently asked questions and answers" },
    { "link": "https://medienserver.example/anleitung.pdf", "name": "User manual" }
  ]
}
```

Load the file, check for success, and output the PDFs as a list:

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $data = $wsExternalData.load("products/137497.json", { type: "json" }) }}
{{ if $data and $data.pdf }}
  <ul>
    {{ foreach $doc in $data.pdf }}
      <li>
        <a href="{{= $doc.link }}" target="_blank" rel="noopener">{{= $doc.name }}</a>
      </li>
    {{ /foreach }}
  </ul>
{{ /if }}
```

**Result** \
A list of linked PDFs. But only if the file was loaded and contains a `pdf` array.

### Load CMS/Strapi content from the `system` source

From the `system` bucket, you typically load Strapi exports. In `Content`, Strapi mixes different block types. Each block carries a `__component` field, which you can use to filter selectively.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $cms = $wsExternalData.load("json/Deutsch/ws_start.json", { source: "system", type: "json", maxDepth: 20 }) }}
{{ if $cms }}
  {{ foreach $item in $cms.attributes.Content }}
    {{ if $item.__component == "elemente.categories" }}
      {{ include "components/cms/categories.htm" with $cContentItem = $item }}
    {{ /if }}
  {{ /foreach }}
{{ /if }}
```

A block in the JSON file looks like this, for example:

```json theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{
  "id": 1,
  "__component": "elemente.categories",
  "CategoryIDs": "10-04418,2-06719,3-82749",
  "ClassList": "row mb-3"
}
```

**Result** \
Only blocks of type `elemente.categories` are rendered via the matching component. Other block types are skipped.

### List the files of a directory

With `read()`, you determine which files are present without loading their contents. You then load the contents specifically with `load()`.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $files = $wsExternalData.read("json/Deutsch/", { source: "system" }) }}
{{ if $files }}
  {{ foreach $file in $files }}
    {{ var $data = $wsExternalData.load($file, { source: "system", type: "json" }) }}
    {{ if $data }}
      <!-- continue processing $data -->
    {{ /if }}
  {{ /foreach }}
{{ /if }}
```

**Result** \
All matching files are found and loaded one by one.

### Loading with error handling

Output only on success; otherwise write the error message to the console.

```html theme={"theme":{"light":"github-light","dark":"github-dark"},"languages":{"custom":["/languages/websale.json"]}}
{{ var $data = $wsExternalData.load("products/product_123.json", { type: "json" }) }}
{{ if $data }}
  {{= $data | json }}
{{ else }}
  {{ var $error = $wsExternalData.getLastError() }}
  {{ if $error }}
    <script>
      {{ autoescape "js" }}
        console.error("$wsExternalData Error: {{= $error }}");
      {{ /autoescape }}
    </script>
  {{ /if }}
{{ /if }}
```

**Result** \
On success, the data appears; otherwise the cause of the error ends up in the browser console.

<Note>
  If you build a path from several components, you can join an array into a string with `| join` before passing it to `load()` / `read()`. For a fixed path, the string by itself is sufficient (as in the examples above).
</Note>

***

## Related links

* [External data interface (file-/bucket-based)](/en/schnittstellen/externe-datenschnittstelle-datei-bucket-basiert) – defines which buckets/sources exist and how files are stored.
* [join function](/en/frontend/referenz/funktionen#join) – joins an array into a string (for composed paths).
