<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>GraphQL Archives - Codersee blog- Kotlin on the backend</title>
	<atom:link href="https://blog.codersee.com/tag/graphql/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>Kotlin &#38; Backend Tutorials - Learn Through Practice.</description>
	<lastBuildDate>Thu, 17 Apr 2025 09:17:14 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	

<image>
	<url>https://blog.codersee.com/wp-content/uploads/2025/04/cropped-codersee_logo_circle_2-32x32.png</url>
	<title>GraphQL Archives - Codersee blog- Kotlin on the backend</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Spring for GraphQL with Kotlin Coroutines</title>
		<link>https://blog.codersee.com/spring-graphql-kotlin-coroutines/</link>
					<comments>https://blog.codersee.com/spring-graphql-kotlin-coroutines/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Sat, 12 Apr 2025 12:10:34 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[Kotlin coroutines]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">https://codersee.com/?p=19012915</guid>

					<description><![CDATA[<p>In this tutorial, we will learn how to expose GraphQL APIs using Spring for GraphQL and Kotlin coroutines.</p>
<p>The post <a href="https://blog.codersee.com/spring-graphql-kotlin-coroutines/">Spring for GraphQL with Kotlin Coroutines</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p>At the end of this article, you will know precisely how to create a <strong>Spring Boot GraphQL</strong> application that exposes <strong>queries</strong> and <strong>mutations</strong> with the help of Kotlin <strong>suspended functions</strong> and <strong>Flows</strong>. </p>



<p>Long story short, we will see: </p>



<ul class="wp-block-list">
<li>how to prepare a GraphQL schema,</li>



<li>what imports are necessary to utilize suspended functions,</li>



<li>how to expose queries, mutations, and map schema with annotated controllers,</li>



<li>exception handling with <code>GraphQlExceptionHandler</code> </li>
</ul>



<h2 class="wp-block-heading" id="h-create-project">Create Project </h2>



<p>As the first step, let&#8217;s prepare a brand new project. </p>



<p>To do so, let&#8217;s navigate to the <a href="https://start.spring.io/" target="_blank" rel="noreferrer noopener">Spring Initializr</a> page and select the following imports: </p>



<figure class="wp-block-image size-large"><img fetchpriority="high" decoding="async" width="1024" height="537" src="http://blog.codersee.com/wp-content/uploads/2025/04/spring_for_graphql_webflux_spring_initializr_page_screenshot-1024x537.png" alt="Image is a screenshot from Spring Initializr page and shows necessary spring configs for the tutorial, among others, Spring Reactive Web and Spring for GraphQL" class="wp-image-19012919" srcset="https://blog.codersee.com/wp-content/uploads/2025/04/spring_for_graphql_webflux_spring_initializr_page_screenshot-1024x537.png 1024w, https://blog.codersee.com/wp-content/uploads/2025/04/spring_for_graphql_webflux_spring_initializr_page_screenshot-300x157.png 300w, https://blog.codersee.com/wp-content/uploads/2025/04/spring_for_graphql_webflux_spring_initializr_page_screenshot-768x403.png 768w, https://blog.codersee.com/wp-content/uploads/2025/04/spring_for_graphql_webflux_spring_initializr_page_screenshot.png 1530w" sizes="(max-width: 1024px) 100vw, 1024px" /></figure>



<p>The project <strong>metadata</strong> are totally up to you, but when it comes to the <strong>dependencies</strong>, we must select the <em>Spring Reactive Web</em> and <em>Spring for GraphQL</em>.</p>



<p>Thanks to the first one, Spring Boot configures a <strong>reactive web stack</strong> based on <strong>Project Reactor</strong> and <strong>Netty</strong> instead of Tomcat. And given we want to work with GraphQL <strong>and </strong>Kotlin coroutines, then this is a must-have for us. </p>



<p>The second provides support for Spring applications built on GraphQL Java. Moreover, it is the <strong>successor</strong> of the GraphQL Java Spring project from the GraphQL Java team.</p>



<p>Lastly, let&#8217;s download the zip package, extract and import it to our IDE. </p>



<h2 class="wp-block-heading" id="h-define-graphql-schema">Define GraphQL Schema</h2>



<h3 class="wp-block-heading" id="h-what-is-graphql-schema">What is GraphQL Schema?</h3>



<p>If you have ever worked with GraphQL before, then you know that compared to the REST, everything starts with <strong>schema definition</strong>.</p>



<p>If you haven&#8217;t, then <strong>GraphQL schema</strong> is a file typically written with <strong>Schema Definition Language (SDL)</strong> that describes how our API clients can interact with our server. To be more specific, inside it, we define the structure of returned data, as well as how consumers can fetch or send us the payloads. </p>



<p>And even though it may sound like a GraphQL substitute for OpenAPI specs, it is something more. In REST, OpenAPI docs are a nice addition to our project. In GraphQL, our services continuously utilize the schema definition to validate and execute requests against it. </p>



<p>And if you would like to learn more about GraphQL schema, then check out the <a href="https://graphql.org/learn/schema/">official documentation</a> later. But for now, let&#8217;s focus on the Spring / GraphQL / Kotlin combination 😉 </p>



<h3 class="wp-block-heading" id="h-schema-graphql-in-spring">schema.graphql in Spring </h3>



<p>As the first step, let&#8217;s head to the <code>resources &gt; graphql</code> directory in our project, and let&#8217;s insert the <code>schema.graphql</code> file:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="type Query {
  article(id: ID!): Article
  articles: [Article!]!
}

type Mutation {
  createArticle(input: CreateArticleInput!): Article!
  addComment(articleId: ID!, input: AddCommentInput!): Comment
}

input CreateArticleInput {
  title: String!
  content: String!
  userId: ID!
}

input AddCommentInput {
  userId: String!
  content: String!
}

type User {
  id: ID!
  name: String!
}

type Article {
  id: ID!
  title: String!
  content: String!
  author: User!
  comments: [Comment!]!
  createdAt: String!
}

type Comment {
  id: ID!
  content: String!
  author: User
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">type</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Query</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  article(id: ID!): Article</span></span>
<span class="line"><span style="color: #E1E4E8">  articles: [Article!]!</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F97583">type</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Mutation</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  createArticle(input: CreateArticleInput!): Article!</span></span>
<span class="line"><span style="color: #E1E4E8">  addComment(articleId: ID!, input: AddCommentInput!): Comment</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #E1E4E8">input CreateArticleInput {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">title</span><span style="color: #E1E4E8">: String</span><span style="color: #F97583">!</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">content</span><span style="color: #E1E4E8">: String</span><span style="color: #F97583">!</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">userId</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">ID</span><span style="color: #F97583">!</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #E1E4E8">input AddCommentInput {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">userId</span><span style="color: #E1E4E8">: String</span><span style="color: #F97583">!</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #B392F0">content</span><span style="color: #E1E4E8">: String</span><span style="color: #F97583">!</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F97583">type</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  id: ID!</span></span>
<span class="line"><span style="color: #E1E4E8">  name: String!</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F97583">type</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  id: ID!</span></span>
<span class="line"><span style="color: #E1E4E8">  title: String!</span></span>
<span class="line"><span style="color: #E1E4E8">  content: String!</span></span>
<span class="line"><span style="color: #E1E4E8">  author: User!</span></span>
<span class="line"><span style="color: #E1E4E8">  comments: [Comment!]!</span></span>
<span class="line"><span style="color: #E1E4E8">  createdAt: String!</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #F97583">type</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  id: ID!</span></span>
<span class="line"><span style="color: #E1E4E8">  content: String!</span></span>
<span class="line"><span style="color: #E1E4E8">  author: User</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>In Spring Boot, this file is registered automatically as our schema definition. </p>



<p>And as we can see, we defined 5 things: </p>



<ul class="wp-block-list">
<li><strong>queries</strong>&#8211; how a client reads data. Every GraphQL schema <strong>must </strong>support them,</li>



<li><strong>mutations</strong>&#8211; how a client modifies data,     </li>



<li><strong>inputs</strong>&#8211; used to pass structured arguments to our mutations and queries,</li>



<li><strong>types</strong>&#8211; establishing the structure of data we return,</li>



<li><strong>scalars</strong>&#8211; like <code>String</code>, or <code>ID</code> (but also <code>Int</code>, <code>Float</code>, <code>Boolean</code>), describing returned fields.</li>
</ul>



<p>Moreover, we can see the exclamation mark- <code>!</code>. In contrast to Kotlin, in GraphQL, we must <strong>explicitly define non-nullable types.</strong> Meaning, that <code>[Comment]</code> describes a nullable array of nullable <code>Comment</code> type (<code>Array&lt;Comment?&gt;?</code>), whereas <code>[Comment!]!</code> is a not null array with not null items.</p>



<h3 class="wp-block-heading" id="h-spring-graphql-schema-inspection">Spring GraphQL Schema Inspection</h3>



<p>Following, let&#8217;s rerun our application. </p>



<p>As a result, we should get the following in logs: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#2e3440ff"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="o.s.b.a.g.GraphQlAutoConfiguration: GraphQL schema inspection:
Unmapped fields: {Query=[article, articles], Mutation=[createArticle, addComment]}
Unmapped registrations: {}
Unmapped arguments: {}
Skipped types: []" style="color:#d8dee9ff;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki nord" style="background-color: #2e3440ff" tabindex="0"><code><span class="line"><span style="color: #d8dee9ff">o.s.b.a.g.GraphQlAutoConfiguration: GraphQL schema inspection:</span></span>
<span class="line"><span style="color: #d8dee9ff">Unmapped fields: {Query=[article, articles], Mutation=[createArticle, addComment]}</span></span>
<span class="line"><span style="color: #d8dee9ff">Unmapped registrations: {}</span></span>
<span class="line"><span style="color: #d8dee9ff">Unmapped arguments: {}</span></span>
<span class="line"><span style="color: #d8dee9ff">Skipped types: []</span></span></code></pre></div>



<p>As we can see, Spring Boot verifies the schema we defined every time we start the application. And the above message simply says that <strong>we do not have handler methods for our definition.</strong></p>



<p>Let&#8217;s fix that then 😉</p>



<h2 class="wp-block-heading" id="h-create-models">Create Models </h2>



<p>As the next step, let&#8217;s add Kotlin data classes to our Spring Boot project. They will be later used to serialize and deserialize data defined in our GraphQL schema.</p>



<p>To do so, let&#8217;s add the <code>Models.kt</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="data class User(
    val id: String,
    val name: String,
)
data class Article(
    val id: String,
    val title: String,
    val content: String,
    val authorId: String,
    val createdAt: String,
)
data class Comment(
    val id: String,
    val content: String,
    val articleId: String,
    val userId: String,
)
data class CreateArticleInput(
    val title: String,
    val content: String,
    val userId: String,
)
data class AddCommentInput(
    val content: String,
    val userId: String,
)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> name: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> title: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> content: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> authorId: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> createdAt: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> content: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> articleId: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> userId: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">CreateArticleInput</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> title: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> content: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> userId: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">AddCommentInput</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> content: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> userId: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">)</span></span></code></pre></div>



<p>As we can see, nothing spectacular here. The only interesting fact is that the <code>ID</code> is serialized in the same way as a <code>String</code>.</p>



<h2 class="wp-block-heading" id="h-querymapping-as-suspend-fun">QueryMapping as suspend fun</h2>



<p>Following, let&#8217;s learn how the Spring for GraphQL works with Kotlin coroutines. </p>



<p>According to the documentation:</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>Kotlin coroutine and <code>Flow</code> are adapted to <code>Mono</code> and <code>Flux</code>.</p>
</blockquote>



<p>Which means that whenever we use the Spring WebFlux, <strong>Spring automatically converts our suspended functions <code>Mono</code> and functions that return <code>Flow</code> to <code>Flux</code></strong> .</p>



<h3 class="wp-block-heading" id="h-implement-article-service">Implement Article Service</h3>



<p>Before we head to the GraphQL part, let&#8217;s make some small preparations.</p>



<p>Let&#8217;s insert the <code>ArticleService</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@Component
object ArticleService {
    private val articles = mutableListOf(
        Article(
            id = &quot;article-id-1&quot;,
            title = &quot;article-title-1&quot;,
            content = &quot;article-content-1&quot;,
            authorId = &quot;user-id-2&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
        Article(
            id = &quot;article-id-2&quot;,
            title = &quot;article-title-2&quot;,
            content = &quot;article-content-2&quot;,
            authorId = &quot;user-id-2&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
        Article(
            id = &quot;article-id-3&quot;,
            title = &quot;article-title-3&quot;,
            content = &quot;article-content-3&quot;,
            authorId = &quot;user-id-3&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
    )
    suspend fun findArticleById(id: String): Article? {
        delay(100)
        return articles.firstOrNull { it.id == id }
    }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@Component</span></span>
<span class="line"><span style="color: #F97583">object</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">ArticleService</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">private</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> articles </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">mutableListOf</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">    )</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findArticleById</span><span style="color: #E1E4E8">(id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">? {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">delay</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">100</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> articles.</span><span style="color: #B392F0">firstOrNull</span><span style="color: #E1E4E8"> { it.id </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> id }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As we can see, we use my favorite, in-memory database called <strong>mutable list</strong> (😉) and expose <code>findArticleById</code> &#8211; a suspended function.</p>



<p>This function will either find the desired article by ID or return a null value.</p>



<h3 class="wp-block-heading" id="h-add-articlecontroller">Add ArticleController</h3>



<p>With that done, let&#8217;s add the <code>ArticleController</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@Controller
class ArticleController(
    private val articleService: ArticleService,
) {
    @QueryMapping
    suspend fun article(@Argument id: String): Article? =
        articleService.findArticleById(id)
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@Controller</span></span>
<span class="line"><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">ArticleController</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">private</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> articleService: </span><span style="color: #B392F0">ArticleService</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">@QueryMapping</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">article</span><span style="color: #E1E4E8">(</span><span style="color: #B392F0">@Argument</span><span style="color: #E1E4E8"> id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">? </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">        articleService.</span><span style="color: #B392F0">findArticleById</span><span style="color: #E1E4E8">(id)</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As we can see, thanks to the Spring GraphQL, we can leverage the annotation-based approach.</p>



<p>Firstly, we must annotate our class with the <strong>@Controller </strong>annotation. Then, all the methods inside it annotated with <strong>@SchemaMapping</strong> annotation will become handlers. </p>



<p>And <strong>@QueryMapping, @MutationMapping, </strong>or <strong>@SubscriptionMapping</strong> are nothing else than meta annotations (annotated with @SchemaMapping). This way, we achieve a more readable code. </p>



<p>Additionally, in Spring, we use the <code><strong>@Argument</strong></code> annotation to bind GraphQL input arguments to our Kotlin instances. </p>



<h3 class="wp-block-heading" id="h-enable-graphiql-amp-test">Enable GraphiQL &amp; Test</h3>



<p>Nextly, let&#8217;s turn on the <strong>GraphiQL</strong>&#8211; the IDE for testing GraphQL APIs (like Postman, Bruno, or Insomnia).</p>



<p>To do so, let&#8217;s navigate to the <code>resources</code> directory and change the <code>application.properties</code> into the <code>application.yaml</code> file:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="spring:
  application:
    name: graphsandbox
  graphql:
    graphiql:
      enabled: true" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #85E89D">spring</span><span style="color: #E1E4E8">:</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #85E89D">application</span><span style="color: #E1E4E8">:</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #85E89D">name</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">graphsandbox</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #85E89D">graphql</span><span style="color: #E1E4E8">:</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #85E89D">graphiql</span><span style="color: #E1E4E8">:</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #85E89D">enabled</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">true</span></span></code></pre></div>



<p>Then, let&#8217;s open up the browser, go to <code>http://localhost:8080/graphiql</code>, and put the following: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="query SomeRandomQuery {
  article(id: &quot;article-id-1&quot;) {
    id
    title
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">query</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">SomeRandomQuery</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">article</span><span style="color: #E1E4E8">(</span><span style="color: #FFAB70">id</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">title</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As we can see, the <strong>query</strong> name is up to us, but inside it, we must define which of the &#8220;exposed&#8221; queries we would like to use. As the input parameter, we pass the String value- <code>article-id-1</code>&#8211; and lastly, we define what fields we would like to get in response (that&#8217;s what the whole GraphQL is a about, right?😉).</p>



<p>So, as the next step, let&#8217;s run the query and check out the result: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;errors&quot;: [
    {
      &quot;message&quot;: &quot;INTERNAL_ERROR for f337f3f3-5&quot;,
      &quot;locations&quot;: [
        {
          &quot;line&quot;: 2,
          &quot;column&quot;: 3
        }
      ],
      &quot;path&quot;: [
        &quot;article&quot;
      ],
      &quot;extensions&quot;: {
        &quot;classification&quot;: &quot;INTERNAL_ERROR&quot;
      }
    }
  ],
  &quot;data&quot;: {
    &quot;article&quot;: null
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;errors&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">    {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;message&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;INTERNAL_ERROR for f337f3f3-5&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;locations&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">        {</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;line&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">2</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;column&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">3</span></span>
<span class="line"><span style="color: #E1E4E8">        }</span></span>
<span class="line"><span style="color: #E1E4E8">      ],</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;path&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #9ECBFF">&quot;article&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      ],</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;extensions&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;classification&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;INTERNAL_ERROR&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  ],</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;article&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">null</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>Unfortunately, that is not what we expected. </p>



<p>Moreover, when we check out logs, we should see this:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="s.g.e.ExceptionResolversExceptionHandler : Unresolved NoClassDefFoundError for executionId f337f3f3-5
java.lang.NoClassDefFoundError: org/springframework/data/util/KotlinReflectionUtils
	at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.invokeSuspendingFunction(InvocableHandlerMethodSupport.java:141) ~[spring-graphql-1.3.4.jar:1.3.4]
	at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.doInvoke(InvocableHandlerMethodSupport.java:108) ~[spring-graphql-1.3.4.jar:1.3.4]" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #e1e4e8">s.g.e.ExceptionResolversExceptionHandler : Unresolved NoClassDefFoundError for executionId f337f3f3-5</span></span>
<span class="line"><span style="color: #e1e4e8">java.lang.NoClassDefFoundError: org/springframework/data/util/KotlinReflectionUtils</span></span>
<span class="line"><span style="color: #e1e4e8">	at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.invokeSuspendingFunction(InvocableHandlerMethodSupport.java:141) ~[spring-graphql-1.3.4.jar:1.3.4]</span></span>
<span class="line"><span style="color: #e1e4e8">	at org.springframework.graphql.data.method.InvocableHandlerMethodSupport.doInvoke(InvocableHandlerMethodSupport.java:108) ~[spring-graphql-1.3.4.jar:1.3.4]</span></span></code></pre></div>



<h3 class="wp-block-heading" id="h-fix-spring-graphql-coroutines-issue">Fix Spring GraphQL Coroutines Issue</h3>



<p>As we could see, the issue is caused by the missing dependency, and we can easily fix that.</p>



<p>To do so, let&#8217;s open up the <code>build.gradle.kts</code> and add the following dependency:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="implementation(&quot;org.springframework.data:spring-data-commons&quot;)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #e1e4e8">implementation(&quot;org.springframework.data:spring-data-commons&quot;)</span></span></code></pre></div>



<p>Then, let&#8217;s sync the gradle project and rerun the application.</p>



<p>After we run the test again, we should see the following:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;data&quot;: {
    &quot;article&quot;: {
      &quot;id&quot;: &quot;article-id-1&quot;,
      &quot;title&quot;: &quot;article-title-1&quot;
    }
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;article&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-title-1&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>Splendid! Everything works perfectly fine😉</p>



<h2 class="wp-block-heading" id="h-spring-graphql-with-kotlin-flow">Spring GraphQL with Kotlin Flow </h2>



<p>With that done, let&#8217;s learn how to work with Kotlin Flow and Spring GraphQL.</p>



<p>As the first step, let&#8217;s insert a new function to our <code>ArticleService</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="fun findAllArticles(): Flow&lt;Article&gt; = articles.asFlow()" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findAllArticles</span><span style="color: #E1E4E8">(): </span><span style="color: #B392F0">Flow</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">&gt; </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> articles.</span><span style="color: #B392F0">asFlow</span><span style="color: #E1E4E8">()</span></span></code></pre></div>



<p>This way, we produce a <strong>cold flow</strong> from our list of articles.</p>



<p>Then, let&#8217;s add a new handler:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@QueryMapping
fun articles(): Flow&lt;Article&gt; =
    articleService.findAllArticles()" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@QueryMapping</span></span>
<span class="line"><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">articles</span><span style="color: #E1E4E8">(): </span><span style="color: #B392F0">Flow</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">&gt; </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">findAllArticles</span><span style="color: #E1E4E8">()</span></span></code></pre></div>



<p>Nothing new this time. Just like previously, we add a new function marked with <strong>@QueryMapping</strong>.</p>



<p>When we run the following query in GraphiQL: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="query AnotherOne {
  articles {
    id
    title
    createdAt
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">query</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">AnotherOne</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">articles</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">title</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">createdAt</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>We should see the exact result: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;data&quot;: {
    &quot;articles&quot;: [
      {
        &quot;id&quot;: &quot;article-id-1&quot;,
        &quot;title&quot;: &quot;article-title-1&quot;,
        &quot;createdAt&quot;: &quot;2025-04-12T13:06:29.142469200&quot;
      },
      {
        &quot;id&quot;: &quot;article-id-2&quot;,
        &quot;title&quot;: &quot;article-title-2&quot;,
        &quot;createdAt&quot;: &quot;2025-04-12T13:06:29.142469200&quot;
      },
      {
        &quot;id&quot;: &quot;article-id-3&quot;,
        &quot;title&quot;: &quot;article-title-3&quot;,
        &quot;createdAt&quot;: &quot;2025-04-12T13:06:29.142469200&quot;
      }
    ]
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;articles&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">      {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-title-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;createdAt&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;2025-04-12T13:06:29.142469200&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      },</span></span>
<span class="line"><span style="color: #E1E4E8">      {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-title-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;createdAt&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;2025-04-12T13:06:29.142469200&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      },</span></span>
<span class="line"><span style="color: #E1E4E8">      {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-title-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;createdAt&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;2025-04-12T13:06:29.142469200&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    ]</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<h2 class="wp-block-heading" id="h-schemamapping">@SchemaMapping</h2>



<p>One of the greatest thing in GraphQL is the possibility to return the related data in one, single query. And if you remember our schema definition, our API should allow us to do that.</p>



<p>To be more specific, each article can have the <strong>author</strong>, multiple <strong>comments</strong>, and each comment also has its <strong>author</strong>.</p>



<p>But when we test that functionality right now:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="query AnotherOne {
  articles {
    id
    title
    createdAt
    author {
      id
      name
    }
    
    comments {
      id
      content
      author {
        name
      }
    }
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">query</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">AnotherOne</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">articles</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">title</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">createdAt</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">author</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">name</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">comments</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">content</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">author</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #FFAB70">name</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>The only thing we will get in response will be a looooong list of errors:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;errors&quot;: [
    {
      &quot;message&quot;: &quot;The field at path '/articles[0]/author' was declared as a non null type, but the code involved in retrieving data has wrongly returned a null value.  The graphql specification requires that the parent field be set to null, or if that is non nullable that it bubble up null to its parent and so on. The non-nullable type is 'User' within parent type 'Article'&quot;,
      &quot;path&quot;: [
        &quot;articles&quot;,
        0,
        &quot;author&quot;
      ],
      &quot;extensions&quot;: {
        &quot;classification&quot;: &quot;NullValueInNonNullableField&quot;
      }
    }
... other errors" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #e1e4e8">{</span></span>
<span class="line"><span style="color: #e1e4e8">  &quot;errors&quot;: [</span></span>
<span class="line"><span style="color: #e1e4e8">    {</span></span>
<span class="line"><span style="color: #e1e4e8">      &quot;message&quot;: &quot;The field at path &#39;/articles[0]/author&#39; was declared as a non null type, but the code involved in retrieving data has wrongly returned a null value.  The graphql specification requires that the parent field be set to null, or if that is non nullable that it bubble up null to its parent and so on. The non-nullable type is &#39;User&#39; within parent type &#39;Article&#39;&quot;,</span></span>
<span class="line"><span style="color: #e1e4e8">      &quot;path&quot;: [</span></span>
<span class="line"><span style="color: #e1e4e8">        &quot;articles&quot;,</span></span>
<span class="line"><span style="color: #e1e4e8">        0,</span></span>
<span class="line"><span style="color: #e1e4e8">        &quot;author&quot;</span></span>
<span class="line"><span style="color: #e1e4e8">      ],</span></span>
<span class="line"><span style="color: #e1e4e8">      &quot;extensions&quot;: {</span></span>
<span class="line"><span style="color: #e1e4e8">        &quot;classification&quot;: &quot;NullValueInNonNullableField&quot;</span></span>
<span class="line"><span style="color: #e1e4e8">      }</span></span>
<span class="line"><span style="color: #e1e4e8">    }</span></span>
<span class="line"><span style="color: #e1e4e8">... other errors</span></span></code></pre></div>



<p>Of course, with Spring we can easily fix it, but we will need small preparation first.</p>



<h3 class="wp-block-heading" id="h-update-service">Update Service</h3>



<p>Firstly, let&#8217;s get back to our service and update it: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@Component
object ArticleService {
    private val articles = mutableListOf(
        Article(
            id = &quot;article-id-1&quot;,
            title = &quot;article-title-1&quot;,
            content = &quot;article-content-1&quot;,
            authorId = &quot;user-id-2&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
        Article(
            id = &quot;article-id-2&quot;,
            title = &quot;article-title-2&quot;,
            content = &quot;article-content-2&quot;,
            authorId = &quot;user-id-2&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
        Article(
            id = &quot;article-id-3&quot;,
            title = &quot;article-title-3&quot;,
            content = &quot;article-content-3&quot;,
            authorId = &quot;user-id-3&quot;,
            createdAt = LocalDateTime.now().toString()
        ),
    )
    private val users = mutableListOf(
        User(id = &quot;user-id-1&quot;, name = &quot;user-name-1&quot;),
        User(id = &quot;user-id-2&quot;, name = &quot;user-name-2&quot;),
        User(id = &quot;user-id-3&quot;, name = &quot;user-name-3&quot;),
    )
    private val comments = mutableListOf(
        Comment(id = &quot;comment-id-1&quot;, content = &quot;comment-content-1&quot;, articleId = &quot;article-id-1&quot;, userId = &quot;user-id-3&quot;),
        Comment(id = &quot;comment-id-2&quot;, content = &quot;comment-content-2&quot;, articleId = &quot;article-id-2&quot;, userId = &quot;user-id-3&quot;),
        Comment(id = &quot;comment-id-3&quot;, content = &quot;comment-content-3&quot;, articleId = &quot;article-id-2&quot;, userId = &quot;user-id-2&quot;),
        Comment(id = &quot;comment-id-4&quot;, content = &quot;comment-content-4&quot;, articleId = &quot;article-id-3&quot;, userId = &quot;user-id-3&quot;),
    )
    
    suspend fun findArticleById(id: String): Article? {
        delay(100)
        return articles.firstOrNull { it.id == id }
    }
    fun findAllArticles(): Flow&lt;Article&gt; = articles.asFlow()
    suspend fun findUserById(id: String): User? {
        delay(100)
        return users.firstOrNull { it.id == id }
    }
    fun findCommentsByArticleId(id: String): Flow&lt;Comment&gt; =
        comments.filter { it.articleId == id }.asFlow()
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@Component</span></span>
<span class="line"><span style="color: #F97583">object</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">ArticleService</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">private</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> articles </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">mutableListOf</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">            id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-title-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-content-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">        ),</span></span>
<span class="line"><span style="color: #E1E4E8">    )</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">private</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> users </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">mutableListOf</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-1&quot;</span><span style="color: #E1E4E8">, name </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-name-1&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">, name </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-name-2&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">, name </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-name-3&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">    )</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">private</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> comments </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">mutableListOf</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-id-1&quot;</span><span style="color: #E1E4E8">, content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-content-1&quot;</span><span style="color: #E1E4E8">, articleId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">, userId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-id-2&quot;</span><span style="color: #E1E4E8">, content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-content-2&quot;</span><span style="color: #E1E4E8">, articleId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-2&quot;</span><span style="color: #E1E4E8">, userId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-id-3&quot;</span><span style="color: #E1E4E8">, content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-content-3&quot;</span><span style="color: #E1E4E8">, articleId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-2&quot;</span><span style="color: #E1E4E8">, userId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-id-4&quot;</span><span style="color: #E1E4E8">, content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;comment-content-4&quot;</span><span style="color: #E1E4E8">, articleId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;article-id-3&quot;</span><span style="color: #E1E4E8">, userId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">),</span></span>
<span class="line"><span style="color: #E1E4E8">    )</span></span>
<span class="line"><span style="color: #E1E4E8">    </span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findArticleById</span><span style="color: #E1E4E8">(id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">? {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">delay</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">100</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> articles.</span><span style="color: #B392F0">firstOrNull</span><span style="color: #E1E4E8"> { it.id </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> id }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findAllArticles</span><span style="color: #E1E4E8">(): </span><span style="color: #B392F0">Flow</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">&gt; </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> articles.</span><span style="color: #B392F0">asFlow</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findUserById</span><span style="color: #E1E4E8">(id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8">? {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">delay</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">100</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> users.</span><span style="color: #B392F0">firstOrNull</span><span style="color: #E1E4E8"> { it.id </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> id }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">findCommentsByArticleId</span><span style="color: #E1E4E8">(id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Flow</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">&gt; </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">        comments.</span><span style="color: #B392F0">filter</span><span style="color: #E1E4E8"> { it.articleId </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> id }.</span><span style="color: #B392F0">asFlow</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As we can see, we added two more &#8220;tables&#8221; along with simple functions to obtain data from them. </p>



<h3 class="wp-block-heading" id="h-update-controller">Update @Controller</h3>



<p>Then, let&#8217;s update our controller class:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@SchemaMapping
suspend fun author(article: Article): User =
    articleService.findUserById(article.authorId)!!
@SchemaMapping
suspend fun author(comment: Comment): User =
    articleService.findUserById(comment.userId)!!
@SchemaMapping
fun comments(article: Article): Flow&lt;Comment&gt; =
    articleService.findCommentsByArticleId(article.id)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@SchemaMapping</span></span>
<span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">author</span><span style="color: #E1E4E8">(article: </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">findUserById</span><span style="color: #E1E4E8">(article.authorId)</span><span style="color: #F97583">!!</span></span>
<span class="line"><span style="color: #B392F0">@SchemaMapping</span></span>
<span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">author</span><span style="color: #E1E4E8">(comment: </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">User</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">findUserById</span><span style="color: #E1E4E8">(comment.userId)</span><span style="color: #F97583">!!</span></span>
<span class="line"><span style="color: #B392F0">@SchemaMapping</span></span>
<span class="line"><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">comments</span><span style="color: #E1E4E8">(article: </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Flow</span><span style="color: #E1E4E8">&lt;</span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">&gt; </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">findCommentsByArticleId</span><span style="color: #E1E4E8">(article.id)</span></span></code></pre></div>



<p>As we can see, this time we leverage the <strong>@SchemaMapping</strong> annotation for our handlers. Pretty similar to what we did already.</p>



<p>The interesting part here is the <strong>parameters</strong>. Every handler takes the <strong>parent type as an argument</strong>. Meaning, that for the <code>article -&gt; author</code> parent-child relationship, we define the function that takes the article instance as an argument. And that&#8217;s it!</p>



<p>Of course, please the double exclamation (<code>!!</code>) should not land in the real, production-ready code😉</p>



<p>Anyway, when we rerun our test now, we will see the following: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;data&quot;: {
    &quot;articles&quot;: [
      {
        &quot;id&quot;: &quot;article-id-1&quot;,
        &quot;title&quot;: &quot;article-title-1&quot;,
        &quot;createdAt&quot;: &quot;2025-04-12T13:21:17.584507600&quot;,
        &quot;author&quot;: {
          &quot;id&quot;: &quot;user-id-2&quot;,
          &quot;name&quot;: &quot;user-name-2&quot;
        },
        &quot;comments&quot;: [
          {
            &quot;id&quot;: &quot;comment-id-1&quot;,
            &quot;content&quot;: &quot;comment-content-1&quot;,
            &quot;author&quot;: {
              &quot;name&quot;: &quot;user-name-3&quot;
            }
          }
        ]
      }
... more thingies " style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;articles&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">      {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;article-title-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;createdAt&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;2025-04-12T13:21:17.584507600&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;author&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-id-2&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;name&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-name-2&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">        },</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;comments&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">          {</span></span>
<span class="line"><span style="color: #E1E4E8">            </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;comment-id-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            </span><span style="color: #79B8FF">&quot;content&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;comment-content-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">            </span><span style="color: #79B8FF">&quot;author&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">              </span><span style="color: #79B8FF">&quot;name&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-name-3&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">            }</span></span>
<span class="line"><span style="color: #E1E4E8">          }</span></span>
<span class="line"><span style="color: #E1E4E8">        ]</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #FDAEB7; font-style: italic">...</span><span style="color: #E1E4E8"> </span><span style="color: #FDAEB7; font-style: italic">more</span><span style="color: #E1E4E8"> </span><span style="color: #FDAEB7; font-style: italic">thingies</span><span style="color: #E1E4E8"> </span></span></code></pre></div>



<p>And that&#8217;s what we expected. Awesome!</p>



<h2 class="wp-block-heading" id="h-graphql-mutations">GraphQL Mutations </h2>



<p>When we run the application right now, we see that we still miss two things that we defined- <strong>mutations</strong>.</p>



<p>So, before we add a new handler, let&#8217;s implement the following function in our service:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="suspend fun createArticle(input: CreateArticleInput): Article {
    delay(100)
    return Article(
        id = UUID.randomUUID().toString(),
        title = input.title,
        content = input.content,
        authorId = input.userId,
        createdAt = LocalDateTime.now().toString(),
    ).also { articles.add(it) }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">createArticle</span><span style="color: #E1E4E8">(input: </span><span style="color: #B392F0">CreateArticleInput</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">delay</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">100</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">        id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> UUID.</span><span style="color: #B392F0">randomUUID</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">(),</span></span>
<span class="line"><span style="color: #E1E4E8">        title </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> input.title,</span></span>
<span class="line"><span style="color: #E1E4E8">        content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> input.content,</span></span>
<span class="line"><span style="color: #E1E4E8">        authorId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> input.userId,</span></span>
<span class="line"><span style="color: #E1E4E8">        createdAt </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> LocalDateTime.</span><span style="color: #B392F0">now</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">(),</span></span>
<span class="line"><span style="color: #E1E4E8">    ).</span><span style="color: #B392F0">also</span><span style="color: #E1E4E8"> { articles.</span><span style="color: #B392F0">add</span><span style="color: #E1E4E8">(it) }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As can be seen, we take the input, create a new <code>Article</code> instance, and insert that to the list.</p>



<p>With Kotlin <code>also</code> ,we can return at the same time the created instance and achieve a slightly cleaner code. To be even fancier, we could use a reference here: <code>.also(articles::add)</code> 😉 </p>



<p>Then, let&#8217;s add the appropriate handler to our controller:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@MutationMapping
suspend fun createArticle(@Argument input: CreateArticleInput): Article =
    articleService.createArticle(input)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@MutationMapping</span></span>
<span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">createArticle</span><span style="color: #E1E4E8">(</span><span style="color: #B392F0">@Argument</span><span style="color: #E1E4E8"> input: </span><span style="color: #B392F0">CreateArticleInput</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Article</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">createArticle</span><span style="color: #E1E4E8">(input)</span></span></code></pre></div>



<p>As we can see, just like with @QueryMapping, this time, we mark the handler with <strong>@MutationMapping</strong> and our argument with <strong>@Argument</strong>. Nothing else is necessary to make our mutation work. </p>



<p>So, given that, let&#8217;s rerun the app and test our functionality:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="mutation someMutation {
  createArticle(
    input: {
      title: &quot;Awesome codersee Kotlin article&quot;, 
      content: &quot;Some content&quot;, 
      userId: &quot;user-id-3&quot;
    }
  ) {
    id
    title
    content
    author {
      id
      name
    }
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">mutation</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">someMutation</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">createArticle</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">input</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #9ECBFF">title</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;Awesome codersee Kotlin article&quot;</span><span style="color: #E1E4E8">, </span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #9ECBFF">content</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;Some content&quot;</span><span style="color: #E1E4E8">, </span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #9ECBFF">userId</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  ) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">title</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">content</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">author</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #FFAB70">name</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As a result, we should get the following:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;data&quot;: {
    &quot;createArticle&quot;: {
      &quot;id&quot;: &quot;0e51a902-835b-4e55-9b4a-f2dccd242ea7&quot;,
      &quot;title&quot;: &quot;Awesome codersee Kotlin article&quot;,
      &quot;content&quot;: &quot;Some content&quot;,
      &quot;author&quot;: {
        &quot;id&quot;: &quot;user-id-3&quot;,
        &quot;name&quot;: &quot;user-name-3&quot;
      }
    }
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;createArticle&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;0e51a902-835b-4e55-9b4a-f2dccd242ea7&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;title&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;Awesome codersee Kotlin article&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;content&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;Some content&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;author&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;id&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-id-3&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;name&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-name-3&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>Wonderful! We can see that with GraphQL mutations, we can not only create/modify things on our server. With this one query, we can also retrieve the data and the dependent objects😮</p>



<h2 class="wp-block-heading" id="h-error-handling-in-spring-graphql">Error Handling in Spring GraphQL</h2>



<p>As the last step, let&#8217;s add one more mutation to learn more about <strong>error handling</strong>.</p>



<p>First, let&#8217;s add the new custom exception ot our codebase in <code>Models.kt</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="data class GenericNotFound(val msg: String) : RuntimeException(msg)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">data</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">GenericNotFound</span><span style="color: #E1E4E8">(</span><span style="color: #F97583">val</span><span style="color: #E1E4E8"> msg: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">) : </span><span style="color: #B392F0">RuntimeException</span><span style="color: #E1E4E8">(</span><span style="color: #B392F0">msg</span><span style="color: #E1E4E8">)</span></span></code></pre></div>



<p>Then, let&#8217;s implement the following function in <code>ArticleService</code>:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="suspend fun addComment(id: String, input: AddCommentInput): Comment {
        delay(100)
        users.firstOrNull { it.id == input.userId }
            ?: throw GenericNotFound(&quot;User not found&quot;)
        return articles.firstOrNull { it.id == id }
            ?.let {
                Comment(
                    id = UUID.randomUUID().toString(),
                    articleId = id,
                    content = input.content,
                    userId = input.userId,
                ).also { comments.add(it) }
            }
            ?: throw GenericNotFound(&quot;Article not found&quot;)
    }" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">addComment</span><span style="color: #E1E4E8">(id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">, input: </span><span style="color: #B392F0">AddCommentInput</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #B392F0">delay</span><span style="color: #E1E4E8">(</span><span style="color: #79B8FF">100</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">        users.</span><span style="color: #B392F0">firstOrNull</span><span style="color: #E1E4E8"> { it.id </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> input.userId }</span></span>
<span class="line"><span style="color: #E1E4E8">            ?: </span><span style="color: #F97583">throw</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">GenericNotFound</span><span style="color: #E1E4E8">(</span><span style="color: #9ECBFF">&quot;User not found&quot;</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #F97583">return</span><span style="color: #E1E4E8"> articles.</span><span style="color: #B392F0">firstOrNull</span><span style="color: #E1E4E8"> { it.id </span><span style="color: #F97583">==</span><span style="color: #E1E4E8"> id }</span></span>
<span class="line"><span style="color: #E1E4E8">            ?.</span><span style="color: #B392F0">let</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">                </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">                    id </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> UUID.</span><span style="color: #B392F0">randomUUID</span><span style="color: #E1E4E8">().</span><span style="color: #B392F0">toString</span><span style="color: #E1E4E8">(),</span></span>
<span class="line"><span style="color: #E1E4E8">                    articleId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> id,</span></span>
<span class="line"><span style="color: #E1E4E8">                    content </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> input.content,</span></span>
<span class="line"><span style="color: #E1E4E8">                    userId </span><span style="color: #F97583">=</span><span style="color: #E1E4E8"> input.userId,</span></span>
<span class="line"><span style="color: #E1E4E8">                ).</span><span style="color: #B392F0">also</span><span style="color: #E1E4E8"> { comments.</span><span style="color: #B392F0">add</span><span style="color: #E1E4E8">(it) }</span></span>
<span class="line"><span style="color: #E1E4E8">            }</span></span>
<span class="line"><span style="color: #E1E4E8">            ?: </span><span style="color: #F97583">throw</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">GenericNotFound</span><span style="color: #E1E4E8">(</span><span style="color: #9ECBFF">&quot;Article not found&quot;</span><span style="color: #E1E4E8">)</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span></code></pre></div>



<p>This time, we can see a more production-ready code.</p>



<p>If we want to add a comment to the article, we must first check if both the desired author and article exist, right?</p>



<p>If that is not the case, then we throw our custom exception.</p>



<p>Following, let&#8217;s add a new mutation handler: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@MutationMapping
suspend fun addComment(
    @Argument id: String,
    @Argument input: AddCommentInput,
): Comment =
    articleService.addComment(id, input)" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@MutationMapping</span></span>
<span class="line"><span style="color: #F97583">suspend</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">addComment</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">@Argument</span><span style="color: #E1E4E8"> id: </span><span style="color: #B392F0">String</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">@Argument</span><span style="color: #E1E4E8"> input: </span><span style="color: #B392F0">AddCommentInput</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">): </span><span style="color: #B392F0">Comment</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">    articleService.</span><span style="color: #B392F0">addComment</span><span style="color: #E1E4E8">(id, input)</span></span></code></pre></div>



<p>Now, when we rerun the app and execute the following test: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="mutation anotherMutation {
  addComment(
    articleId: &quot;non-existing&quot;
    input: {userId: &quot;user-id-1&quot;, content: &quot;My comment&quot;}
  ) {
    id
    content
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #F97583">mutation</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">anotherMutation</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #FFAB70">addComment</span><span style="color: #E1E4E8">(</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">articleId</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;non-existing&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">input</span><span style="color: #E1E4E8">: {</span><span style="color: #9ECBFF">userId</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;user-id-1&quot;</span><span style="color: #E1E4E8">, </span><span style="color: #9ECBFF">content</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;My comment&quot;</span><span style="color: #E1E4E8">}</span></span>
<span class="line"><span style="color: #E1E4E8">  ) {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">id</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #FFAB70">content</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>We will see the following error: </p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;errors&quot;: [
    {
      &quot;message&quot;: &quot;INTERNAL_ERROR for f62a4c7e-1&quot;,
      &quot;locations&quot;: [
        {
          &quot;line&quot;: 52,
          &quot;column&quot;: 3
        }
      ],
      &quot;path&quot;: [
        &quot;addComment&quot;
      ],
      &quot;extensions&quot;: {
        &quot;classification&quot;: &quot;INTERNAL_ERROR&quot;
      }
    }
  ],
  &quot;data&quot;: {
    &quot;addComment&quot;: null
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;errors&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">    {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;message&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;INTERNAL_ERROR for f62a4c7e-1&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;locations&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">        {</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;line&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">52</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">          </span><span style="color: #79B8FF">&quot;column&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">3</span></span>
<span class="line"><span style="color: #E1E4E8">        }</span></span>
<span class="line"><span style="color: #E1E4E8">      ],</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;path&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #9ECBFF">&quot;addComment&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      ],</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;extensions&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;classification&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;INTERNAL_ERROR&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  ],</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;addComment&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">null</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>And this is not what we wanted, right? </p>



<h3 class="wp-block-heading" id="h-graphqlexceptionhandler-and-controlleradvice">GraphQlExceptionHandler and ControllerAdvice</h3>



<p>One of the solutions we can have in such a situation is a combination of ControllerAdvice and GraphQlExceptionHandler.</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p>If you would like to learn more about ControllerAdvice and RestControllerAdvice, then check out my <a href="https://blog.codersee.com/controlleradvice-vs-restcontrolleradvice/">other article</a>.</p>
</blockquote>



<p>So, as the next step, let&#8217;s add the <code>GlobalExceptionHandler</code> class to the project:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="@ControllerAdvice
class GlobalExceptionHandler {
    @GraphQlExceptionHandler
    fun handleGenericNotFound(ex: GenericNotFound): GraphQLError =
        GraphQLError.newError()
            .errorType(ErrorType.DataFetchingException)
            .message(ex.msg)
            .build()
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #B392F0">@ControllerAdvice</span></span>
<span class="line"><span style="color: #F97583">class</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">GlobalExceptionHandler</span><span style="color: #E1E4E8"> {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #B392F0">@GraphQlExceptionHandler</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #F97583">fun</span><span style="color: #E1E4E8"> </span><span style="color: #B392F0">handleGenericNotFound</span><span style="color: #E1E4E8">(ex: </span><span style="color: #B392F0">GenericNotFound</span><span style="color: #E1E4E8">): </span><span style="color: #B392F0">GraphQLError</span><span style="color: #E1E4E8"> </span><span style="color: #F97583">=</span></span>
<span class="line"><span style="color: #E1E4E8">        GraphQLError.</span><span style="color: #B392F0">newError</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">            .</span><span style="color: #B392F0">errorType</span><span style="color: #E1E4E8">(ErrorType.DataFetchingException)</span></span>
<span class="line"><span style="color: #E1E4E8">            .</span><span style="color: #B392F0">message</span><span style="color: #E1E4E8">(ex.msg)</span></span>
<span class="line"><span style="color: #E1E4E8">            .</span><span style="color: #B392F0">build</span><span style="color: #E1E4E8">()</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<p>As we can see, inside it, we implement a function that takes the <strong>GenericNotFound</strong> as an argument. This way, whenever our custom exception is thrown, Spring will return the <strong>GraphQLError</strong> instance with our config instead.</p>



<p>As a result, when we retest the application, we should get the following JSON:</p>



<div class="wp-block-kevinbatdorf-code-block-pro" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:block;padding:16px 0 0 16px;margin-bottom:-1px;width:100%;text-align:left;background-color:#24292e"><svg xmlns="http://www.w3.org/2000/svg" width="54" height="14" viewBox="0 0 54 14"><g fill="none" fill-rule="evenodd" transform="translate(1 1)"><circle cx="6" cy="6" r="6" fill="#FF5F56" stroke="#E0443E" stroke-width=".5"></circle><circle cx="26" cy="6" r="6" fill="#FFBD2E" stroke="#DEA123" stroke-width=".5"></circle><circle cx="46" cy="6" r="6" fill="#27C93F" stroke="#1AAB29" stroke-width=".5"></circle></g></svg></span><span role="button" tabindex="0" data-code="{
  &quot;errors&quot;: [
    {
      &quot;message&quot;: &quot;Article not found&quot;,
      &quot;locations&quot;: [],
      &quot;extensions&quot;: {
        &quot;classification&quot;: &quot;DataFetchingException&quot;
      }
    }
  ],
  &quot;data&quot;: {
    &quot;addComment&quot;: null
  }
}" style="color:#e1e4e8;display:none" aria-label="Copy" class="code-block-pro-copy-button"><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"></path></svg></span><pre class="shiki github-dark" style="background-color: #24292e" tabindex="0"><code><span class="line"><span style="color: #E1E4E8">{</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;errors&quot;</span><span style="color: #E1E4E8">: [</span></span>
<span class="line"><span style="color: #E1E4E8">    {</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;message&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;Article not found&quot;</span><span style="color: #E1E4E8">,</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;locations&quot;</span><span style="color: #E1E4E8">: [],</span></span>
<span class="line"><span style="color: #E1E4E8">      </span><span style="color: #79B8FF">&quot;extensions&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">        </span><span style="color: #79B8FF">&quot;classification&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #9ECBFF">&quot;DataFetchingException&quot;</span></span>
<span class="line"><span style="color: #E1E4E8">      }</span></span>
<span class="line"><span style="color: #E1E4E8">    }</span></span>
<span class="line"><span style="color: #E1E4E8">  ],</span></span>
<span class="line"><span style="color: #E1E4E8">  </span><span style="color: #79B8FF">&quot;data&quot;</span><span style="color: #E1E4E8">: {</span></span>
<span class="line"><span style="color: #E1E4E8">    </span><span style="color: #79B8FF">&quot;addComment&quot;</span><span style="color: #E1E4E8">: </span><span style="color: #79B8FF">null</span></span>
<span class="line"><span style="color: #E1E4E8">  }</span></span>
<span class="line"><span style="color: #E1E4E8">}</span></span></code></pre></div>



<h2 class="wp-block-heading" id="h-spring-graphql-with-kotlin-coroutines-summary">Spring GraphQL with Kotlin Coroutines Summary</h2>



<p>And that&#8217;s all for this article on how to work with Kotlin coroutines and Flows in the Spring Boot GraphQL project.</p>



<p>As always, you can find the source code in my <a href="https://github.com/codersee-blog/spring-boot-3-graphql-kotlin-coroutines" target="_blank" rel="noreferrer noopener">GitHub repostory</a>.</p>
<p>The post <a href="https://blog.codersee.com/spring-graphql-kotlin-coroutines/">Spring for GraphQL with Kotlin Coroutines</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/spring-graphql-kotlin-coroutines/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>GraphQL SPQR with Spring Boot and Kotlin</title>
		<link>https://blog.codersee.com/graphql-spqr-with-spring-boot-and-kotlin/</link>
					<comments>https://blog.codersee.com/graphql-spqr-with-spring-boot-and-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Mon, 16 Nov 2020 08:00:00 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1617</guid>

					<description><![CDATA[<p>This time, I would like to teach you how to set up the Spring Boot GraphQL project with SPQR and Kotlin programming language.</p>
<p>The post <a href="https://blog.codersee.com/graphql-spqr-with-spring-boot-and-kotlin/">GraphQL SPQR with Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In my <a href="http://codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/" target="_blank" rel="noopener noreferrer">previous article</a>, I&#8217;ve shown you how to create a simple <strong>GraphQL Spring Boot</strong> project using <strong>GraphQL</strong> <strong>schema files</strong>. This time, I would like to teach you another approach- the <strong>GraphQL SPQR</strong> (GraphQL Schema Publisher &amp; Query Resolver).</p>
<p>To put it simply, <strong>GraphQL SPQR</strong> dynamically<strong> generates a schema from the source code</strong>. With this approach, we don&#8217;t need to define <em><strong>*.graphqls</strong></em> files anymore, but it will require us to add some additional configuration to the project.</p>
<h2 class="article-heading-main">2. Imports</h2>
<p>Just like in the previous article, we will start with the imports. However, this time the <strong><em>graphql-java-tools</em></strong> dependency will be replaced by the <em><strong>SPQR</strong></em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.graphql-java:graphql-spring-boot-starter:5.0.2")
implementation("com.graphql-java:graphiql-spring-boot-starter:5.0.2")
implementation("io.leangen.graphql:spqr:0.10.1")
</pre>
<h2 class="article-heading-main">3. Create Models</h2>
<p>As the next step, let&#8217;s define our POJOs.</p>
<p>Let&#8217;s start by adding a <strong>Company</strong> class with 4 simple properties:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class Company(
    val id: Long,
    val name: String,
    val address: String,
    val zipCode: String
)
</pre>
<p>Nextly, let&#8217;s implement the <strong>Employee</strong> class just like below:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class Employee(
    val id: Long,
    val firstName: String,
    val lastName: String,
    val status: EmployeeStatus,
    val company: Company
)

enum class EmployeeStatus {
    ACTIVE, RETIRED
}
</pre>
<h2 class="article-heading-main">4. Create Repositories</h2>
<p>As the third step, we need to implement the logic responsible for data management. To keep this article<strong> as simple as possible</strong>, we won&#8217;t be connecting to any external data source (anyway, this architecture might be a great entry point for further development).</p>
<h3 class="article-heading-sub">4.1. Implement CompanyRepository</h3>
<p>Firstly, let&#8217;s create the <strong>CompanyRepository</strong> class and annotate it with <em><strong>@Component</strong></em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyRepository {

    private val companyId = AtomicLong(4)

    private val companies = mutableSetOf(
        Company(1, "Company One", "Address 1", "10001"),
        Company(2, "Company Two", "Address 2", "10002"),
        Company(3, "Company Three", "Address 3", "10003"),
        Company(4, "Company Four", "Address 4", "10004")
    )
}
</pre>
<p><strong>CompanyRepository</strong> contains two properties: <em><strong>companyId</strong></em>&#8211; an atomic variable, which will be used to generate identifiers for companies; and <em><strong>companies</strong></em>&#8211; a set containing predefined objects.</p>
<p>Nextly, let&#8217;s add functions responsible for searching, creating, and deleting companies:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findAll(): Set = companies

fun findById(companyId: Long): Company =
    companies.find { it.id == companyId }
        ?: throw RuntimeException("Company with id [$companyId] could not be found.")

fun create(name: String, address: String, zipCode: String): Company {
    val company = Company(
        id = companyId.incrementAndGet(),
        name = name,
        address = address,
        zipCode = zipCode
    )

    companies.add(company)
    return company
}

fun delete(id: Long): Boolean =
    companies.removeIf { it.id == id }
</pre>
<h3 class="article-heading-sub">4.2. Implement EmployeeRepository</h3>
<p>With that being done, we can create the <strong>EmployeeRepository</strong> as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeRepository(
    private val companyRepository: CompanyRepository
) {

    val employerId = AtomicLong(3)

    val employees = mutableSetOf(
        Employee(1, "John", "Doe", EmployeeStatus.ACTIVE, companyRepository.findById(1)),
        Employee(2, "Adam", "Nowak", EmployeeStatus.ACTIVE, companyRepository.findById(2)),
        Employee(3, "Stan", "Bar", EmployeeStatus.RETIRED, companyRepository.findById(3))
    )
}
</pre>
<p>Similarly, the <strong>EmployeeRepository</strong> will contain 4 simple functions:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findAll(): Set = employees

fun findById(employeeId: Long) =
    employees.find { it.id == employeeId }
        ?: throw RuntimeException("Employee with id [$employeeId] could not be found.")

fun create(
    firstName: String,
    lastName: String,
    status: EmployeeStatus,
    companyId: Long
): Employee {
    val company = companyRepository.findById(companyId)

    val employee = Employee(
        id = employerId.incrementAndGet(),
        firstName = firstName,
        lastName = lastName,
        status = status,
        company = company
    )

    employees.add(employee)
    return employee
}

fun delete(id: Long): Boolean =
    employees.removeIf { it.id == id }
</pre>
<h2 class="article-heading-main">5. Define Queries</h2>
<p>In simple terms, <em><strong>Queries</strong></em> are responsible for retrieving data from our server. You might consider it something, like <strong>GET</strong> requests in<strong> REST APIs</strong>.</p>
<p>Let&#8217;s start by creating the <em><strong>CompanyQuery</strong></em> class as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyQuery(
    private val companyRepository: CompanyRepository
) {

    @GraphQLQuery(name = "companies")
    fun companies(): Set&lt;Company&gt; =
        companyRepository.findAll()

    @GraphQLQuery(name = "companyById")
    fun companyById(id: Long): Company =
        companyRepository.findById(id)
}
</pre>
<p>As you can see, it&#8217;s just a simple Spring Boot component. However, if we want to expose our functions, we need to annotate them with <em><strong>@GraphQLQuery</strong></em>. Moreover, query and function names do not need to match at all- we can name them independently.</p>
<p>Similarly, let&#8217;s create the <em><strong>EmployeeQuery</strong></em> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeQuery(
    private val employeeRepository: EmployeeRepository
) {

    @GraphQLQuery(name = "employees")
    fun employees(): Set&lt;Employee&gt; =
        employeeRepository.findAll()

    @GraphQLQuery(name = "employeeById")
    fun employeeById(id: Long): Employee =
        employeeRepository.findById(id)
}
</pre>
<h2 class="article-heading-main">6. Create Mutations</h2>
<p>Our next step will be the implementation of <em><strong>Mutations</strong></em>. They work in a similar way, but we use them to modify the data (you might consider them as the equivalent of <strong>POST/PUT/PATCH/DELETE</strong> handlers).</p>
<p>Let&#8217;s start with creating the <em><strong>CompanyMutation </strong></em>class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyMutation(
    private val companyRepository: CompanyRepository
) {

    @GraphQLMutation(name = "newCompany")
    fun newCompany(name: String, address: String, zipCode: String): Company =
        companyRepository.create(name, address, zipCode)

    @GraphQLMutation(name = "deleteCompany")
    fun deleteCompany(id: Long): Boolean =
        companyRepository.delete(id)
}</pre>
<p>Please notice, that this time we&#8217;ve used the <em><strong>@GraphQLMutation</strong></em> annotation instead.</p>
<p>Finally, let&#8217;s implement the <em><strong>EmployeeMutation</strong></em> in a similar manner:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeMutation(
    private val employeeRepository: EmployeeRepository
) {

    @GraphQLMutation(name = "newEmployee")
    fun newEmployee(
        firstName: String,
        lastName: String,
        status: EmployeeStatus,
        companyId: Long
    ): Employee =
        employeeRepository.create(firstName, lastName, status, companyId)

    @GraphQLMutation(name = "deleteEmployee")
    fun deleteEmployee(id: Long): Boolean =
        employeeRepository.delete(id)
}</pre>
<h2 class="article-heading-main">7. Prepare Configuration File</h2>
<p>After all the above is finished, we need to implement a <strong>schema generator</strong> and configure the <strong>GraphQL</strong> bean.</p>
<p>Let&#8217;s start by adding the <strong>GraphQLConfig</strong> class with the<em><strong> prepareGraphQLSchema</strong></em> function:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Configuration
class GraphQLConfig {
    private fun prepareGraphQLSchema(
        companyQuery: CompanyQuery, employeeQuery: EmployeeQuery,
        companyMutation: CompanyMutation, employeeMutation: EmployeeMutation
    ): GraphQLSchema =
        GraphQLSchemaGenerator()
            .withResolverBuilders(
                AnnotatedResolverBuilder(),
                PublicResolverBuilder("com.codersee.graphqlspqr")
            )
            .withOperationsFromSingletons(companyQuery, employeeQuery, companyMutation, employeeMutation)
            .withValueMapperFactory(JacksonValueMapperFactory())
            .generate()
}
</pre>
<p>Please notice, that the String passed to the <strong>PublicResolverBuilder</strong> constructor has to reflect the package structure of your project.</p>
<p>Lastly, let&#8217;s set up our <em><strong>GraphQL</strong> </em>bean using the already implemented schema:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Bean
fun graphQL(
    companyQuery: CompanyQuery, employeeQuery: EmployeeQuery,
    companyMutation: CompanyMutation, employeeMutation: EmployeeMutation
): GraphQL {
    val schema = prepareGraphQLSchema(companyQuery, employeeQuery, companyMutation, employeeMutation)

    return GraphQL.newGraphQL(schema)
        .queryExecutionStrategy(AsyncExecutionStrategy())
        .instrumentation(
            ChainedInstrumentation(
                listOf(
                    MaxQueryComplexityInstrumentation(100),
                    MaxQueryDepthInstrumentation(10)
                )
            )
        )
        .build()
}
</pre>
<p>As you can see, besides the execution strategy, we&#8217;ve configured the maximum <strong>complexity</strong> and <strong>depth</strong> values for our queries and mutations.</p>
<h2 class="article-heading-main">8. Implement the Controller</h2>
<p>As the last step, we need to create the entry point for our queries.</p>
<p>Let&#8217;s create a class called <strong>GraphQLController</strong>. Additionally, let&#8217;s inject the <strong>GraphQL</strong> bean configured in the previous step:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@RestController
class GraphQLController(
    private val graphQL: GraphQL
) {

    @PostMapping("/graphql")
    @ResponseBody
    fun execute(@RequestBody request: Map&lt;String, String&gt;): ExecutionResult {
        return graphQL
            .execute(
                request["query"].toString()
            )
    }
}
</pre>
<p>As you can see, the only function we need to implement here is the <em><strong>execute</strong></em>, which will be responsible for handling all the <strong>GraphQL requests</strong>.</p>
<h2 class="article-heading-main">9. Testing</h2>
<p>Finally, we can run the application and test it. Just like in the previous article, we will use the <strong>GraphiQL</strong>&#8211; a dedicated GUI for communicating with GraphQL servers.</p>
<p>In the beginning, we&#8217;ve added the<strong> GraphiQL Spring Boot Starter dependency</strong>, which allows us to run its web-based version under the <em><strong>/graphiql</strong></em> endpoint. However, you can always download it&#8217;s Electron-based wrapper <a href="https://github.com/skevy/graphiql-app" target="_blank" rel="noopener noreferrer">here</a>.</p>
<h3 class="article-heading-sub">9.1. Test Queries</h3>
<p>After starting the application, let&#8217;s head to the <em><strong>http://localhost:8080/graphiql</strong> </em>endpoint and run the following query:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  employees {
    id
    firstName
    lastName
    status
    company {
      name
    }
  }
}
</pre>
<p>As you can see, the query structure allows us to define, which fields we would like to fetch. I highly suggest you check out different combinations and see what will be the results. As a result of the above query, we should see the following output:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "employees": [
      {
        "id": "1",
        "firstName": "John",
        "lastName": "Doe",
        "status": "ACTIVE",
        "company": {
          "name": "Company One"
        }
      }
...
</pre>
<p>Nextly, let&#8217;s try to find the employee by id:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  employeeById(id: 1) {
    id
    firstName
    lastName
    status
    company {
      id
      name
    }
  }
}

# Result:

{
  "data": {
    "employeeById": {
      "id": "1",
      "firstName": "John",
      "lastName": "Doe",
      "status": "ACTIVE",
      "company": {
        "id": "1",
        "name": "Company One"
      }
    }
  }
}
</pre>
<p>Similarly, we can test company queries:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  companies {
    id
    name
    address
    zipCode
  }
}

# Result:

{
  "data": {
    "companies": [
      {
        "id": "1",
        "name": "Company One",
        "address": "Address 1",
        "zipCode": "10001"
      },
...
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  companyById(id: 1) {
    id
    name
    address
    zipCode
  }
}

# Result:

{
  "data": {
    "companyById": {
      "id": "1",
      "name": "Company One",
      "address": "Address 1",
      "zipCode": "10001"
    }
  }
}
</pre>
<h3 class="article-heading-sub">9.1. Test Mutations</h3>
<p>Testing mutations is quite similar to queries. The only difference is the usage of a <em><strong>mutation</strong></em> keyword:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  newCompany(name: "New", address: "Address new", zipCode: "10201") {
    id
    name
    address
    zipCode
  }
}
</pre>
<p>This time, a new company will be created, and the following data will be returned:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "newCompany": {
      "id": "5",
      "name": "New",
      "address": "Address new",
      "zipCode": "10201"
    }
  }
}
</pre>
<p>Similarly, we can create a new employee:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  newEmployee(firstName: "Piotr", lastName:"Wolak", status: ACTIVE, companyId: 2) {
    id
    firstName
    lastName
    status
    company {
      id
      name
    }
  }
}

# Result:

{
  "data": {
    "newEmployee": {
      "id": "4",
      "firstName": "Piotr",
      "lastName": "Wolak",
      "status": "ACTIVE",
      "company": {
        "id": "2",
        "name": "Company Two"
      }
    }
  }
}
</pre>
<p>Finally, let&#8217;s test delete mutations:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "deleteCompany": true
  }
}

