FallstudienBlogÜber uns
Anfragen

Tech-Stack 2020: GraphQL, Apollo Server und React.js

Wojciech Cichoradzki

12. Mai 20207 Min. Lesezeit

Back-end developmentProduct developmentGraphQL

Inhaltsverzeichnis

  • GraphQL vs. REST

  • GraphQL

  • Apollo GraphQL 

  • React.js

  • Unser Fazit 

Seit den 2000er‑Jahren sind RESTful‑Prinzipien der Industriestandard für den Aufbau von Web‑APIs. REST hat viele Probleme gelöst, die frühere Protokolle nicht bewältigen konnten, weist jedoch Schwächen auf. In modernen Anwendungen sind Daten komplex miteinander verknüpft, was in der Produktentwicklung zu Performance‑Problemen führen kann. Genau hier setzt GraphQL an.

GraphQL vs. REST

REST basiert auf HTTP und bietet eine einfache Art der Kommunikation zwischen Client‑Anwendung und Server. Auf Daten kann über HTTP‑Methoden auf spezifischen Endpunkten zugegriffen und diese verändert werden. Die Anwendung ist damit vom Server entkoppelt. Eigene Bibliotheken sind nicht mehr nötig. Die Integration mit unterschiedlichen Plattformen wird einfacher. 

Auf einer News‑Website hat jeder Artikel einen Titel, Textinhalt, Datum, Autor und visuelle Inhalte wie Bilder oder Videos. Außerdem kann er Nutzerkommentare enthalten, die in Threads gruppiert sind, sowie Links zu weiteren, verwandten Artikeln. Die wachsende Komplexität im Beziehungsgraphen der Daten zeigt die Grenzen des traditionellen REST‑Ansatzes. Jede Ressource muss separat, oft nacheinander, abgefragt werden, da eine Ressource von der anderen abhängt. GraphQL verbessert hier die Performance.

GraphQL

2015 stellten Facebook‑Ingenieure GraphQL als völlig neue Lösung für das Design von APIs vor. Der Kern von GraphQL ist, Entwicklern durch einen einzigen „intelligenten“ Endpunkt eine feinere Kontrolle über die benötigten Ressourcen zu geben, statt viele verschiedene Endpunkte für jede Ressource bereitzustellen. 

Der Aufbau von GraphQL-APIs ist entlang von Typen und Feldern organisiert, nicht entlang von Endpunkten. Daten werden über Queries abgefragt, jede Manipulation an den Daten ist in der GraphQL‑Terminologie eine Mutation. Da der Bedarf in der Industrie nach einer Lösung wie GraphQL bereits hoch war, gewann es schnell an Popularität und Unterstützung durch Entwickler. 

GraphQL hat sich etabliert und bietet Implementierungen in allen gängigen Programmiersprachen. Es wird bereits von großen Unternehmen wie Twitter, Yelp, The New York Times, Airbnb und weiteren genutzt.

Entwickler schätzen die Robustheit und die einfache Nutzung von GraphQL. Die Entwicklerumfrage 2019 „State of JavaScript“ zeigte, dass fast 40 % der JavaScript‑Entwickler GraphQL bereits ausprobiert haben und es wieder verwenden würden. Der Trend ist steigend, und wir werden in den kommenden Jahren mehr GraphQL‑Adoption sehen.

Apollo GraphQL 

Apollo ist eine führende GraphQL‑Implementierung mit einem Set an Tools und Bibliotheken, die beim Bau von GraphQL‑Anwendungen helfen. Ein typischer Apollo‑Server besteht aus einer Reihe von GraphQL‑Schema‑Definitionen und den dazugehörigen Resolvern. Um die Möglichkeiten von GraphQL zu zeigen, bauen wir eine einfache Anwendung mit einem Apollo Node.js‑Server und einem React.js‑Client.

Die Anwendung speichert Rezensionen gelesener Bücher. Beginnen wir mit der Grundstruktur des Projekts und bauen unseren Server.

$ mkdir app app/server app/client && cd app/server
$ yarn init

Als Nächstes legen wir eine Datenbank mit zwei Tabellen an: Authors und Books.

