Entwicklung 19. April 2026 12 min Lesezeit

Liquid FetchXML vs. Web API in Power Pages: Wann was?

Liquid FetchXML oder Web API in Power Pages? Entscheidungsbaum, Performance-Vergleich, Security und Hybrid-Pattern aus der Praxis.

Beide existieren, beide funktionieren — und trotzdem taucht in fast jedem Power Pages Projekt irgendwann die gleiche Frage auf: Liquid FetchXML oder Web API? Wer die Antwort mit "kommt drauf an" abtut, hat recht, hilft aber niemandem. Dieser Artikel gibt dir einen konkreten Entscheidungsbaum, erklärt die technischen Unterschiede und zeigt, wann der Hybrid-Ansatz die sauberste Lösung ist.

Liquid FetchXML: Server-Side Rendering

Liquid FetchXML ist der ältere der beiden Ansätze — und oft der bessere. Der Mechanismus ist einfach: Beim Rendern der Seite führt Power Pages den FetchXML-Query serverseitig aus und schreibt das Ergebnis direkt in den HTML-Output. Wenn der Browser die Seite empfängt, sind die Daten bereits im DOM.

Liquid FetchXML — Grundmuster
{% fetchxml accounts_query %}
<fetch top="50">
  <entity name="account">
    <attribute name="name" />
    <attribute name="accountid" />
    <filter>
      <condition attribute="statecode" operator="eq" value="0" />
    </filter>
    <link-entity name="contact" from="accountid"
                 to="accountid" link-type="inner">
      <filter>
        <condition attribute="contactid" operator="eq"
                   value="{{ user.id }}" />
      </filter>
    </link-entity>
  </entity>
</fetch>
{% endfetchxml %}

{% for account in accounts_query.results.entities %}
  <div>{{ account.name }}</div>
{% endfor %}

Vorteile

Kein CORS, kein CSRF

Da alles serverseitig passiert, gibt es keine Cross-Origin-Probleme und du brauchst keinen Anti-Forgery-Token. Der Request läuft nicht vom Browser, sondern vom Portal-Server selbst.

N:M-Beziehungen kein Problem

Beide nutzen dasselbe Table Permissions Modell. Der Unterschied: FetchXML traversiert N:M-Beziehungen direkt im Query per link-entity — der Scope der Permission spielt dabei keine Rolle. Die Web API übersetzt Scope-Bedingungen in OData-Filter, was bei N:M-Traversal mit Parental-, Contact- oder Account-Scope zu CDS-Fehlern führt (Workaround: FetchXML-Parameter im OData-Query).

Schnellerer Initial Load

Daten kommen mit dem ersten HTML-Response. Keine Extra-Requests, kein Flackern, kein Spinner. Time to Interactive ist messbar niedriger — besonders bei großen Datensätzen.

SEO-freundlich

Suchmaschinen-Crawler sehen die Daten direkt im HTML. Was über JavaScript nachgeladen wird, kann von Crawlern ignoriert werden — besonders kritisch für Produktseiten oder öffentliche Verzeichnisse.

Nachteile

Kein dynamisches Nachladen

Was beim Page Load nicht da ist, bleibt nicht da — ohne einen vollständigen Page Reload.

Nur Lesen — kein Create, Update, Delete

Mit Liquid kannst du Daten lesen (auch über komplexe Beziehungen), aber nicht schreiben, updaten oder löschen. Für Mutationen brauchst du immer die Web API.

Template-Komplexität

Verschachtelte Beziehungen, bedingte Logik und Transformationen werden in Liquid schnell unübersichtlich.

Web API: Client-Side Data Access

Die Power Pages Web API ist ein OData-kompatibler REST-Endpunkt unter /_api/. Anfragen laufen vom Browser aus — nach dem Page Load, ausgelöst durch User-Interaktionen oder Timer. Das macht sie zur richtigen Wahl für alles, was dynamisch ist.

