smplx.
Shopify Architektur

Custom App vs. Theme-Code: Wann lohnt sich was in Shopify? [2026]

Claudio Gerlich··11 min

Im Shopify-Architektur-Ratgeber besprechen wir die Grundlagen. Heute eine praktische Frage, die in fast jedem Shopify-Projekt auftaucht: Sollte ich diese Feature im Theme (Liquid) oder in einer Custom App bauen?

Wir bei smplx. beantworten diese Frage nicht theoretisch. Wir schauen uns deinen Use-Case an, kalkulieren Kosten, Performance und Maintenance – und treffen dann eine fundierte Entscheidung.

Die Grundlegenden Unterschiede

Theme-Code (Liquid)

Was ist Liquid?

Liquid ist Shopify's Template-Language. Code im Theme läuft im Storefront – also im Browser des Kunden. Das macht es schnell für einfache Dinge, aber auch limitiert.

Architektur:

Kunde Browser → Liquid im Theme → Rendered HTML

Pros:

  • Einfaches Deployment (Git Push, Theme-Editor)
  • Sofort verfügbar nach dem Push
  • Gute Performance für einfache Dinge (kein API-Call nötig)
  • Nah am Katalog-Daten (Produkte, Collections, etc.)
  • Keine zusätzliche Infrastruktur

Cons:

  • Keine Logik auf dem Server (Security-Risk bei sensiblen Daten)
  • Limits bei komplexer Geschäftslogik
  • Begrenzte Debugging-Tools
  • Kein Zugriff auf Admin API
  • Schwer zu testen automatisiert

Custom App

Was ist eine Custom App?

Eine Custom App ist Code, der auf Shopify's Servern läuft (Backend) oder auf deinen eigenen Servern. Sie hat Zugriff auf die Admin API und kann direkt mit Shop-Daten arbeiten.

Architektur:

Kunde Browser → Shopify Admin / Deine App → Liquid im Theme (optional) → Rendered HTML

Pros:

  • Zugriff auf Admin API (Schreib-Operationen!)
  • Server-seitige Logik = sichere Verarbeitung
  • Webhooks für Event-Driven Architecture
  • Skalierbar für komplexe Szenarien
  • Bessere Debugging-Tools
  • Automatisiertes Testing möglich

Cons:

  • Komplexerer Deployment-Prozess
  • API-Rate-Limits beachten
  • Mehr Infrastruktur (Server / Shopify Platform)
  • Höhere Latenz für Echtzeit-Daten
  • Kosten für Hosting/Infrastruktur

Real-World-Entscheidungen: Konkrete Beispiele

Beispiel 1: Dynamische Rabatt-Logik

Szenario: Du möchtest Kunden basierend auf Kauf-Historie automatisch Rabatte geben.

Option 1: Im Theme (Liquid)

{% if customer %}
  {% if customer.orders_count > 10 %}
    <p class="discount-banner">
      VIP-Kunde! +10% auf alles!
    </p>
  {% endif %}
{% endif %}

Problem:

  • Nur UI-Anzeige, nicht auf den Cart anwenden
  • Kein Zugriff auf Order-Daten in Echtzeit
  • Keine Automatisierung möglich

Option 2: Custom App mit Admin API

// Bei Product-Add-To-Cart (via Webhook)
const app = express();

app.post('/webhooks/orders/create', async (req, res) => {
  const order = req.body.order;
  const customer = order.customer;

  if (customer.orders_count > 10) {
    // Admin API: Automatisch Discount Code erstellen
    await shopify.post('/admin/api/2024-01/discount_codes.json', {
      discount_code: {
        price_rule_id: VETERAN_RULE_ID,
        code: `VIP_${customer.id}`,
      }
    });
  }

  res.status(200).send({ success: true });
});

Entscheidung: Custom App (Webhooks + Admin API) Grund: Automatische Discount-Anwendung braucht Server-Logik


Beispiel 2: Custom Checkout Extension

Szenario: Du möchtest einen Custom Feld im Checkout (z.B. "Lieferdatum-Wunsch").

Option 1: Theme Liquid + JavaScript

<input type="date" name="attributes[preferred_delivery_date]">
<script>
  // Validation im Browser
  document.querySelector('form').addEventListener('submit', (e) => {
    const date = document.querySelector('input[name*="delivery_date"]').value;
    if (new Date(date) < new Date()) {
      e.preventDefault();
      alert('Bitte Zukunftsdatum wählen');
    }
  });