CREATE TABLE `authors` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(256) NOT NULL UNIQUE,
  PRIMARY KEY (`id`)
);
CREATE TABLE `books` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `author` INT NOT NULL,
  `title` VARCHAR(512) NOT NULL,
  `image` VARCHAR(512) NOT NULL,
  `review` VARCHAR(2048) NOT NULL,
  PRIMARY KEY (`id`),
  FOREIGN KEY (`author`)
    REFERENCES `authors`(`id`)
    ON UPDATE NO ACTION ON DELETE CASCADE
);

Für die Kommunikation mit der Datenbank verwenden wir die Query‑Builder‑Library knex. Installieren wir die benötigten Abhängigkeiten und richten einen Basis‑Server ein.

$ yarn add dotenv knex mysql2 apollo-server
$ touch index.js

In index.js erstellen wir unsere Schema‑Definitionen und Resolver. Wir nutzen zwei Typen: Author und Book. Außerdem stellt unsere API zwei Queries für jeden Typ sowie eine einzelne Mutation bereit, die ein neues Buch hinzufügt.

require('dotenv').config();
const { ApolloServer, gql } = require('apollo-server');


const knex = require('knex')({
  client: 'mysql2',
  connection: {
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'root',
    password: process.env.DB_PASSWORD || '1234',
    database: process.env.DB_NAME || 'books_database',
  },
});

// The GraphQL schema
const typeDefs = gql`
  type Author {
    id: Int!
    name: String!
  }

  type Book {
    id: Int!
    author: Author!
    title: String!
    image: String!
    review: String!
  }

  type Query {
    books: [Book!]
    authors: [Author!]
  }

  type Mutation {
    addBook(author: String!, title: String!, image: String!, review: String!): Book!
  }
`;

// A map of functions which return data for the schema.
const resolvers = {
  Query: {
    books: async (parent, args, context, resolveInfo) => {
      const result = await knex('books')
        .leftJoin('authors', 'books.author', 'authors.id')
        .select('*')
        .options({ nestTables: true });
        return result.map(({ books, authors }) => ({
        ...books,
        author: authors,
      }));
    },
    authors: async () => knex('authors').select('*'),
  },
  Mutation: {
    addBook: async (parent, {
      author: authorName, title, image, review,
    }) => {
      let author = await knex('authors').first().where({ name: authorName });
      if (!author) {
        const [authorId] = await knex('authors').insert({ name: authorName });
        author = {
          id: authorId,
          name: authorName,
        };
      }
      const newBook = {
        author: author.id,
        title,
        image,
        review,
      };
      const [bookId] = await knex('books').insert(newBook);
      return {
        ...newBook,
        id: bookId,
      };
    },
  },
};

const server = new ApolloServer({
  typeDefs,
  resolvers,
});

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
}); 

Um den Server zu starten und unsere API mit aktivierten knex‑Logs zu testen, führen wir Node.js mit unserer index.js aus:

DEBUG=knex:query node index.js 🚀 Server ready at http://localhost:4000/

Öffnen wir anschließend http://localhost:4000/, begrüßt uns der sogenannte GraphQL Playground. Hier können wir unsere API bequem testen. 

Versuchen wir, alle Bücher mit folgendem Befehl im GraphQL Playground abzurufen: 

query { books { title author } } 

Zu diesem Zeitpunkt solltest du als Antwort ein leeres Array sehen, was bedeutet, dass derzeit keine Bücher vorhanden sind.

Da wir aktuell noch keine Bücher gespeichert haben, ist die Antwort einfach ein leeres Array. 

Wir können Bücher hinzufügen, indem wir die addBook‑Mutation ausführen. Zum Beispiel kannst du im GraphQL Playground den folgenden Befehl verwenden: 

mutation { addBook(title: "New Book", author: "John Doe") { title author } } 

Dies gibt im Response den Titel und den Autor des hinzugefügten Buchs zurück.

Wenn wir die Bücher erneut mit demselben Befehl wie zuvor abfragen: 

query { books { title author } }

 sehen wir das neu erstellte Buch in der Ergebnisliste.

 Unser „books“-Resolver führt einen LEFT JOIN auf der Tabelle ‘authors’ aus, um ID und Namen des Autors zu holen und in die Antwort zu übernehmen. Wird das Feld author jedoch nicht angefordert, könnte die aufwendige LEFT‑JOIN‑Operation entfallen. Um eine solche Optimierung umzusetzen, können wir das Package graphql-parse-resolve-info verwenden und unseren Resolver so refactoren, dass er prüft, ob das Feld author in der Anfrage enthalten ist.

