---
title: Sourcing Data via App Scripts | Shopware Community Hub
description: >-
  Learn how to use App Scripts to load data from the database and attach it to
  storefront templates.
canonical_url: 'https://hub.shopware.com/learn/unit/app-sourcing-data-via-app-scripts'
---

# Sourcing Data via App Scripts

<LearningObjectives>

- **Understand** what **hooks** are and why they matter in **App Scripts**.
- **Attach** data to the **`page` object** in an App Script.
- **Use** built-in services to **load data** from the Shopware database.

</LearningObjectives>

# Sourcing Data via App Scripts

We are close to displaying our custom Physical Shop data on the storefront. But there is still one missing piece: **How do we make this data available in Twig templates?**

This is where [App Scripts](https://developer.shopware.com/docs/guides/plugins/apps/app-scripts/) come in. They allow you to hook into Shopware events and load data in the storefront.

## Code-Along

To follow along, use the following command within the ShopwareStoreTracker directory:

```bash
git checkout tags/app_scripting--start
```

## What are App Scripts?

App Scripts are Twig files that run in response to [hooks](https://www.techopedia.com/definition/programming-hooks), or in other words, **events** that are triggered by Shopware core logic. They don't directly change the storefront template. Instead, they let you **attach or modify data** that templates can later render.

Choosing the right hook is crucial. It defines **when** and **where** your script runs. For our StoreTracker App, we want to attach data when a common storefront page is loaded.

<Callout title="Which Hook Should I Use?" type="info">

Two good examples of events are:

- [product-page-loaded](https://developer.shopware.com/docs/resources/references/app-reference/script-reference/script-hooks-reference.html#product-page-loaded)
- [landing-page-loaded](https://developer.shopware.com/docs/resources/references/app-reference/script-reference/script-hooks-reference.html#landing-page-loaded)

</Callout>

## Structuring Your Scripts

App Scripts are located at `[app_root]/Resources/scripts`. Each script targets **exactly one hook**, so you need a separate file for every event you want to react to.

<Callout title="Differences to Plugin Event Listeners" type="info">

In a Shopware plugin, one PHP event listener can subscribe to multiple events via the [`getSubscribedEvents()`](https://github.com/symfony/symfony/blob/7.3/src/Symfony/Component/EventDispatcher/EventSubscriberInterface.php). With App Scripts, each file is bound to **one specific hook only**.

</Callout>

Currently, our `Resources/` folder should look like this:

```txt
Resources/
├── app
│ └── storefront
│     └── dist
│         └── assets
│             └── kudos-regular.svg ## optional
├── entities.xml
├── theme.json
└── views
    └── storefront
        └── layout
            └── footer
                └── footer.html.twig
```

To add App Scripts, extend the structure like this:

```txt
Resources/
├── app
│ └── storefront
│     └── dist
│         └── assets
│             └── kudos-regular.svg
├── entities.xml
├── scripts     // <--- This is the directory for App Scripts!
│ └── footer-pagelet-loaded // <--- This folder has the same name as the hook
│     └── physical-shops.twig
├── theme.json
└── views
    └── storefront
        └── layout
            └── footer
                └── footer.html.twig
```

Here we added a `scripts` folder with a subdirectory matching the hook name (`footer-pagelet-loaded`). Although the files have a `.twig` ending, they are **App Scripts**, not storefront templates.

To sum up:

- Twig files in `[app_root]/Resources/scripts` are **App Scripts** (run on hooks, manipulate data).
- Twig files in `[app_root]/Resources/views` are **storefront templates** files (render the storefront).

## The Anatomy of the App Script

App Scripts are [Twig template](https://twig.symfony.com/doc/3.x/templates.html) files which run in a [sandboxed execution environment](https://developer.shopware.com/docs/guides/plugins/apps/app-scripts/#scripts) for security. They don't directly change the storefront layout, but they **manipulate the data** passed into templates.

Example:

```twig
{% set page = hook.page %}

{# Data-loading service (you will use this in a later learning unit) #}
{# {% set shops = services.repository.search('ce_physical_shop', {}) %} #}

{# Mock data (for now) #}
{% set shops = [
  { name: 'Berlin Store', country: 'Germany'},
  { name: 'London Store', country: 'UK'} 
] %}

{% do page.addArrayExtension('physicalShops', shops) %}
```

**Explanation:**

- The **first line** extracts the page object from the hook variable passed into the script.
- The **shops variable** currently contains mock data. Later you will replace the mock with a real database using **[data-loading service](https://developer.shopware.com/docs/resources/references/app-reference/script-reference/data-loading-script-services-reference.html)**.
- The **last line** adds data to the page object via the `addArrayExtension()` function.

<Callout title="Where Does the Data Come From?" type="info">

In this learning unit, we use **mock data** for simplicity and clarity. Later in the course, you will learn how to **fetch real records from a Custom Entity** using `services.repository.search()`:

- [Sending Data to Custom Entity via the Admin API](/learn/unit/app-uploading-custom-entity-data).
- [Inserting Entity Data via Hooks](/learn/unit/entity-data-via-hooks).

</Callout>

### Validating the Effects of Your App Script

Now that the App Script is in place, let's confirm that it actually provides data to the storefront.

First, open your `footer.html.twig` override file. Then add Twig's built-in [dump()](https://twig.symfony.com/doc/3.x/functions/dump.html) function inside the block.

```twig
{% sw_extends '@Storefront/storefront/layout/footer/footer.html.twig' %}

{% block layout_footer_service_menu_content %}
  {{ parent() }}
  
  <h1>Check me out I'm custom</h1>
  {{ dump(footer) }}
{% endblock %}
```

<Callout title="Reminder: Using dump()" type="info">

The `dump()` function prints variables directly on the page.

- `dump()` alone shows **all variables available on the current page**.
- `dump(variable_name)` shows only the variable you pass in. For example, `dump(footer)` shows only the `footer` object.

Very handy for debugging!

</Callout>

After incrementing the App version and running `bin/console app:refresh` once again, you should see the Physical Shop data visible in the `footer` object, under `extensions`:

![Twig dump of Physical Shop Data](assets/physical-shops-dump.jpeg)

Time to output some data!

## Rendering Some Physical Shop Data to the Page

Now that the data is available in the `footer` object, let's render it in the storefront. First, open your `footer.html.twig` override file. Then, inside the `layout_footer_bottom` block loop over the `physicalShops` extension and output its data:

```twig
{% sw_extends '@Storefront/storefront/layout/footer/footer.html.twig' %}

{% block layout_footer_bottom %}
    {{ parent() }}

    <h1>Check me out I'm custom!</h1>

    {% if footer.hasExtension('physicalShops') %}
        <ul class="physical-shop__list" style="display:grid; gap: 10px">
            {% for shop in footer.getExtension('physicalShops') %}
                <li class="physical_shop__item" style="list-style:none">
                    <strong class="physical-shop__item title">{{ shop.name }}</strong>
                    <ul class="physical-shop__item address">
                        {% for name, value in shop.streetAddress %}
                            <ul class="physical-shop__street-address">
                                {% if name and value %}
                                    <strong>{{ name }}</strong>
                                    {{value}}
                                {% endif %}
                            </ul>
                        {% endfor %}
                    </ul>
                    <p class="physical-shop__item country"><strong>Country:</strong>{{ shop.country }}</p>
                    <p class="physical-shop__item email"><strong>Email:</strong>{{ shop.email }}</p>
                </li>
            {% endfor %}
        </ul>
    {% endif %}
{% endblock %}
```

Save the file, increment your App version and run `bin/console app:refresh`. Reload the storefront, and you should now see your Physical Shop data displayed in the footer.

![App Script data is loaded to the page](assets/footer-shop-info-output.jpeg)

### Stability and Versioning of App Script Data

In this learning path, we use simplified keys (extension names) for demonstration purposes. However, in a real-world scenario, it is important to plan for long-term stability and compatibility.

As your App evolves over time, you may want to change how data is exposed through App Scripts, for example, renaming an extension key like `physicalShops` to `physicalStores`. If your App is **used by a theme that relies on that data**, the theme may still use the old key and therefore break after an update.

To avoid this, follow these best practices:

1. Take your time to create extension names that are **clear**, **stable**, and **future-proof** for your project.
2. Keep existing extension keys **unchanged** whenever possible.
3. If needed, introduce extension names (keys) with a **version suffix** (e.g., `physicalShops_v2`) to avoid breaking changes.
4. Document changes in a short changelog (CHANGELOG.md) and communicate deprecations clearly.
5. In themes, use **defensive checks** such as `hasExtension()` and `getExtension()` and provide fallbacks. This prevents errors if an App Script's data is missing or renamed.

**Example:**

```twig
{% if footer.hasExtension('physicalShops') %}
  {% set shops = footer.getExtension('physicalShops') %}
{% elseif footer.hasExtension('physicalStores') %}
  {% set shops = footer.getExtension('physicalStores') %}
{% elseif footer.hasExtension('physicalShops_v2') %}
  {% set shops = footer.getExtension('physicalShops_v2') %}
{% else %} 
  {# Fallback #}
  {% set shops = [] %}
{% endif %}
```

<Callout title="Note" type="info">

While not a formal design pattern, using **versioned naming** (e.g., `_v2`) is a widely adopted common software practice to keep integrations stable over time and prevent breaking changes.

</Callout>

## Code-Along (end)

To see what the final result of this learning unit should look like, run the following command:

```bash
git checkout tags/app_scripting--end
```

<Callout title="Removing Working Changes" type="warning">

If you have local changes, you will need to run `git reset --hard HEAD` in the App directory. Be mindful that this command will destroy any local changes!

</Callout>

## Summary

In this learning unit, you learned how to:

- Use App Scripts to react to Shopware hooks
- Load custom data from the database with built-in services
- Attach this data to the **`page`** object and render it in the storefront

With this, you now have a complete workflow: Creating a custom entity, sourcing its data and displaying it in the storefront.

App Scripts are a versatile, event-driven system with many more use cases, such as [data-loading](https://developer.shopware.com/docs/guides/plugins/apps/app-scripts/data-loading.html) or even [custom API endpoints](https://developer.shopware.com/docs/guides/plugins/apps/app-scripts/custom-endpoints.html).