</script>

Problem:

  • Checkout ist zu sensitiv für nur Browser-Validation
  • Keine Verknüpfung mit Fulfillment-Logik
  • Keine Benachrichtigung ans Warehouse

Option 2: Checkout Extension (App-Block)

// Extension auf Checkout-UI (moderne Shopify-API)
import { useExtensionInput } from '@shopify/checkout-ui-extensions-react';
import { Box, Text, Select } from '@shopify/checkout-ui-extensions-react';

export default function DeliveryDateExtension() {
  const { extensionPoint } = useExtensionInput();

  return (
    <Box border="base" padding="base">
      <Text>Wunsch-Lieferdatum:</Text>
      <Select
        options={generateFutureDate()}
        onChange={(value) => applyAttributes({ delivery_date: value })}
      />
    </Box>
  );
}

Entscheidung: Checkout Extension (moderne App-Architektur) Grund: Checkout ist Shopify Core – Extensions sind der richtige Ort


Beispiel 3: Bekateq Case Study

Bekateq hatte ursprünglich eine Shop mit 5+ Custom Apps für verschiedene Funktionen. Die Komplexität war nicht mehr zu handhaben.

Problem:

  • Jede App brauchte Monitoring
  • Unterschiedliche Deployments
  • Rate Limiting war kritisch
  • Wartungskosten explodierten

Lösung: Bekateq migrierte zu einer Single Custom App mit 4400+ Zeilen und Zero Dependencies (außer Shopify SDK).

Lernpunkt: Nicht jede kleine Funktion braucht eine separate App. Wenige, gut durchdachte Apps > viele kleine Apps.

Entscheidungsmatrix: Wann nutze ich was?

Feature Theme (Liquid) Custom App Checkout Extension
Produktseiten-UI ✅ Ja ❌ Nein ❌ Nein
Collection-Filter ✅ Ja ⚠️ Optional ❌ Nein
Cart-Logik ⚠️ Nur View ✅ Ja ✅ Ja
Checkout-Feld ❌ Unsicher ⚠️ Möglich ✅ Ja
Rabatt-Automation ❌ Nein ✅ Ja ❌ Nein
Webhook-Verarbeitung ❌ Nein ✅ Ja ❌ Nein
Admin Panel ❌ Nein ✅ Ja ❌ Nein
Echtzeit-Sync mit externem System ❌ Nein ✅ Ja ❌ Nein

Performance-Vergleich

Szenario: Produkt wird zur Collection hinzugefügt

Theme-Only (Liquid-Loop):

Browser-Request → Server rendert → HTML mit Loop
Rendering-Zeit: ~200ms (abhängig von Collection-Größe)

Custom App (GraphQL Admin API):

Frontend-Request → Admin API Call → Backend-Logik → Webhook → Theme-Update
Latenz: ~500-1000ms (API Call + Webhook-Verarbeitung)

Best Practice:

  • Zeitkritisch (< 500ms): Theme
  • Nicht zeitkritisch (Automation, Background-Jobs): Custom App

Shopify App Bridge: Die Brücke zwischen beiden Welten

App Bridge erlaubt dir, im Theme JavaScript zu schreiben, das mit einer Custom App kommuniziert, ohne die Seite zu laden.

Beispiel: Theme spricht mit App

// Im Theme-Liquid JavaScript
const client = ShopifyApp.apiClient;

document.querySelector('.add-to-cart').addEventListener('click', async () => {
  // Ruft Custom App Backend auf
  const response = await fetch('/api/custom-app/add-to-cart-hook', {
    method: 'POST',
    body: JSON.stringify({ productId: '123', quantity: 1 })
  });

  const result = await response.json();

  if (result.allowed) {
    // Weitermachen mit Standard-Checkout
  } else {
    alert(result.reason);
  }
});

Vorteil: Theme-UI mit App-Logic. Beste Welten kombiniert.

Admin API: Wann braucht deine App Schreib-Zugriff?

Die Admin API hat zwei Scopes:

  • read_products: Daten lesen
  • write_products: Daten schreiben

Schreib-Zugriff ist teuer – kostet mehr API-Calls und braucht mehr Testing.

