Direkt zum Inhalt
Maximilian Rabus
Decoupled Drupal mit React und GraphQL am Beispiel mit Drupal 8

Decoupled (oder Headless) Drupal ist momentan eines der größten Themen in der Drupal Community. Durch eine Decoupled Drupal Installation wird der tatsächliche Inhalt einer Drupal-Seite von dessen Darstellung in zwei voneinander unabhängige Komponenten getrennt. Die Meinungen über diese Herangehensweise gehen auseinander, doch dass dieses Thema es wert ist, sich damit zu beschäftigen, steht außer Frage.

Am Beispiel einer Drupal 8 Installation soll im Folgenden gezeigt werden, wie sich Decoupled Drupal mit React als Frontend und GraphQL als Verbindungsstück umsetzen lässt. In Verbindung damit werden Vor- und Nachteile, Chancen und aktuelle Limitierungen der verwendeten Komponenten erörtert.

Decoupled Drupal

Wie bereits erwähnt, werden bei einer Decoupled Drupal Installation Front- und Backend voneinander getrennt. Das Backend (Drupal) dient “nur” noch als Lager für die Daten und den Content. Das Frontend bedient sich nach Belieben aus diesem Lager und hat komplette Souveränität, wie es mit den Daten und deren Darstellung verfährt.

Einer der Vorteile der Unabhängigkeit ist, dass sowohl Front- wie auch Backend eigenständig agieren können, ohne auf den jeweils anderen Rücksicht nehmen zu müssen. So sind komplette Redesigns möglich, ohne auch nur eine Einstellung im Backend zu ändern.

Zudem ist Drupal ein einzigartiges System, welches normalerweise von Frontend-Entwicklern ein recht umfangreiches Backend-Verständnis verlangt, um effektiv und vor allem effizient arbeiten zu können. Durch eine Trennung dieser Bereiche können Frontend-Entwickler ohne Drupal-Kenntnisse arbeiten und ihre natürlichen Fähigkeiten einsetzen, ohne sich weiteres Wissen aneignen zu müssen.

Auch von verschiedenen Geräten kann man sich hierdurch unabhängig machen. Ein Backend kann für mobile Applikationen, Web Applikationen oder klassische Webseiten gleichzeitig verwendet werden – das Frontend hat jederzeit die Kontrolle, welche Daten es wie darstellt.

Wie immer gibt es natürlich aber auch eine Kehrseite der Medaille: Viele Funktionalitäten, die Drupal sonst standardmäßig und umsonst mitliefert, müssen selbst implementiert werden. Dazu gehören Layout, Templating, Routing, Authentifikation, Security, Performance, usw. Die Liste ist an dieser Stelle noch nicht zu Ende. Der Vorteil von einem CMS wie Drupal ist, dass es alle eben genannten Funktionalitäten plus viele weitere Features ohne großen Mehraufwand bereitstellt. Damit soll nicht gesagt sein, dass sich Decoupled Drupal nicht lohnt, sondern dass man sich genau überlegen sollte, ob Decoupled Drupal das Richtige für seine Vision einer Drupal Applikation ist.

React und GraphQL

React wird die Grundlage für das Frontend der späterer Beispielapplikation sein. Ursprünglich von Facebook entwickelt, hat sich React in den letzten Jahren einen Namen als übersichtliche und effiziente Library für die Darstellung von Komponenten im Frontend gemacht.

Einer der Gründe dafür ist der Verzicht auf Templates und ein eher modularer Aufbau, der auf Komponenten basiert. Dies sorgt dafür, dass React sich in jedem Projekt testweise in kleinem Maßstab implementieren lässt, um später darauf aufzubauen.

Zudem führt React JSX ein – ein Dateiformat, welches es erlaubt, klassisches JS und HTML zu vermischen. HTML-Tags können so einfach in JS ohne umständliche Konkatenation verwendet werden, was das Schreiben von einzelnen Render Komponenten übersichtlicher und intuitiver gestaltet. Mehr über die Vorteile von React findet sich hier: http://blog.thefirehoseproject.com/posts/13-reasons-react-taking-web-development-industry-storm/