const {
  parseResolveInfo,
  simplifyParsedResolveInfoFragmentWithType,
} = require('graphql-parse-resolve-info');
…
    books: async (parent, args, context, resolveInfo) => {
      const query = knex('books')
        .select('*')
        .options({ nestTables: true });
      const simplifiedFragment = simplifyParsedResolveInfoFragmentWithType(
        parseResolveInfo(resolveInfo),
        resolveInfo.returnType,
      );
      if (simplifiedFragment.fields.author) {
        query.leftJoin('authors', 'books.author', 'authors.id');
      }
 
      const result = await query;
      return result.map(({ books, authors: author }) => ({
        ...books,
        author,
      }));
    },

Wenn wir die Query anpassen und das Feld author entfernen, etwa so: 

query { books { title } }

stellen wir geänderte SQL‑Abfragen fest. Das liegt daran, dass die Abfrage spezifischer geworden ist und nun nur noch die Titel der Bücher anfordert.

Unsere API ist jetzt bereit, vom Client genutzt zu werden.

React.js

Wir initialisieren das React‑Projekt mit create-react-app und installieren die benötigten Abhängigkeiten:

cd .. && npx create-react-app client && cd client yarn add apollo-boost @apollo/react-hooks graphql

Wenn wir jetzt yarn start ausführen, öffnet sich ein Basis‑Starterprojekt. Erstellen wir unseren GraphQL‑Client und stellen ihn den Sub‑Components via Context Provider zur Verfügung. Dazu passen wir index.js folgendermaßen an:

import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from '@apollo/react-hooks';
import ApolloClient from 'apollo-boost';

import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';

const client = new ApolloClient({
  uri: 'http://localhost:4000',
});

ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode>,
  document.getElementById('root')
);

Im nächsten Schritt führen wir eine Query gegen unsere API aus und rendern eine Liste von Büchern. Apollo bietet dafür nützliche React Hooks. In unserem Fall verwenden wir den Hook useQuery zusammen mit unserer bereits genutzten GraphQL‑books‑Query. Passen wir App.js so an, dass die gespeicherten Bücher geladen und gerendert werden.

import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';

const BOOK_QUERY = gql`
  {
    books{
      id
      author{
        name
      }
      title
      image
      review
    }
  }
`;