Brauchst du Schreib-Zugriff für:

  • ❌ Daten anzeigen (nur Lese-Zugriff)
  • ❌ Filtered List anzeigen (nur Lese-Zugriff)
  • ✅ Produkte erstellen/editieren (Schreib-Zugriff!)
  • ✅ Inventory-Sync (Schreib-Zugriff!)
  • ✅ Automatische Discount-Erstellung (Schreib-Zugriff!)

Fehler, die wir oft sehen

Fehler 1: "Ich baue alles in eine massive Custom App"

Problem: 5000+ Zeilen, schwer zu testen, Deployment-Angst.

Lösung: Aufteilen nach Verantwortlichkeit. Bekateq's 4400 Zeilen waren hochgradig fokussiert.

Fehler 2: "Ich benutze Liquid für sichere Operationen"

Problem: Customer-Daten, Rabatte, Pricing – alle sichtbar im Quelltext.

Lösung: Alles, das sensitiv ist, gehört in eine Custom App.

Fehler 3: "Rate Limits? Kümmern wir uns später drum"

Problem: App läuft langsam, bekommt 429er, Kunden sehen Fehler.

Lösung: Von Tag 1: Rate Limit Handling implementieren.

Unsere Architektur-Prinzipien bei smplx.

1. Liquid für Presentation Theme-Code ist nur für UI. Keine geschäftskritische Logik.

2. Admin API nur wenn nötig Lese-Zugriff wo möglich, Schreib-Zugriff nur für echte Admin-Operationen.

3. Webhooks für Automation Nicht alle 10 Sekunden die API abfragen – Webhooks triggern die Logik.

4. Testing-Kultur Custom Apps müssen automatisiert testbar sein.

5. Minimale Apps Eine Verantwortlichkeit pro App. Ein großer klarer Purpose.

Die Entscheidungs-Checkliste

Frag dich selbst:

  1. Ist das Daten-sensitiv? → Custom App
  2. Brauche ich Admin API Zugriff? → Custom App
  3. Läuft das automatisch (Webhook)? → Custom App
  4. Muss das super-schnell sein (< 200ms)? → Theme
  5. Ist das nur UI-Anzeige? → Theme
  6. Testbar sein? → Custom App
  7. Braucht es Infrastruktur-Monitoring? → Custom App

Wenn mehr als 2 Punkte auf Custom App hindeuten → Custom App.

Kosten: Theme vs. Custom App

Theme-Development:

  • €10-50k Einmalige Kosten
  • €100-500/Monat Wartung
  • Niedrige Skalierungskosten

Custom App:

  • €30-150k Einmalige Entwicklung
  • €500-2000/Monat Infrastruktur + Monitoring
  • Höhere Skalierungskosten (pro Request)

Aber: Eine Custom App mit klarem Purpose ist günstiger als 3 Theme-Hacks, die du jeden Monat fixen musst.

Long-term Perspective

Nach 2-3 Jahren sieht es so aus:

Theme-Only Approach:

Year 1: Cost: $30k (Dev) + $1.2k (Hosting)
Year 2: Cost: $10k (Bug-Fixes) + $1.2k (Hosting)
Year 3: Cost: $20k (Big Feature) + $1.2k (Hosting)
Total: $64.6k

Problem: Code wurde zu komplex, kann nicht mehr ändern

App-Based Approach (Bekateq-Style):

Year 1: Cost: $80k (Dev) + $6k (Hosting)
Year 2: Cost: $5k (Bug-Fixes) + $6k (Hosting)
Year 3: Cost: $10k (New Feature) + $6k (Hosting)
Total: $113k

Vorteil: Code ist wartbar, neue Features dauern 5 Tage statt 15

Nach Jahr 3 zahlst du weniger neue Features, weil die Entwicklung 3x schneller ist.


Über den Autor

Claudio Gerlich ist Technical Architect bei smplx. und seit 2020 spezialisiert auf Shopify-Technologie. Mit smplx. haben wir über 50+ Shops architektonisch beraten – von simplen Theme-Anpassungen bis zu komplexen Multi-App-Systemen.

Bekateq ist ein gutes Beispiel: Wir halfen ihnen, ihre App-Architektur von chaotisch zu elegant umzustrukturn.

smplx. ist Shopify Technical Partner (seit 2020) mit Sitz in Coesfeld, NRW.