Zurück zum Blog Strapi als Headless CMS
Development & Einblicke

Strapi als Headless CMS

Von Simon Fischer10.08.2025
#Strapi#Angular#Headless CMS

Warum ich für meine Angular-Webseite auf Strapi als Headless CMS setze

Als Entwickler liebe ich es, an meinen eigenen Projekten zu feilen. Meine persönliche Portfolio-Webseite, gebaut mit Angular, ist meine digitale Visitenkarte. Sie zeigt meine Kenntnisse, meine Angebote und enthält einen Blog, mit dem ich meine Online-Präsenz ausbauen und über SEO organischen Traffic generieren möchte.

Doch schnell stand ich vor einer klassischen Herausforderung: Wie verwalte ich die Inhalte – insbesondere die Blogartikel – effizient, ohne jedes Mal in den Code eingreifen zu müssen? Die Antwort war ein Headless CMS. Meine Wahl fiel auf Strapi, und in diesem Beitrag zeige ich dir, warum und wie ich es umgesetzt habe.

Die Anforderungen: Flexibel und ohne Aufwand

Die Idee, eine komplette Admin-Oberfläche von Grund auf selbst zu bauen, war für mich keine Option. Das hätte den Zeitrahmen meines Projekts gesprengt. Ich brauchte eine fertige Lösung, die aber maximale Flexibilität bietet.

Meine wichtigsten Kriterien waren:

  • Keine eigene Admin-Oberfläche entwickeln: Ich wollte eine fertige, intuitive Benutzeroberfläche für die Inhaltspflege.
  • Wiederverwendbarkeit: Die Lösung sollte es mir ermöglichen, das CMS potenziell an mehrere Webseiten anzubinden.
  • Serverless und konfigurierbar: Ich wollte mich nicht um die Wartung eines eigenen Servers kümmern müssen. Eine gehostete Cloud-Lösung war ideal.
  • Volle Kontrolle über die Datenstruktur: Ich musste meine Inhalte genau so modellieren können, wie ich sie für mein Angular-Frontend benötigte.

Überraschenderweise musste ich nicht lange suchen. Strapi war das erste und einzige System, das ich mir ansah, da es auf den ersten Blick alle meine Anforderungen erfüllte. Die Entscheidung war schnell gefallen.

Die Umsetzung: Lokales Setup, Deployment via GitHub

Die Implementierung war erstaunlich unkompliziert. Anstatt mich direkt an eine Web-Oberfläche zu binden, habe ich mein Strapi-Projekt lokal auf meinem Rechner aufgesetzt. Das gibt mir die volle Kontrolle und die Freiheit, in meiner gewohnten Umgebung zu arbeiten.

Der Clou ist der Deployment-Prozess: Ich habe mein lokales Strapi-Projekt mit einem GitHub-Repository verbunden. Bei jedem neuen Commit auf den main-Branch wird automatisch ein neues Deployment auf der Strapi-Hosting-Infrastruktur angestoßen. Dieser CI/CD-Workflow ist nicht nur elegant, sondern auch extrem praktisch.

1. Inhalte in Strapi modellieren

Der erste Schritt war, meine Datenstruktur im Content-Type Builder von Strapi anzulegen. Mein wichtigster Content-Type ist der Beitrag. Wie im Screenshot zu sehen, habe ich ihn mit allen Feldern ausgestattet, die ich für einen Blogartikel benötige:

Strapi Admin Oberfläche

Besonders hervorzuheben sind hier:

  • Relationen: Ich kann jeden Beitrag einfach mit einem Author, Categories und Tags verknüpfen.
  • Komponenten: Für SEO-Meta-Daten habe ich eine wiederverwendbare SEO-Komponente erstellt. So kann ich für jeden Artikel individuelle Meta-Titel, -Beschreibungen und Social-Media-Bilder (ogImage) pflegen. Das ist extrem mächtig für meine SEO-Strategie.

2. Die Anbindung an Angular

Die Verbindung zwischen Strapi und meiner Angular-App habe ich über die REST-API realisiert. Angulars nativer HttpClient ist dafür perfekt geeignet.

Hier ist ein Auszug aus meinem StrapiService, der die Daten von der API abruft:

import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

import { environment } from '../../../../environments/environment';

@Injectable({
  providedIn: 'root',
})
export class StrapiService {
  private strapiUrl = environment.strapiUrl;

  /**
   * Zentrale Konfiguration für alle Standard-Populates,
   * um Redundanz zu vermeiden.
   */
  private defaultPopulateFields = ['author', 'coverImage', 'categories', 'tags', 'seo'];