Web API — GET mit safeAjax
webapi.safeAjax({
    type: "GET",
    url: "/_api/accounts?$select=name,accountid&$filter=statecode eq 0",
    contentType: "application/json"
}).done(function(data) {
    data.value.forEach(function(account) {
        console.log(account.name);
    });
}).fail(function(jqXHR) {
    console.error("Fehler:", jqXHR.status);
});

Wichtig: webapi.safeAjax() ist die Power Pages eigene Wrapper-Funktion. Sie setzt automatisch den Anti-Forgery-Token (__RequestVerificationToken), der für alle schreibenden Operationen (POST, PATCH, DELETE) verpflichtend ist. Direkte $.ajax()-Aufrufe ohne diesen Token scheitern mit 403.

Vorteile

Vollständige CRUD-Unterstützung

GET, POST, PATCH, DELETE — alles ist möglich. Dazu Associate und Disassociate für N:M-Beziehungen über den $ref-Endpunkt.

Dynamisch ohne Reload

Inhalte aktualisieren sich, Filter greifen sofort, Formulare speichern ohne Seitenwechsel. Moderne UX-Patterns wie Infinite Scroll oder Live-Updates sind nur so umsetzbar.

Bekannte Technologie

OData ist ein etablierter Standard. Entwickler mit Dataverse-Erfahrung kennen die Query-Syntax. Debugging im Browser-DevTools ist einfacher als Liquid-Fehlersuche.

Selektive Datenlast

Du kannst gezielt nur die Daten nachladen, die gerade gebraucht werden — statt alles beim Page Load zu rendern.

Nachteile

Table Permissions + Known Issue bei N:M

Table Permissions und Site Setting (Webapi/<entity>/enabled = true) sind zwingend. Bei N:M-Lesezugriffen mit Parental-, Contact- oder Account-Scope gibt es ein bekanntes CDS-Fehlerproblem — Workaround: FetchXML-Parameter im OData-Query oder disableodatafilter = true.

n+1 Request-Problem

Jeder API-Call ist ein eigener HTTP-Request. Bei vielen Datenpunkten beim Page Load summiert sich das zu merklicher Latenz — und der User sieht Spinner statt Inhalte.

Der Entscheidungsbaum

Vier Fragen reichen in der Praxis aus, um die richtige Methode zu wählen. Beantworte sie der Reihe nach:

flowchart TD Start([Dataverse-Daten in Power Pages]) --> Q1{Beim Page Load\nbenötigt?} Q1 -->|Ja| Q2{N:M-Beziehung\nlesen?} Q1 -->|Nein – erst nach\nUser-Interaktion| Q3{CRUD-Operation?} Q2 -->|Ja| LIQUID1["Liquid FetchXML empfohlen\n(Web API möglich, aber\nFetchXML-Parameter nötig)"] Q2 -->|Nein| Q4{SEO-relevant?} Q4 -->|Ja| LIQUID2[Liquid FetchXML] Q4 -->|Nein| BOTH["Beides möglich –\nverwende Liquid für\neinfacheren Code"] Q3 -->|Create / Update /\nDelete| WEBAPI1[Web API] Q3 -->|N:M Associate /\nDisassociate| WEBAPI2["Web API\n$ref-Endpunkt"] Q3 -->|Nur Lesen| LIQUID3[Liquid FetchXML] style LIQUID1 fill:#dcfce7,stroke:#16a34a,color:#15803d style LIQUID2 fill:#dcfce7,stroke:#16a34a,color:#15803d style LIQUID3 fill:#dcfce7,stroke:#16a34a,color:#15803d style BOTH fill:#fef9c3,stroke:#ca8a04,color:#92400e style WEBAPI1 fill:#dbeafe,stroke:#2563eb,color:#1d4ed8 style WEBAPI2 fill:#dbeafe,stroke:#2563eb,color:#1d4ed8