# Result:

{
  "data": {
    "deleteCompany": true
  }
}
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  deleteEmployee(id: 1)
}

# Result:

{
  "data": {
    "deleteEmployee": true
  }
}
</pre>
<h2 class="article-heading-main">9. Conclusion</h2>
<p>And that would be all for this tutorial. I really hope that this article helped you to get a better understanding of how to create a <strong>GraphQL SPQR project with Spring Boot and Kotlin</strong>.</p>
<p>As always, you can find the working source code in our <a href="https://github.com/codersee-blog/kotlin-spring-boot-graphql-spqr" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>If you would like to ask about anything or share any feedback with me, I would be more than happy to hear from you. You can always contact me via <a href="https://www.facebook.com/codersee/" target="_blank" rel="noopener noreferrer"><strong>fan page</strong></a>, <a href="https://www.facebook.com/groups/622361565094117" target="_blank" rel="noopener noreferrer"><strong>group</strong></a>, or a <strong><a href="http://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> </strong>form.</p>
<p>The post <a href="https://blog.codersee.com/graphql-spqr-with-spring-boot-and-kotlin/">GraphQL SPQR with Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/graphql-spqr-with-spring-boot-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Introduction to GraphQL with Spring Boot and Kotlin</title>
		<link>https://blog.codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/</link>
					<comments>https://blog.codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/#respond</comments>
		
		<dc:creator><![CDATA[Piotr]]></dc:creator>
		<pubDate>Thu, 12 Nov 2020 07:00:26 +0000</pubDate>
				<category><![CDATA[Spring]]></category>
		<category><![CDATA[GraphQL]]></category>
		<category><![CDATA[Spring Boot]]></category>
		<guid isPermaLink="false">http://codersee.com/?p=1593</guid>

					<description><![CDATA[<p>In this step by step tutorial, we will learn how to create a simple app using GraphQL with Spring Boot and Kotlin and test it with GraphiQL.</p>
<p>The post <a href="https://blog.codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/">Introduction to GraphQL with Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></description>
										<content:encoded><![CDATA[<h2 class="article-heading-introduction">1. Introduction</h2>
<p>In this step by step tutorial, I would like to show you how to use <strong>GraphQL with Spring Boot and Kotlin.</strong> Additionally, we will learn how to test with <strong>GraphiQL</strong>&#8211; a dedicated GUI for editing and testing GraphQL queries and mutations.</p>
<p>In simple words, <a href="https://graphql.org/" target="_blank" rel="noopener noreferrer">GraphQL</a> is a query language for APIs that provides a more flexible alternative to REST. It allows clients to define the structure of the desired data, and the same structure of the data is returned from the server, therefore preventing large amounts of data from being returned. It was developed internally by <strong>Facebook</strong> in 2012 and open-sourced in 2015.</p>
<h2 class="article-heading-main">2. Imports</h2>
<p>As the first step, let&#8217;s add the necessary imports to our project:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="groovy">implementation("org.springframework.boot:spring-boot-starter-web")
implementation("com.graphql-java:graphql-spring-boot-starter:5.0.2")
implementation("com.graphql-java:graphiql-spring-boot-starter:5.0.2")
implementation("com.graphql-java:graphql-java-tools:5.2.4")
</pre>
<p>As you can see, besides Spring Boot starters, we&#8217;ve added the <strong>GraphQL Java Tools library</strong>. It allows us to use the GraphQL schema language to build our graphql-java schema. Additionally, it parses the given GraphQL schema and allows us to <strong>BYOO</strong> (bring your own object) to fill in the implementations.</p>
<h2 class="article-heading-main">3. Create Models</h2>
<p>As the next step, let&#8217;s implement our models. In this tutorial, we will implement a simple CRUD service for employees&#8217; management.</p>
<p>Let&#8217;s start by adding a <em><strong>Company</strong></em> class with 4 simple properties:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class Company(
    val id: Long,
    val name: String,
    val address: String,
    val zipCode: String
)
</pre>
<p>As the next step, let&#8217;s implement the <strong>Employee</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">class Employee(
    val id: Long,
    val firstName: String,
    val lastName: String,
    val status: EmployeeStatus,
    val company: Company
)

enum class EmployeeStatus {
    ACTIVE, RETIRED
}
</pre>
<p>Besides the class, we&#8217;ve also added the <strong>EmployeeStatus</strong> enum, which will be used as one of the properties of <strong>Employee</strong>. Additionally, each employee will have a <em><strong>company</strong></em> property indicating the associated company.</p>
<h2 class="article-heading-main">4. Create Repositories</h2>
<p>Nextly, let&#8217;s create repositories responsible for in-memory data management.</p>
<h3 class="article-heading-sub">4.1. Implement CompanyRepository</h3>
<p>Firstly, let&#8217;s create the <strong>CompanyRepository</strong> class and annotate it with <em><strong>@Component</strong></em>:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyRepository {

    private val companyId = AtomicLong(4)

    private val companies = mutableSetOf(
        Company(1, "Company One", "Address 1", "10001"),
        Company(2, "Company Two", "Address 2", "10002"),
        Company(3, "Company Three", "Address 3", "10003"),
        Company(4, "Company Four", "Address 4", "10004")
    )
}
</pre>
<p><strong>CompanyRepository</strong> contains two properties: <em><strong>companyId</strong></em>&#8211; an atomic variable, which will be used to generate identifiers for companies; and <em><strong>companies</strong></em>&#8211; a set containing predefined objects.</p>
<p>As the next step, let&#8217;s add functions responsible for searching, creating, and deleting companies:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findAll(): Set = companies

fun findById(companyId: Long): Company =
    companies.find { it.id == companyId }
        ?: throw RuntimeException("Company with id [$companyId] could not be found.")

fun create(name: String, address: String, zipCode: String): Company {
    val company = Company(
        id = companyId.incrementAndGet(),
        name = name,
        address = address,
        zipCode = zipCode
    )

    companies.add(company)
    return company
}

fun delete(id: Long): Boolean =
    companies.removeIf { it.id == id }
</pre>
<h3 class="article-heading-sub">4.2. Implement EmployeeRepository</h3>
<p>With that being done, we can create the <strong>EmployeeRepository</strong> as follows:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeRepository(
    private val companyRepository: CompanyRepository
) {

    val employerId = AtomicLong(3)

    val employees = mutableSetOf(
        Employee(1, "John", "Doe", EmployeeStatus.ACTIVE, companyRepository.findById(1)),
        Employee(2, "Adam", "Nowak", EmployeeStatus.ACTIVE, companyRepository.findById(2)),
        Employee(3, "Stan", "Bar", EmployeeStatus.RETIRED, companyRepository.findById(3))
    )
}
</pre>
<p>Similarly, the <strong>EmployeeRepository</strong> will have 4 simple functions:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">fun findAll(): Set = employees

fun findById(employeeId: Long) =
    employees.find { it.id == employeeId }
        ?: throw RuntimeException("Employee with id [$employeeId] could not be found.")

fun create(
    firstName: String,
    lastName: String,
    status: EmployeeStatus,
    companyId: Long
): Employee {
    val company = companyRepository.findById(companyId)

    val employee = Employee(
        id = employerId.incrementAndGet(),
        firstName = firstName,
        lastName = lastName,
        status = status,
        company = company
    )

    employees.add(employee)
    return employee
}

fun delete(id: Long): Boolean =
    employees.removeIf { it.id == id }
</pre>
<h2 class="article-heading-main">5. Define GraphQL Schema</h2>
<p>After we&#8217;ve implemented our models and repositories, we can start defining the schema. In simple terms, a schema is a contract between the client and the server describing the structure of the data and how the data might be operated.</p>
<p>For the purpose of this tutorial, let&#8217;s focus on the 4 types, we will be using:</p>
<ul>
<li><strong>Object Types</strong>&#8211; the most basic components, which represent a kind of objects we can fetch from our service (classes in our example)</li>
<li><strong>Enums</strong>&#8211; a special kind of scalar values restricted to a particular set of values</li>
<li><strong>Queries</strong>&#8211; every GraphQL service needs to have a Query. To put it simply, queries are used to describe how to read, or fetch values</li>
<li><strong>Mutations</strong>&#8211; work in a similar way, as queries and are used to modify a server-side data</li>
</ul>
<p>If you would like to learn more about the GraphQL Schema, please refer to the<a href="https://graphql.org/learn/schema/" target="_blank" rel="noopener noreferrer"> official documentation</a>.</p>
<h3 class="article-heading-sub">5.1. Describe Company Schema</h3>
<p>Let&#8217;s start with the definition of company schema. Let&#8217;s create a <strong>company.graphqls</strong> file and put it under the <em><strong>./src/main/resources/</strong></em> directory:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">type Company {
    id: ID!
    name: String!
    address: String!
    zipCode: String!
}