GraphQL ist das Verbindungsstück zwischen Front- und Backend und eine an Fahrt und Aufmerksamkeit gewinnende Alternative zu REST.

Der vermutlich größte Unterschied zu REST ist, dass der Client selber die Struktur der Daten, die er erhalten will, definiert und genau diese auch vom Server wieder erhält, was typische Probleme von REST wie “over-” oder “under-fetching” verhindert. Ein Artikel mit einer detaillierteren Übersicht findet sich hier: http://nordicapis.com/5-potential-benefits-integrating-graphql/

Gerade im Zusammenhang mit Drupal 8 bietet sich GraphQL aufgrund des gleichnamigen Contrib-Modules an, welches den Einstieg und die Verwendung von GraphQL erheblich erleichtert.

Vorbereitung

Bevor es an ein Praxisbeispiel mit React und GraphQL geht, müssen einige Vorbereitungen getroffen werden. Als Grundlage für die Drupal-Installation dient das drupal-composer/drupal-project von github, welches mit dem Befehl

composer create-project drupal-composer/drupal-project:8.x-dev 
some-dir --stability dev --no-interaction

installiert werden kann. Sollte Composer noch nicht installiert sein, findet sich hier eine Anleitung für Linux: https://getcomposer.org/doc/00-intro.md#installation-linux-unix-osx. Unabhängig davon lassen sich alle späteren Beispiele auch ohne dieses Composer-Projekt und mit einer “normalen” Drupal Installation durchführen.

Die folgenden Module sollten installiert und eingeschaltet sein:

  • GraphQL

    • GraphQL Core

    • GraphQL Mutations

    • GraphQL Content

    • GraphQL Content Mutations

    • GraphQL Explorer

Optional können die anderen GraphQL Module für verschiedene Anwendungen aktiviert werden, um z. B. Views oder Blocks mit GraphQL zu erhalten und zu verändern. Eine genauere Übersicht der einzelnen Module und Funktionen findet sich hier: https://www.amazeelabs.com/blog/

Mit dem folgenden Befehl können Module mit Composer installiert werden:

composer require drupal/module_name

Module werden durch diesen Befehl nur heruntergeladen, aber nicht eingeschaltet!

Unter /admin/people/permissions können für das GraphQL-Modul jetzt verschiedene Einstellungen vorgenommen werden. Die Einstellungen beziehen sich hierbei ausschließlich auf das Erhalten und Verändern des Inhaltstyps Artikel, welcher als Beispiel in diesem Blogartikel herangezogen wird.

Authentifikation ist ein weiteres großes Thema, womit sich dieser Blogartikel nicht beschäftigen wird. Aus diesem Grund werden sämtliche Abfragen in GraphQL als Anonymous User getätigt, was folgende Berechtigungen voraussetzt:

Welche Felder eines Inhaltstyps GraphQL zur Verfügung stehen und ausgelesen werden können, wird über einen View Mode festgelegt.

Unter /admin/structure/types/manage/article/display sollte ein neuer View Mode “graphql” angelegt werden. Alle Felder, die in diesem View Mode enthalten sind, können abgefragt und verändert werden.

Anschließend kann unter /admin/config/graphql/content festgelegt werden, welche Art von Inhalt GraphQL zur Verfügung stehen soll. Für das Beispiel eines Artikels muss lediglich der Haken bei “Content” und “Article” gesetzt sein. Bei “Article” kann jetzt der View Mode “graphql” ausgewählt werden.

Atomic React