export default function App() {
  const { loading, error, data } = useQuery(BOOK_QUERY);

  if (loading) {
    return <p>Loading...</p>;
  }
  if (error) {
    return <p>Error :(</p>;
  } 

  return (
    <div className="App">
      <h2>My Books</h2>
      { data.books.map(({ id, author, title, image, review }) => (
      <div key={id}>
        <img src={image} alt={title} />
        <p>{ title }</p>
        <p>{ author.name }</p>
        <p>{ review }</p>
      </div>
     ))}
    </div>
  );
}

Nach einem Refresh rendert unsere App nun erfolgreich eine Bücherliste. Das Styling ist jedoch noch nicht sehr ansprechend. Fügen wir die Library milligram.css hinzu, extrahieren die Book‑Komponente in ein separates Modul und geben dem Ganzen etwas CSS‑Feinschliff.

Index.html

<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/milligram/1.3.0/milligram.css">

Book.js

export default function Book({ author, title, image, review }: Props) {
  return (
    <div className="book container">
      <div className="row">
        <div className="column-10">
          <img
            src={image}
            alt="book cover"
            className="cover"
          />
        </div>
        <div className="column-90">
          <h4 className="title">{ title }</h4>
          <i>{ author }</i>
          <p>{ review }</p>
        </div>
      </div>
    </div>
  );
}

Index.css

.App {
  margin: 2rem 20%;
}

.book .cover {
  width: 100px;
  height: auto;
  margin-right: 2rem;
}

.book .title {
  margin-bottom: 0;
  font-weight: bold;
}

Nach diesen Anpassungen und den Verbesserungen an unseren GraphQL‑Queries sind unsere Datenabfragen effizienter und die Anwendung reagiert spürbar schneller.

Unser Fazit 

Die Einfachheit und Robustheit von GraphQL, insbesondere in Kombination mit Apollo und React, ist die beste Wahl für einen vielseitigen 2020‑Tech‑Stack. In den kommenden Monaten erwarten wir eine breitere Nutzung und weitere Optimierungen durch die wachsende Community. Lust auf mehr? Schau auf unserem Blog vorbei für weitere Artikel über Entwicklungs‑Tools.

Hast du Fragen zur Produktentwicklung? Dann besuche unsere Kontaktseite oder schreib uns an

Veröffentlicht am 12. Mai 2020

Teilen


Wojciech Cichoradzki

JavaScript Developer

Digital Transformation Strategy for Siemens Finance

Cloud-based platform for Siemens Financial Services in Poland

See full Case Study
Ad image
Tech-Stack 2020: GraphQL, Apollo Server und React.js
Verpassen Sie nichts – abonnieren Sie unseren Newsletter
Ich stimme dem Empfang von Marketing-Kommunikation von Startup House zu. Klicken Sie für die Details

Das könnte Ihnen auch gefallen...

Ruby on Rails - guide
Ruby on RailsBack-end developmentComputer programming

Tutorial: Ruby und Ruby on Rails installieren und RubyGems verwenden

Entdecken Sie eine Schritt-für-Schritt-Anleitung zur Installation und Nutzung von Ruby on Rails für die effiziente Entwicklung von Webanwendungen. Erfahren Sie, wie Sie Ruby einrichten, verschiedene Versionen verwalten, mit RubyGems und Bundler arbeiten und ein neues Ruby on Rails-Projekt erstellen. Starten Sie mit diesem umfassenden Leitfaden in die Entwicklung mit Ruby on Rails.

Jan Grela

20. März 20206 Min. Lesezeit

Business team analyzing smart locker monetization strategy
Digital productsProduct development

Was sagt ein im Rahmen der testgetriebenen Entwicklung (TDD) geschriebener Test aus?

Testgetriebene Entwicklung (TDD) ist eine Kernpraxis der agilen Softwareentwicklung und bietet einen robusten, rigorosen Ansatz für das Programmieren. Wenn du dir schon einmal die Frage gestellt hast: "Was genau drückt ein nach TDD geschriebener Test aus?", bist du hier richtig. In diesem Artikel nehmen wir diese Methode auseinander und beleuchten die Rolle von Unit-Tests, Testframeworks, Testfällen und mehr.

Marek Majdak

24. Jan. 20237 Min. Lesezeit

Wie Error Tracking und Application Monitoring Entwicklungszeit sparen
Quality ControlBack-end developmentSoftware development

Wie Error Tracking und Application Monitoring Entwicklungszeit sparen

Fehler im Code zu entdecken, nachzuverfolgen und zu melden ist in jeder Phase des Lebenszyklus Ihrer Anwendung entscheidend. Erfahren Sie, welche Vorteile Anwendungstests bieten, wie sie die Entwicklung beschleunigen und warum der Einsatz von Error-Tracking-Tools so wichtig ist. Wählen Sie das passende Test-Framework und das richtige Error-Tracking-Tool, um eine gründlich getestete, fehlerfreie Anwendung sicherzustellen. Kontaktieren Sie uns unter hello@start-up.house für fachkundige Unterstützung bei der Entwicklung einer großartigen App.

Jan Grela

04. Juni 20204 Min. Lesezeit

Bereit, Ihr Know-how mit KI zu zentralisieren?

Beginnen Sie ein neues Kapitel im Wissensmanagement – wo der KI-Assistent zum zentralen Pfeiler Ihrer digitalen Support-Erfahrung wird.

Kostenlose Beratung buchen

Arbeiten Sie mit einem Team, dem erstklassige Unternehmen vertrauen.

Rainbow logo
Siemens logo
Toyota logo

Wir entwickeln, was als Nächstes kommt.

Unternehmen

Startup Development House sp. z o.o.

Aleje Jerozolimskie 81

Warsaw, 02-001

VAT-ID: PL5213739631

KRS: 0000624654

REGON: 364787848

Kontakt

hello@startup-house.com

Unser Büro: +48 789 011 336

Neues Geschäft: +48 798 874 852

Folgen Sie uns

Award
logologologologo

Copyright © 2026 Startup Development House sp. z o.o.

EU-ProjekteDatenschutzerklärung