Sonderfall N:M lesen: Die Web API hat laut offizieller Microsoft-Dokumentation ein bekanntes Problem: GET-Requests auf Tabellen mit N:M-Table Permissions schlagen fehl, wenn der Scope Parental, Contact oder Account ist — der Portal-Server erzeugt einen CDS-Fehler. Microsofts empfohlene Lösung ist, FetchXML als Parameter im OData-Query zu verwenden (/_api/entity?fetchXml=...) statt OData-Filterung. Als Alternative existiert das Site Setting Webapi/<table>/disableodatafilter = true, das jedoch Performance-Einbußen mit sich bringt. Liquid FetchXML umgeht dieses Problem vollständig — kein Workaround nötig.

Der Hybrid-Ansatz: Das Beste aus beiden Welten

In der Praxis sind die meisten anspruchsvollen Power Pages Seiten Hybride. Das Muster ist simpel und bewährt: Liquid lädt Daten beim Page Load in eine JavaScript-Variable — die Web API übernimmt nur Mutationen.

Ein konkretes Beispiel: Eine Seite zeigt alle Produkte, die einem Incident zugeordnet sind (N:M). Der Nutzer kann Produkte hinzufügen und entfernen. Ohne Hybrid-Ansatz würde man bei jedem Klick einen Full-Reload auslösen oder kämpft mit Global Permissions. Mit dem Hybrid-Pattern geht es sauber:

Schritt 1 — Liquid preloaded alle Produkte (serverseitig)
{% fetchxml products_query %}
<fetch>
  <entity name="product">
    <attribute name="name" />
    <attribute name="productid" />
    <attribute name="productnumber" />
    <filter>
      <condition attribute="statecode" operator="eq" value="0" />
    </filter>
  </entity>
</fetch>
{% endfetchxml %}

<script>
// Alle verfügbaren Produkte – ein Request, kein Spinner
var AVAILABLE_PRODUCTS = [
{% for p in products_query.results.entities %}
  { id: "{{ p.productid }}", name: "{{ p.name | escape }}", number: "{{ p.productnumber | escape }}" }{% unless forloop.last %},{% endunless %}
{% endfor %}
];
</script>
Schritt 2 — Web API nur für Associate / Disassociate (clientseitig)
// Produkt einem Incident zuordnen (N:M Associate)
function associateProduct(incidentId, productId) {
    return webapi.safeAjax({
        type: "POST",
        url: "/_api/incidents(" + incidentId + ")/product_incidents/$ref",
        contentType: "application/json",
        data: JSON.stringify({
            "@odata.id": "/_api/products(" + productId + ")"
        })
    });
}

// Zuordnung entfernen (N:M Disassociate)
function disassociateProduct(incidentId, productId) {
    return webapi.safeAjax({
        type: "DELETE",
        url: "/_api/incidents(" + incidentId + ")/product_incidents(" + productId + ")/$ref"
    });
}

// JavaScript filtert die preloaded Daten — kein Extra-Request
function renderFilteredProducts(searchTerm) {
    return AVAILABLE_PRODUCTS.filter(function(p) {
        return p.name.toLowerCase().includes(searchTerm.toLowerCase());
    });
}

Das Ergebnis: Der initiale Page Load ist schnell (ein einziger Server-Request via Liquid), clientseitiges Filtern und Suchen braucht kein Netzwerk, und nur die tatsächlichen Änderungen (Associate/Disassociate) gehen über die Web API. Table Permissions werden nur für die Mutation konfiguriert — nicht für das Lesen der N:M-Daten.

Performance im Vergleich

Die Performance-Implikationen sind direkter als die meisten Entwickler erwarten. Die entscheidende Metrik ist Time to Interactive — wann kann der Nutzer tatsächlich mit der Seite arbeiten?

Szenario Liquid FetchXML Web API
100 Datensätze beim Page Load 1 Request (HTML) 2 Requests (HTML + API)
Daten sichtbar ab Sofort (im HTML) Nach API-Response
Clientseitiges Filtern/Suchen Sofort (kein Netzwerk) Extra-Request je Suche
Daten nach User-Aktion updaten Page Reload nötig Kein Reload
Große Datenmenge (1.000+ Zeilen) Langsamer Page Load Pagination möglich