Als Starter Kit für das Frontend mit React wird in diesem Beispiel Atomic React (https://arc.js.org/) verwendet. Atomic React bietet eine solide Grundlage für das Entwickeln einer klassischen Web-Applikation mit Dependencies zu Paketen, die das Routing, Testen, “module-bundling”, “Hot Reloading” und vieles mehr ermöglichen. Zudem ist die Struktur an der Atomic Design Methodik angelehnt, welche sehr gut mit dem komponentenbasierten React harmoniert.

Um Atomic React zu installieren, kann auf der Ordnerebene, auf der auch das drupal- (oder web) Verzeichnis liegt, der folgende Befehl ausgeführt werden:

git clone -b master https://github.com/diegohaz/arc.git frontend

“frontend” spezifiziert hierbei den Namen des Ordners, in dem die React Komponenten erstellt werden. In dem Ordner “frontend” kann nun ein

npm install

ausgeführt werden, um die vorhandenen Dependencies zu installieren. Zusätzlich werden noch zwei weitere Packages für später benötigt:

npm install --save apollo-client react-apollo

 

Atomic Design

Atomic Design ist eine Methodik des Interface-Designs, bei dem ein Interface in einzelne Komponenten aufgeteilt wird. Innerhalb von /frontend/src-example/components lassen sich beispielhaft einzelne Komponenten einsehen. “Atoms” bilden die kleinste Komponente und bestehen aus einem einzigen HTML-Tag oder einer Third-Party-Komponente.

“Molecules” sind mehrere “atoms” zusammengefasst. “organisms” setzen sich aus mehreren “atoms”, “molecules” oder anderen “organisms” zusammen. “Pages” sind die einzelnen Seiten, auf denen hauptsächlich “organisms” verwendet werden. “Templates” bestimmen die grundsätzliche Struktur einer “page” und “themes” stellen konfigurierbare Stylings zur Verfügung.

Jedes der Verzeichnisse stellt bereits eine Sammlung an Beispiel-Komponenten zur Verfügung. Um das Beispielprojekt im Browser anzusehen, kann der Befehl

npm run dev:example

verwendet werden. Auf http://localhost:3000/ sollte folgendes zu sehen sein: https://arc.js.org/

React Basics

Wer sich bisher noch nicht mit React beschäftigt hat, sollte auf dieser Seite https://reactjs.org/docs/hello-world.html zumindest die Kapitel bis einschließlich “Handling Events” genauer ansehen, um ein grobes Verständnis von React zu bekommen. Gerade das nachfolgende Praxisbeispiel sollte anschließend einfacher nachzuvollziehen sein.

Um die eigentliche Anwendung zu starten, wird der Befehl:

npm run dev

verwendet. Aktuell wird lediglich “Hello World” ausgegeben.

React Templating

Bevor die Seite mit weiterem Content befüllt wird, sollte ein Template erstellt werden, welches die Grundlage für diese und weitere Seiten bilden wird. Für den nächsten Schritt werden zwei weitere Dateien benötigt. Zum einen ein Template und ein neuer Organismus “Header”.

/components/templates/DefaultTemplate/index.js:

​​​​​​​import React from 'react'
import PropTypes from 'prop-types'


const DefaultTemplate = ({ header, children }) => {
  return (
    <div id="wrapper">
      <div id="header">{header}</div>
      <div id="content">{children}</div>
    </div>
  )
};

DefaultTemplate.propTypes = {
  header: PropTypes.node.isRequired,
  children: PropTypes.any.isRequired,
};

export default DefaultTemplate

Hier wird eine Komponente “DefaultTemplate” definiert, die als Parameter “header” und “children” übergeben bekommt. Die Komponente gibt drei <div> Tags zurück: Einen Wrapper, den Header und den Content.

Unter der Komponente werden zudem “proptypes” definiert. “proptypes” dienen dazu, übergebene Elemente zu validieren und konsistent zu halten. Für das Element “header” wird der Typ “node” als “isRequired” gesetzt, was bedeutet, dass das übergebene Element eine React Komponente sein muss, die gerendert werden kann. Für die “children” gibt es keine Spezifikation des Typs. Es wird lediglich vorausgesetzt, dass nicht nichts übergeben wird.

/components/organisms/Header/index.js:

import React from 'react'

const Header = (props) => {
  return (
    <div>
      {props.title}
    </div>
  )
};

export default Header

Die Komponente “Header” gibt lediglich ein <div> zurück. Der Inhalt wird durch das Attribut “title” des Objektes “props” bestimmt. “props” stellt hier die Summe der Übergabeparameter dar.

/components/pages/HomePage/index.js:

import React from 'react'

import { DefaultTemplate, Header } from 'components'

const HomePage = () => {
  return (
    <DefaultTemplate header={<Header title="Homepage"/>}>
    <div>Hello World</div>
    </DefaultTemplate>
  )
};

export default HomePage

Um das Template und den neuen Organismus “Header” auf der aktuellen Startseite zu verwenden, müssen diese beiden Komponenten importiert werden.

Anschließend kann das Template “DefaultTemplate” die Elemente “Header” als “header” und den Inhalt, (hier nur ein <div>) als “children” erhalten. Da der “title” hier auf “HomePage” gesetzt wird, wird der Inhalt der “Header” Komponente diesen Wert erhalten.

Wird die Seite aktualisiert, sollte das Markup folgendermaßen aussehen:

 

React Routing

Für das Routing wird eine zweite “page” “LoginPage” benötigt. Am schnellsten wird das durch Kopieren der “HomePage” Komponente erreicht. Anschließend wird in “LoginPage” der “title” der “Header” Komponente auf “LoginPage” sowie der Inhalt des <div> Tags auf “Willkommen auf der Login-Seite.” geändert.

/components/App.js:

​​​​​​​import React from 'react'
import {Switch, Route} from 'react-router-dom'

import {HomePage, LoginPage} from 'components'

const App = () => {
  return (
    <Switch>
      <Route exact path="/" component={HomePage}/>
      <Route exact path="/login" component={LoginPage}/>
    </Switch>
  )
};

export default App

Das Routing wird in der Datei App.js durchgeführt. Der bisherige Inhalt wurde aus Übersichtsgründen gegen obigen Code ausgetauscht. Die Rückgabe der “App” Komponente enthält einen Switch, der für verschiedene Routes verschiedene “pages” mit ihren entsprechenden Templates rendert.

Das Attribut “exact” sorgt dafür, dass nur für genau diese Routen die entsprechenden Komponenten gerendert werden. Ansonsten würde z. B. auch für die Route /login/user “LoginPage” gerendert.

Lädt man die Seite neu, wird nun für den angegebenen Pfad die entsprechende Komponente dargestellt.

 

GraphQL Basics

Wie in der Einleitung bereits dargestellt, ist einer der großen Unterschiede von GraphQL zu REST, dass der Client selber entscheiden kann, welche Felder und Ressourcen er erhalten will. Die zur Verfügung stehenden Ressourcen werden durch sogenannte “schemas” und “types” definiert.

Der Vorteil bei der Verwendung von GraphQL in Verbindung mit Drupal ist, dass durch das GraphQL-Modul diese Vorbereitungen bereits erledigt sind. “schemas” und “types” sind für einen Großteil der Drupaldaten verfügbar und können sofort verwendet werden.

GraphiQL

GraphiQL ist eine IDE innerhalb des Browsers, die das Schreiben, Testen und Ausführen von GraphQL-Queries ermöglicht. In Drupal findet sich GraphiQL unter /graphql/explorer.

GraphiQL unterstützt Autovervollständigung basierend auf den vorhandenen “schemas” und “types” sowie einen “Documentation Explorer”, der ebendiese einsichtig macht. Als erstes Beispiel ist es das Ziel, einen einfachen Query zu schreiben, der alle Nodes vom Inhaltstyp “Article” mit ihrer “Id” und ihrem “Label” zurückgibt. Der folgende Query führt diese Abfrage durch:

GraphQL:

query BasicArticleQuery {
  nodeQuery {
    entities {
      ... on NodeArticle {
        entityId
        entityLabel
      }
    }
  }
}

“query” stellt hierbei die Art der Requests dar, während “BasicArticleQuery” der Name dieser Abfrage ist. “nodeQuery” ist eine spezielle Art von Query, die es ermöglicht, Nodes abzufragen. “entities” ist ein Feld dieses Querys, welches alle Grundfelder einer Node zur Verfügung stellt. Hier ist es nur möglich, Felder wie “entityId” oder “entityType” einzutragen – sprich Felder, die für jede Art von Node zur Verfügung stehen.

“... on NodeArticle” ist eine Bedingung, die dafür sorgt, dass nur Nodes des Inhaltstyps “Article” abgefragt werden und dessen spezifische Felder wie “title” oder “body” eingetragen werden können. Unabhängig davon, ob sie inner- oder außerhalb der Bedingung eingetragen werden, kann an dieser Stelle eine beliebige Anzahl von Feldern angegeben werden.

Nach dem Ausführen erhält man eine Liste aller Artikel mit den Werten für ihre Felder “entityId” und “entityLabel”.

Durch sogenannte “mutations” können durch GraphQL auch Elemente erzeugt oder verändert werden. Im nächsten Beispiel ist es das Ziel, einen Artikel mit dem Titel “GraphQL” zu erzeugen.

GraphQL:

mutation CreateArticle($article: NodeArticleInput!) {
createNodeArticle(input: $article) {
entity {
... on NodeArticle {
title
}
}
}
}

Query Variables:

{
"article": {
"title": "GraphQL"
}
}

Um diese Mutation zu verstehen, empfiehlt es sich, den “Documentation Explorer” zu benutzen. Gibt man in die Suche “createNodeArticle” ein und sieht sich das entsprechende Schema an, sieht man, dass diese Art von Mutation einen Parameter “input” des Typs “NodeArticleInput!” benötigt. Das “!” bedeutet, dass dieser Parameter benötigt wird und diese Mutation sich nicht ohne ihn durchführen lässt.

Durch einen Klick auf “NodeArticleInput!” erhält man eine Übersicht aller Felder eines Artikels. Das einzige Feld, welches benötigt wird, ist “title”; alle anderen Felder sind optional. Aus diesem Grund wurde oben eine Variable “article” mit dem Feld “title” und dem Wert “GraphQL” erstellt. Diese Variable lässt sich jetzt der Mutation “CreateArticle” übergeben.

Obwohl die Mutation schon “createNodeArticle” heißt, muss die Bedingung “... on NodeArticle” verwendet werden, da sonst nur die Grundfelder einer Node eingetragen werden können. “title” ist aber ein Feld, welches spezifisch für Artikel ist und nur durch diese Bedingung zur Verfügung steht.

Da “GraphQL” in der Variable “article” dem Feld “title” als Wert zugewiesen ist, wird beim Erstellen eines Artikels durch diese Mutation dessen Feld “title” auf “GraphQL” gesetzt. Ein Ausführen dieser Mutation sollte einen solchen Artikel erstellen und als Rückgabewert den gerade erstellten Artikel mit dem Feld “title” und dem Wert “GraphQL” liefern.

Als Alternative zu GraphiQL ist es auch möglich, den REST Client Insomnia (https://insomnia.rest/) zu verwenden. Es bietet zwar keinen “Documentation Explorer”, aber auch Autovervollständigung und im Gegensatz zu GraphiQL die Möglichkeit, Queries abzuspeichern und später wiederzuverwenden.

GraphQL in React

Das Ziel ist es jetzt, die oben gezeigten Queries aus React heraus auszuführen, die entsprechenden Daten aus Drupal zu erhalten und diese darzustellen. Die folgenden neuen oder geänderten Komponenten werden dafür benötigt:

/components/organisms/ArticleList/index.js:

import React from 'react';
import {gql, graphql} from 'react-apollo';

import {AddArticle} from 'components';

const ArticleList = ({data: {loading, error, nodeQuery}}) => {
  if (loading) {
    return <p>Loading...</p>;
  }
  if (error) {
    return <p>{error.message}</p>;
  }

  return (
    <div>
      <AddArticle />
      <ul>
        {nodeQuery.entities.map(entity =>
          <li>{entity.entityLabel}</li>)}
      </ul>
    </div>
  );
};

export const ArticleListQuery = gql`
query BasicArticleQuery{
  nodeQuery {
    entities {
      ... on NodeArticle {
        entityId
        entityLabel
      }
    }
  }
};

export default graphql(ArticleListQuery)(ArticleList);

Der Organismus “ArticleList” gibt eine <ul> von allen Artikeln auf einer bestimmten Seite zurück. Die Abfrage wird durch die Funktion “graphql” durchgeführt. Dafür wird zunächst in “ArticleListQuery” der Query geparst und in Zusammenhang mit “ArticleList” die Ergebnisse des Querys auf einzelne <li> Tags gemappt.

Beim ersten Aufruf von Article List wurde der Query noch nicht ausgeführt. Der Boolean “loading” ist jedoch wahr und signalisiert, dass noch keine Ergebnisse zur Verfügung stehen. Zudem wird, sofern es einen Fehler gab, dieser ausgegeben.

Ist der Query nicht mehr am Laden und es gab keinen Fehler, wird davon ausgegangen, dass ein Ergebnis zur Verfügung steht, welches verwendet werden kann. Neben einer <ul> mit allen Artikeln einer Seite gibt dieser Organismus auch die Komponente “AddArticle” zurück.

/components/molecules/AddArticle/index.js:

import React from 'react';
import { gql, graphql } from 'react-apollo';

import {ArticleListQuery} from '../../organisms/ArticleList/index'

const AddArticle = ({mutate}) => {
  const handleKeyUp = (evt) => {
    if (evt.keyCode === 13) {
      evt.persist();
      mutate({
        variables: {
          "article": {
            "title": evt.target.value
          }
        };

      refetchQueries: [{query: ArticleListQuery}],
      })

      .then(res => {
        evt.target.value = '';
      });
    }
  };

  return (
    <input
      type="text"
      placeholder="Add Article"
      onKeyUp={handleKeyUp}
    />
  );
};

const CreateArticleMutation = gql`
mutation CreateArticle($article: NodeArticleInput!) {
  createNodeArticle(input: $article) {
    entity {
      ... on NodeArticle {
        title
      }
    }
  }
};

export default graphql(CreateArticleMutation)(AddArticle);

Das Molekül “AddArticle” bietet über ein Textfeld die Möglichkeiten, durch die Eingabetaste einen Artikel mit dem im Textfeld eingegebenen Namen zu erstellen.

Der Aufbau ist ähnlich wie bei dem Organismus “ArticleList”. Die Komponente “AddArticle” besteht aus zwei Teilen. Als Rückgabewert enthält sie ein einfaches Textfeld, welches einen Callback zu einem Event Handler hat. Der Event Handler prüft, ob die gedrückte Taste die Eingabetaste ist und fügt der “mutate-prop” mehrere Attribute hinzu.

Zum einen wird als Variable “article” der Inhalt des Textfeldes “title” zugewiesen. Zum anderen wird mit “refetchQueries” ein Query angegeben, der nach der Mutation erneut ausgeführt werden soll.

Gibt man in das Textfeld also einen Titel ein und drückt die Eingabetaste, wird durch die Mutation ein Artikel mit einem Titel, der dem im Textfeld eingegebenen String entspricht, erstellt. Anschließend wird der “ArticleListQuery” erneut ausgeführt und zeigt in der Liste aller Artikel jetzt auch den eben erstellten Artikel an.

/components/pages/HomePage/index.js:

import React from 'react'
import ApolloClient, { createNetworkInterface } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';

import { DefaultTemplate, Header, ArticleList } from 'components'

const networkInterface = createNetworkInterface({
  uri: 'http://decoupled-project.dev/graphql'
});

const client = new ApolloClient({
  networkInterface
});

const HomePage = () => {
  return (
    <DefaultTemplate header={<Header title="Homepage"/>}>
    <ApolloProvider client={client}>
    <ArticleList />
    </ApolloProvider>
    </DefaultTemplate>
  )
};

export default HomePage

Um die gerade erstellten GraphQL-Queries tatsächlich auszuführen, wird ein GraphQL-Client benötigt, wodurch einige Anpassungen an der HomePage Komponente vorgenommen werden müssen. Als GraphQL-Client für dieses Beispiel wird Apollo verwendet.

Zuerst wird ein “networkInterface” erstellt, welches als einziges Attribut die uri zur aktuellen Drupal Installation mit dem Endpunkt /graphql enthält. Mit diesem “networkInterface” lässt sich jetzt ein neuer GraphQL-Client mit Apollo aufsetzen. Zwar wird für die “HomePage” Komponente immer noch das “DefaultTemplate” verwendet, jedoch wird der Inhalt durch die Komponente “ApolloProvider”, dem als Parameter der GraphQL-Client übergeben wird, ausgetauscht.

Die “ApolloProvider” Komponente erhält als Inhalt die “ArticleList” Komponente, welche wiederum die “AddArticle” Komponente enthält. Dadurch werden nun alle benötigten Komponenten dargestellt. Nach einem Neuladen der Startseite sollte das fertige Ergebnis etwa so aussehen:

 

Je nach vorhandenen Artikeln in der Drupal Installation sieht deren Liste natürlich anders aus. Trägt man jetzt einen Titel für einen neuen Artikel (z.B. “Article 4”) in das Textfeld ein, und bestätigt mit der Eingabetaste, sollte dieser kurz darauf in der Liste der Artikel auftauchen:

 

Troubleshooting

Sollte der Error “Network error: Failed to fetch” ausgegeben werden, liegt das am fehlenden Header “Access-Control-Allow-Origin” der für Cross-Origin-Requests gesetzt sein muss. In diesem Fall empfiehlt sich das Abändern der default.services.yml in /sites/default/files. Ganz unten finden sich die entsprechenden Einstellungen der cors.config, die folgendermaßen aussehen sollten:

cors.config:
enabled: true

# Specify allowed headers, like 'x-allowed-header'.
allowedHeaders: ['x-csrf-token','authorization','content-type',
'accept','origin','x-requested-with','access-control-allow-origin']

# Specify allowed request methods, specify ['*'] to allow all 
# possible ones.
allowedMethods: ['*']

# Configure requests allowed from specific origins.
allowedOrigins: ['*']

# Sets the Access-Control-Expose-Headers header.
exposedHeaders: false

# Sets the Access-Control-Max-Age header.
maxAge: false

# Sets the Access-Control-Allow-Credentials header.
supportsCredentials: false

Löst auch das nicht das Problem, gibt es die Chrome Extension “Allow-Control-Allow-Origin: *”, die nichts anderes macht, als den fehlenden Header zu setzen.

Die Zukunft von GraphQL und Drupal

Wer diesen Artikel bis hierhin gelesen hat, sollte einen groben Überblick über die Möglichkeiten und Vorteile von einer Decoupled Drupal Installation im Verbund mit GraphQL und React erhalten haben.

Trotzdem gibt es aktuell noch einige elementare Anwendungsfälle mit GraphQL, die sich nicht (zumindest nicht nur mit dem GraphQL-Modul) durchführen lassen. So ist es aktuell noch nicht möglich, bestehende Entitäten zu verändern oder diese zu löschen.

Ein weiteres Problem besteht in der Authentifikation. Für diesen Artikel wurden dem Anonymous User Berechtigungen wie “Create Content” oder “Execute arbitrary GraphQL requests” gegeben. Diese Berechtigungen sollten auf einer veröffentlichten Seite in den meisten Fällen nicht für den Anonymous User zur Verfügung stehen.

Für REST gibt es schon länger ein Modul, welches die Authentifikation mittels Oauth 2 ermöglicht. Mit GraphQL ist dies aktuell lediglich mit großem Aufwand und viel custom code durchzuführen. Erste Ansätze, sich mit einem Hybriden aus REST und GraphQL zu authentifizieren gibt es zwar, jedoch sind auch diese noch nicht sehr fortgeschritten.

Abschließend lässt sich sagen, dass GraphQL schon jetzt in speziellen Fällen eine gute Alternative zu REST darstellt. Trotzdem schöpft GraphQL gerade in Verbindung mit Drupal noch nicht sein volles Potenzial aus. Wenn man sich die Entwicklung der vergangenen Monate jedoch ansieht, kann man positiv gestimmt sein, dass nach und nach immer mehr Features, die davor viel custom code benötigt haben, ihren Weg in Drupal finden.

Weiterführende Links: