{"id":671,"date":"2026-05-21T00:29:05","date_gmt":"2026-05-20T23:29:05","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=671"},"modified":"2026-06-17T00:29:48","modified_gmt":"2026-06-16T23:29:48","slug":"graphql-in-java-flexible-apis-mit-spring-for-graphql","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=671","title":{"rendered":"GraphQL in Java \u2014 Flexible APIs mit Spring for GraphQL"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">W\u00e4hrend REST-APIs f\u00fcr viele Anwendungsf\u00e4lle die richtige Wahl bleiben, sto\u00dfen sie bei datenintensiven Client-Anwendungen an ihre Grenzen: Overfetching (der Client erh\u00e4lt mehr Daten als ben\u00f6tigt) und Underfetching (der Client muss mehrere Endpoints nacheinander aufrufen) sind inh\u00e4rente Probleme. GraphQL l\u00f6st diese, indem der Client exakt die ben\u00f6tigten Felder anfordert \u2014 nicht mehr und nicht weniger.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Spring for GraphQL<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Spring for GraphQL ist das offizielle Spring-Modul f\u00fcr die Integration von GraphQL in Spring-Boot-Anwendungen. Es baut auf der etablierten Java-Bibliothek&nbsp;<strong>GraphQL Java<\/strong>&nbsp;auf und f\u00fcgt Spring-typische Konzepte wie Dependency Injection und deklarative Konfiguration hinzu.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span><span class=\"hljs-comment\">&lt;!-- Maven-Dependency f\u00fcr Spring Boot 4.1.x --&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>org.springframework.boot<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">groupId<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>spring-boot-starter-graphql<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">artifactId<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">dependency<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Schema-first-Entwicklung<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">GraphQL-APIs werden schema-first definiert. Das Schema beschreibt die verf\u00fcgbaren Typen, Queries und Mutationen:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">em<\/span>&gt;<\/span># src\/main\/resources\/graphql\/schema.graphqls<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">em<\/span>&gt;<\/span>\ntype Book {\n    id: ID!\n    title: String!\n    author: Author!\n    pages: Int\n}\n\ntype Author {\n    id: ID!\n    name: String!\n    books: &#91;Book!]!\n}\n\ntype Query {\n    bookById(id: ID!): Book\n    books: &#91;Book!]!\n    authors: &#91;Author!]!\n}\n\ntype Mutation {\n    createBook(title: String!, authorId: ID!, pages: Int): Book!\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Controller als Data Fetcher<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In Spring for GraphQL werden keine REST-Endpoints definiert, sondern&nbsp;<strong>Data Fetcher<\/strong>, die das Schema mit Daten versorgen. Der Ansatz ist gewohnt spring-ig:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">import org.springframework.graphql.data.method.annotation.Argument;\nimport org.springframework.graphql.data.method.annotation.QueryMapping;\nimport org.springframework.graphql.data.method.annotation.SchemaMapping;\nimport org.springframework.stereotype.Controller;\n\n@Controller\n<span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">BookController<\/span> <\/span>{\n\n    <span class=\"hljs-keyword\">private<\/span> <span class=\"hljs-keyword\">final<\/span> BookService bookService;\n\n    <span class=\"hljs-keyword\">public<\/span> BookController(BookService bookService) {\n        this.bookService = bookService;\n    }\n\n    @QueryMapping\n    <span class=\"hljs-keyword\">public<\/span> Book bookById(@Argument Long id) {\n        <span class=\"hljs-keyword\">return<\/span> bookService.findById(id);\n    }\n\n    @QueryMapping\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;Book&gt; books() {\n        <span class=\"hljs-keyword\">return<\/span> bookService.findAll();\n    }\n\n    @SchemaMapping(typeName = <span class=\"hljs-string\">\"Author\"<\/span>, field = <span class=\"hljs-string\">\"books\"<\/span>)\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-keyword\">List<\/span>&lt;Book&gt; booksForAuthor(Author author) {\n        <span class=\"hljs-keyword\">return<\/span> bookService.findByAuthorId(author.id());\n    }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Entscheidend: Der Client bestimmt, welche Felder er haben m\u00f6chte. Eine Query wie&nbsp;<code>{ bookById(id: 1) { title author { name } } }<\/code>&nbsp;liefert nur Titel und Autorname \u2014 keine unn\u00f6tigen Daten.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testen mit GraphQlTester<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Spring for GraphQL bringt exzellente Test-Werkzeuge mit. Mit&nbsp;<code>HttpGraphQlTester<\/code>&nbsp;k\u00f6nnen GraphQL-Requests \u00fcber HTTP gegen einen laufenden Server getestet werden:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">import org.junit.jupiter.api.Test;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.graphql.test.tester.HttpGraphQlTester;\nimport org.springframework.test.web.reactive.server.WebTestClient;\n\n@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\nclass BookControllerTest {\n\n    @Autowired\n    private WebTestClient.Builder clientBuilder;\n\n    @Test\n    void shouldReturnBookById() {\n        HttpGraphQlTester tester = HttpGraphQlTester.builder(clientBuilder).build();\n\n        tester.document(\"\"\"\n            query {\n                bookById(id: 1) {\n                    title\n                    author { name }\n                }\n            }\n            \"\"\")\n            .execute()\n            .path(\"bookById.title\")\n            .entity(String.class)\n            .isEqualTo(\"Der Prozess\");\n    }\n}\n<\/code><\/span><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Alternativ kann ein&nbsp;<code>HttpGraphQlTester<\/code>-Bean \u00fcber&nbsp;<code>@AutoConfigureGraphQlTester<\/code>&nbsp;automatisch konfiguriert und per&nbsp;<code>@Autowired<\/code>&nbsp;injiziert werden. F\u00fcr Tests ohne laufenden Server steht&nbsp;<code>ExecutionGraphQlServiceTester<\/code>&nbsp;zur Verf\u00fcgung.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">N+1-Problem und @BatchMapping<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Ein h\u00e4ufiges Problem bei GraphQL ist das N+1-Problem: Wird f\u00fcr jeden Autor dessen B\u00fccherliste einzeln geladen, entstehen Hunderte unn\u00f6tiger Datenbankabfragen. Spring for GraphQL bietet daf\u00fcr&nbsp;<code>@BatchMapping<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-keyword\">@BatchMapping<\/span>(typeName = <span class=\"hljs-string\">\"Author\"<\/span>, field = <span class=\"hljs-string\">\"books\"<\/span>)\npublic Mono&lt;Map&lt;Author, List&lt;Book&gt;&gt;&gt; booksForAuthors(List&lt;Author&gt; authors) {\n    <span class=\"hljs-selector-tag\">return<\/span> <span class=\"hljs-selector-tag\">bookService<\/span><span class=\"hljs-selector-class\">.findByAuthorIds<\/span>(<span class=\"hljs-selector-tag\">authors<\/span><span class=\"hljs-selector-class\">.stream<\/span>()\n        <span class=\"hljs-selector-class\">.map<\/span>(<span class=\"hljs-selector-tag\">Author<\/span><span class=\"hljs-selector-pseudo\">::id).toList())<\/span>;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p class=\"wp-block-paragraph\">Statt N einzelner Queries wird eine einzige Batch-Query ausgef\u00fchrt \u2014 und das Framework verteilt die Ergebnisse korrekt auf die Autoren. Die Methode kann auch imperativ ein&nbsp;<code>Map&lt;Author, List&lt;Book&gt;&gt;<\/code>&nbsp;zur\u00fcckgeben;&nbsp;<code>Mono<\/code>&nbsp;ist vor allem bei rein asynchronen Aufrufen sinnvoll.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Spring for GraphQL macht die Arbeit mit GraphQL in Spring Boot zug\u00e4nglich und entwicklerfreundlich. Die Kombination aus Schema-first-Ansatz, vertrauten Controller-Konventionen und m\u00e4chtigen Test-Tools senkt die Einstiegsh\u00fcrde erheblich. F\u00fcr Projekte, die flexible, client-gesteuerte APIs ben\u00f6tigen, ist GraphQL eine ausgezeichnete Erg\u00e4nzung zum klassischen REST-Stack.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W\u00e4hrend REST-APIs f\u00fcr viele Anwendungsf\u00e4lle die richtige Wahl bleiben, sto\u00dfen sie bei datenintensiven Client-Anwendungen an ihre Grenzen: Overfetching (der Client erh\u00e4lt mehr Daten als ben\u00f6tigt) und Underfetching (der Client muss mehrere Endpoints nacheinander aufrufen) sind inh\u00e4rente Probleme. GraphQL l\u00f6st diese, indem der Client exakt die ben\u00f6tigten Felder anfordert \u2014 nicht mehr und nicht weniger. Spring [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,5],"tags":[],"class_list":["post-671","post","type-post","status-publish","format-standard","hentry","category-plain_java","category-spring"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/671","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=671"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/671\/revisions"}],"predecessor-version":[{"id":672,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/671\/revisions\/672"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=671"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=671"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=671"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}