type Query {
    companies: [Company]
    companyById(id: ID!) : Company
}

type Mutation {
    newCompany(name: String!, address: String!, zipCode: String!) : Company!
    deleteCompany(id: ID!) : Boolean
}
</pre>
<p>In this simple file, we&#8217;ve declared three types:</p>
<ul>
<li><strong>The Company type</strong>&#8211; which will be associated with the Company entity, we&#8217;ve created earlier</li>
<li><strong>The Query type</strong>&#8211; containing two queries</li>
<li><strong>The Mutation type</strong>&#8211; describing mutations for company creation and removal</li>
</ul>
<h3 class="article-heading-sub">5.2. Describe Employee Schema</h3>
<p>As the next step, let&#8217;s create an <strong>employee.graphqls</strong> file:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">type Employee {
    id: ID!
    firstName: String!
    lastName: String!
    status: EmployeeStatus!
    company: Company!
}

enum EmployeeStatus{
    ACTIVE, RETIRED
}

extend type Query {
    employees: [Employee]
    employeeById(id: ID!) : Employee
}

extend type Mutation {
    newEmployee(firstName: String!, lastName: String!, status: EmployeeStatus!, companyId: ID!) : Employee!
    deleteEmployee(id: ID!) : Boolean
}
</pre>
<p>This file is pretty similar to the previous one, but we&#8217;ve also added the <strong>Enum</strong> type here. Additionally, please notice the <em><strong>extend</strong></em> keyword- we have to place it in order to extend the Mutation and Query declared in another file.</p>
<h2 class="article-heading-main">6. Create Queries Resolvers</h2>
<p>After we&#8217;ve defined the schema, we can start adding the code responsible for data manipulation. Let&#8217;s start by adding the <strong>CompanyQuery</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyQuery(
    private val companyRepository: CompanyRepository
) : GraphQLQueryResolver {

    fun companies(): Set =
        companyRepository.findAll()

    fun companyById(id: Long): Company =
        companyRepository.findById(id)
}
</pre>
<p>Please notice two important things here. Firstly, the <strong>CompanyQuery</strong> class implements the <strong>GraphQLQueryResolver</strong>, which is a markup interface provided by the GraphQL dependency. Secondly, public functions need to <strong>match the schema</strong> in order to be resolved correctly.</p>
<p>Similarly, let&#8217;s implement the <strong>EmployeeQuery</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeQuery(
    private val employeeRepository: EmployeeRepository
) : GraphQLQueryResolver {

    fun employees(): Set =
        employeeRepository.findAll()

    fun employeeById(id: Long): Employee =
        employeeRepository.findById(id)
}
</pre>
<h2 class="article-heading-main">7. Implement Mutations Resolvers</h2>
<p>In order for our application to be fully functional, we need to add the resolvers for our <strong>mutations</strong>. As the first step, let&#8217;s add the <strong>CompanyMutation</strong> class:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class CompanyMutation(
    private val companyRepository: CompanyRepository
) : GraphQLMutationResolver {

    fun newCompany(name: String, address: String, zipCode: String): Company =
        companyRepository.create(name, address, zipCode)

    fun deleteCompany(id: Long): Boolean =
        companyRepository.delete(id)
}
</pre>
<p>Just like in Queries resolvers, public functions <strong>need to match the structure from the defined schema</strong>. The only difference here is that we&#8217;ve implemented the <strong>GraphQLMutationResolver</strong> interface.</p>
<p>Finally, we can add the last class to our project which will be responsible for the employee mutations:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="kotlin">@Component
class EmployeeMutation(
    private val employeeRepository: EmployeeRepository
) : GraphQLMutationResolver {

    fun newEmployee(
        firstName: String,
        lastName: String,
        status: EmployeeStatus,
        companyId: Long
    ): Employee =
        employeeRepository.create(firstName, lastName, status, companyId)

    fun deleteEmployee(id: Long): Boolean =
        employeeRepository.delete(id)

}
</pre>
<h2 class="article-heading-main">8. Testing</h2>
<p>Finally, we can run the application and test it. In this article, I would like to show you the tool called <strong>GraphiQL</strong>&#8211; a dedicated GUI for communicating with GraphQL servers.</p>
<p>In the beginning, we&#8217;ve added the<strong> GraphiQL Spring Boot Starter dependency</strong>, which allows us to run its web-based version under the <em><strong>/graphiql</strong></em> endpoint. However, you can always download it&#8217;s Electron-based wrapper <a href="https://github.com/skevy/graphiql-app" target="_blank" rel="noopener noreferrer">here</a>.</p>
<h3 class="article-heading-sub">8.1. Test Queries</h3>
<p>After starting the application, let&#8217;s head to the <em><strong>http://localhost:8080/graphiql</strong> </em>endpoint and run the following query:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  employees {
    id
    firstName
    lastName
    status
    company {
      name
    }
  }
}
</pre>
<p>As you can see, the query structure allows us to define, which fields we would like to fetch. I highly suggest you check out different combinations and see what will be the results. As a result of the above query, we should see the following output:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "employees": [
      {
        "id": "1",
        "firstName": "John",
        "lastName": "Doe",
        "status": "ACTIVE",
        "company": {
          "name": "Company One"
        }
      }
...
</pre>
<p>Nextly, let&#8217;s try to find the employee by id:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  employeeById(id: 1) {
    id
    firstName
    lastName
    status
    company {
      id
      name
    }
  }
}