Faustregel: Bis ca. 200 Datensätze ist Liquid beim initialen Load klar im Vorteil. Darüber hinaus oder wenn Pagination gefragt ist, lohnt sich die Web API — idealerweise kombiniert mit einem Liquid-Preload der ersten Seite.

Security im Vergleich

Beide Ansätze — Liquid FetchXML und Web API — nutzen dasselbe Table Permissions Modell. Es gibt kein separates Permission-System je nach Methode. Der Unterschied liegt darin, wie die Permission-Prüfung technisch umgesetzt wird und welche Konsequenzen das für bestimmte Szenarien hat.

Liquid FetchXML

Table Permissions — serverseitige Query-Filterung

  • + Permission-Check: Darf der Nutzer die Zieltabelle lesen?
  • + Beziehungsfilterung übernimmt FetchXML selbst — Scope-Grenzen greifen bei N:M-Traversal nicht ein
  • + Alle Scopes (Global, Account, Contact, Self, Parental) funktionieren ohne Workaround
  • ~ Konfiguration in Power Pages Studio unter "Sicherheit"

Web API

Table Permissions — als OData-Filter übersetzt

  • + CSRF-Token-Schutz automatisch via safeAjax
  • + Einfach zu konfigurieren für Standard-CRUD
  • - Scope-Bedingungen werden als OData-Filter in die Query injiziert — bei N:M mit Parental/Contact/Account-Scope Known Issue (CDS-Fehler)
  • - Site Settings pro Tabelle erforderlich (Webapi/entity/enabled)

Sicherheitswarnung: Global Table Permissions sind häufig die "schnelle Lösung" für das N:M-Problem mit der Web API — und ein ernstes Sicherheitsrisiko. Sie geben authentifizierten Nutzern Lesezugriff auf alle Datensätze der Tabelle, unabhängig von Beziehungen. Microsofts empfohlener Weg ist stattdessen der Einsatz von FetchXML als Parameter im OData-Query, oder Liquid FetchXML als einfachste Option ohne Workaround. Mehr zu den Sicherheitsebenen im Artikel zur Power Pages Sicherheitsarchitektur.

Quick Reference: Wann was?

Use Case Empfehlung Begründung
Initial Data Load Liquid 1 Request, sofort im DOM, SEO-kompatibel
N:M-Beziehung lesen Liquid empfohlen Web API möglich, aber Known Issue mit engen Scopes — FetchXML-Parameter als Workaround nötig
N:M-Beziehung schreiben Web API $ref-Endpunkt für Associate / Disassociate
CRUD nach User-Interaktion Web API Kein Page Reload, modernes UX-Pattern
Dropdown / Lookup befüllen Liquid Referenzdaten ändern sich selten, Preload effizienter
Infinite Scroll / Pagination Web API $top / $skip für seitenweises Laden
SEO-relevante Daten Liquid Crawler sehen server-gerenderten HTML-Inhalt
Live-Updates (Polling) Web API Intervall-basierte API-Calls ohne Reload
Clientseitiges Filtern / Suchen Hybrid Liquid preloaded alle Daten, JS filtert ohne Netzwerk

Die Cheat Sheets zu Power Pages Web API und Liquid Templates findest du im Download-Bereich.

Power Pages Architektur-Fragen?

Liquid vs. Web API ist eine von vielen Architekturentscheidungen, die über Erfolg oder Frust in Power Pages Projekten entscheiden. Im Erstgespräch schaue ich mir deine konkrete Situation an.

Kostenloses Erstgespräch buchen

Weitere Artikel

Tino Rabe

Tino Rabe

Microsoft Power Pages MVP

Microsoft MVP für Power Pages. Ich unterstütze mittelständische Unternehmen bei der Entwicklung sicherer Kundenportale.

Fragen zu diesem Thema?

Lassen Sie uns darüber sprechen.

Termin buchen