  constructor(private http: HttpClient) {}

  getContentType(contentType: string): Observable<any> {
    let params = new HttpParams();
    // Füllt alle Relationen automatisch auf
    this.defaultPopulateFields.forEach((field) => {
      params = params.append(`populate[${field}]`, 'true');
    });

    return this.http.get(`${this.strapiUrl}/api/${contentType}`, { params });
  }

  getSingleItem(contentType: string, id: string): Observable<any> {
    let params = new HttpParams();
    this.defaultPopulateFields
      .filter((field) => field !== 'seo')
      .forEach((field) => {
        params = params.append(`populate[${field}]`, 'true');
      });

    // Stellt sicher, dass das Bild in der SEO-Komponente mitgeladen wird
    params = params.append('populate[seo][populate]', 'ogImage');

    return this.http.get(`${this.strapiUrl}/api/${contentType}/${id}`, { params });
  }

  getFeaturedPosts(limit: number = 3): Observable<any> {
    let params = new HttpParams()
      .set('filters[featured][$eq]', 'true')
      .set('pagination[limit]', limit.toString())
      .set('sort[0]', 'publishedAt:desc');

    this.defaultPopulateFields.forEach((field) => {
      params = params.append(`populate[${field}]`, 'true');
    });

    return this.http.get(`${this.strapiUrl}/api/beitrags`, { params });
  }
}

Herausforderung: Relationen richtig laden

Eine kleine Hürde hatte ich zu Beginn mit der API. Mir war nicht klar, dass verknüpfte Daten (Relationen wie author oder categories) standardmäßig vollständig fehlen. In der API-Antwort tauchte nicht einmal das Feld oder eine ID der Relation auf. Es sah so aus, als gäbe es die Verknüpfung gar nicht.

Nach kurzer Recherche in der Strapi-Dokumentation war die Lösung aber schnell gefunden: Der populate Parameter in der URL. Dieser weist Strapi an, die verknüpften Inhalte mitzuliefern.

Wie im Code zu sehen, füge ich diesen Parameter nun dynamisch an meine API-Anfragen an. Für tief verschachtelte Komponenten wie mein ogImage im SEO-Teil nutze ich eine spezifischere Populate-Anweisung (populate[seo][populate]=ogImage).

Mein ehrliches Fazit: Der Kosten-Nutzen-Faktor von Strapi

Hat sich die Entscheidung für Strapi gelohnt? Ja, aber mit einem wichtigen "Aber", das sich um den Kosten-Nutzen-Faktor dreht.

Der größte Vorteil bleibt die gewonnene Unabhängigkeit: Ich kann Inhalte schnell und ohne Code-Anpassungen pflegen. Die Anbindung an Angular war dank der guten API-Dokumentation ebenfalls unkompliziert.

Allerdings bringt die kostenlose "Free"-Version von Strapi Cloud, die ich nutze, ein paar Nachteile mit sich, über die man sich im Klaren sein muss:

  • Das "Cold Start"-Problem: Wenn die API eine Weile nicht genutzt wird, geht der Server in eine Art Standby-Modus. Der nächste Aufruf dauert dann merklich länger, da das System erst wieder "aufwachen" muss. Für einen Blog, der vielleicht nicht minütlich Traffic hat, kann diese Ladeverzögerung für den ersten Besucher eines Tages schon spürbar sein.
  • Keine Custom Domain: Im Free-Tier läuft das Backend unter einer strapi.io-Domain. Das ist für ein professionelles Projekt nicht ideal und fühlt sich eher nach einer Testumgebung an.
  • Der Sprung zum Bezahlplan: Der nächste Plan, "Essential", kostet derzeit 15 $ pro Monat. An diesem Punkt stelle ich mir die Frage: Lohnt sich das für einen einfachen Blog? Für diesen Preis könnte man auch einen kleinen vServer (VPS) mieten und Strapi dort selbst hosten – dann hätte man volle Kontrolle, keine Cold Starts und eine eigene Domain. Der Bezahlplan lohnt sich meiner Meinung nach erst dann, wenn der Admin-Bereich so komplex wird, dass die gesparte Entwicklungszeit die monatlichen Kosten rechtfertigt.

Mein Fazit ist daher etwas gespalten. Für den Moment ist die kostenlose Variante ein fantastischer Weg, um schnell zu starten. Aber ich muss für mich noch genau abwägen, welche weiteren Projekte ich mit Strapi umsetzen will, damit sich ein möglicher Umstieg auf den Bezahlplan oder der Aufwand des Self-Hostings wirklich lohnt.