# Result:

{
  "data": {
    "employeeById": {
      "id": "1",
      "firstName": "John",
      "lastName": "Doe",
      "status": "ACTIVE",
      "company": {
        "id": "1",
        "name": "Company One"
      }
    }
  }
}
</pre>
<p>Similarly, we can test company queries:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  companies {
    id
    name
    address
    zipCode
  }
}

# Result:

{
  "data": {
    "companies": [
      {
        "id": "1",
        "name": "Company One",
        "address": "Address 1",
        "zipCode": "10001"
      },
...
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">query {
  companyById(id: 1) {
    id
    name
    address
    zipCode
  }
}

# Result:

{
  "data": {
    "companyById": {
      "id": "1",
      "name": "Company One",
      "address": "Address 1",
      "zipCode": "10001"
    }
  }
}
</pre>
<h3 class="article-heading-sub">8.1. Test Mutations</h3>
<p>Testing mutations is quite similar to queries. The only difference is the usage of a <em><strong>mutation</strong></em> keyword:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  newCompany(name: "New", address: "Address new", zipCode: "10201") {
    id
    name
    address
    zipCode
  }
}
</pre>
<p>This time, a new company will be created, and the following data will be returned:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "newCompany": {
      "id": "5",
      "name": "New",
      "address": "Address new",
      "zipCode": "10201"
    }
  }
}
</pre>
<p>Similarly, we can create a new employee:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  newEmployee(firstName: "Piotr", lastName:"Wolak", status: ACTIVE, companyId: 2) {
    id
    firstName
    lastName
    status
    company {
      id
      name
    }
  }
}

# Result:

{
  "data": {
    "newEmployee": {
      "id": "4",
      "firstName": "Piotr",
      "lastName": "Wolak",
      "status": "ACTIVE",
      "company": {
        "id": "2",
        "name": "Company Two"
      }
    }
  }
}
</pre>
<p>Finally, let&#8217;s test delete mutations:</p>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">{
  "data": {
    "deleteCompany": true
  }
}

# Result:

{
  "data": {
    "deleteCompany": true
  }
}
</pre>
<pre class="EnlighterJSRAW" data-enlighter-language="raw">mutation {
  deleteEmployee(id: 1)
}

# Result:

{
  "data": {
    "deleteEmployee": true
  }
}
</pre>
<h2 class="article-heading-main">9. Conclusion</h2>
<p>And that would be all for this tutorial. I really hope that you have learned how to use <strong>GraphQL with Spring Boot and Kotlin</strong> and that testing with <strong>GraphiQL</strong> will be a piece of cake for you.</p>
<p>For the source code, please refer to this <a href="https://github.com/codersee-blog/kotlin-spring-boot-graphql-introduction" target="_blank" rel="noopener noreferrer">GitHub repository</a>.</p>
<p>If you would like to ask about anything or share any feedback with me, I would be more than happy to hear from you. You can always contact me via <a href="https://www.facebook.com/codersee/" target="_blank" rel="noopener noreferrer"><strong>fan page</strong></a>, <a href="https://www.facebook.com/groups/622361565094117" target="_blank" rel="noopener noreferrer"><strong>group</strong></a>, or a <strong><a href="http://codersee.com/contact/" target="_blank" rel="noopener noreferrer">contact</a> </strong>form.</p>
<p>The post <a href="https://blog.codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/">Introduction to GraphQL with Spring Boot and Kotlin</a> appeared first on <a href="https://blog.codersee.com">Codersee blog- Kotlin on the backend</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.codersee.com/introduction-to-graphql-with-spring-boot-and-kotlin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: blog.codersee.com @ 2026-05-12 21:52:59 by W3 Total Cache
-->