{"id":464,"date":"2025-04-12T12:10:34","date_gmt":"2025-04-12T12:10:34","guid":{"rendered":"https:\/\/imcodinggenius.com\/?p=464"},"modified":"2025-04-12T12:10:34","modified_gmt":"2025-04-12T12:10:34","slug":"spring-for-graphql-with-kotlin-coroutines","status":"publish","type":"post","link":"https:\/\/imcodinggenius.com\/?p=464","title":{"rendered":"Spring for GraphQL with Kotlin Coroutines"},"content":{"rendered":"<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>\n<p>Long story short, we will see: <\/p>\n<p>how to prepare a GraphQL schema,<\/p>\n<p>what imports are necessary to utilize suspended functions,<\/p>\n<p>how to expose queries, mutations, and map schema with annotated controllers,<\/p>\n<p>exception handling with GraphQlExceptionHandler <\/p>\n<h2 class=\"wp-block-heading\">Create Project <\/h2>\n<p>As the first step, let\u2019s prepare a brand new project. <\/p>\n<p>To do so, let\u2019s navigate to the <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noopener\">Spring Initializr<\/a> page and select the following imports: <\/p>\n<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>\n<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>\n<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>\n<p>Lastly, let\u2019s download the zip package, extract and import it to our IDE. <\/p>\n<h2 class=\"wp-block-heading\">Define GraphQL Schema<\/h2>\n<h3 class=\"wp-block-heading\">What is GraphQL Schema?<\/h3>\n<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>\n<p>If you haven\u2019t, 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>\n<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>\n<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\u2019s focus on the Spring \/ GraphQL \/ Kotlin combination  <\/p>\n<h3 class=\"wp-block-heading\">schema.graphql in Spring <\/h3>\n<p>As the first step, let\u2019s head to the resources &gt; graphql directory in our project, and let\u2019s insert the schema.graphql file:<\/p>\n<p>type Query {<br \/>\n  article(id: ID!): Article<br \/>\n  articles: [Article!]!<br \/>\n}<\/p>\n<p>type Mutation {<br \/>\n  createArticle(input: CreateArticleInput!): Article!<br \/>\n  addComment(articleId: ID!, input: AddCommentInput!): Comment<br \/>\n}<\/p>\n<p>input CreateArticleInput {<br \/>\n  title: String!<br \/>\n  content: String!<br \/>\n  userId: ID!<br \/>\n}<\/p>\n<p>input AddCommentInput {<br \/>\n  userId: String!<br \/>\n  content: String!<br \/>\n}<\/p>\n<p>type User {<br \/>\n  id: ID!<br \/>\n  name: String!<br \/>\n}<\/p>\n<p>type Article {<br \/>\n  id: ID!<br \/>\n  title: String!<br \/>\n  content: String!<br \/>\n  author: User!<br \/>\n  comments: [Comment!]!<br \/>\n  createdAt: String!<br \/>\n}<\/p>\n<p>type Comment {<br \/>\n  id: ID!<br \/>\n  content: String!<br \/>\n  author: User<br \/>\n}<\/p>\n<p>In Spring Boot, this file is registered automatically as our schema definition. <\/p>\n<p>And as we can see, we defined 5 things: <\/p>\n<p><strong>queries<\/strong>\u2013 how a client reads data. Every GraphQL schema <strong>must <\/strong>support them,<\/p>\n<p><strong>mutations<\/strong>\u2013 how a client modifies data,     <\/p>\n<p><strong>inputs<\/strong>\u2013 used to pass structured arguments to our mutations and queries,<\/p>\n<p><strong>types<\/strong>\u2013 establishing the structure of data we return,<\/p>\n<p><strong>scalars<\/strong>\u2013 like String, or ID (but also Int, Float, Boolean), describing returned fields.<\/p>\n<p>Moreover, we can see the exclamation mark- !. In contrast to Kotlin, in GraphQL, we must <strong>explicitly define non-nullable types.<\/strong> Meaning, that [Comment] describes a nullable array of nullable Comment type (Array&lt;Comment?&gt;?), whereas [Comment!]! is a not null array with not null items.<\/p>\n<h3 class=\"wp-block-heading\">Spring GraphQL Schema Inspection<\/h3>\n<p>Following, let\u2019s rerun our application. <\/p>\n<p>As a result, we should get the following in logs: <\/p>\n<p>o.s.b.a.g.GraphQlAutoConfiguration: GraphQL schema inspection:<br \/>\nUnmapped fields: {Query=[article, articles], Mutation=[createArticle, addComment]}<br \/>\nUnmapped registrations: {}<br \/>\nUnmapped arguments: {}<br \/>\nSkipped types: []<\/p>\n<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>\n<p>Let\u2019s fix that then  <\/p>\n<h2 class=\"wp-block-heading\">Create Models <\/h2>\n<p>As the next step, let\u2019s 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>\n<p>To do so, let\u2019s add the Models.kt:<\/p>\n<p>data class User(<br \/>\n    val id: String,<br \/>\n    val name: String,<br \/>\n)<\/p>\n<p>data class Article(<br \/>\n    val id: String,<br \/>\n    val title: String,<br \/>\n    val content: String,<br \/>\n    val authorId: String,<br \/>\n    val createdAt: String,<br \/>\n)<\/p>\n<p>data class Comment(<br \/>\n    val id: String,<br \/>\n    val content: String,<br \/>\n    val articleId: String,<br \/>\n    val userId: String,<br \/>\n)<\/p>\n<p>data class CreateArticleInput(<br \/>\n    val title: String,<br \/>\n    val content: String,<br \/>\n    val userId: String,<br \/>\n)<\/p>\n<p>data class AddCommentInput(<br \/>\n    val content: String,<br \/>\n    val userId: String,<br \/>\n)<\/p>\n<p>As we can see, nothing spectacular here. The only interesting fact is that the ID is serialized in the same way as a String.<\/p>\n<h2 class=\"wp-block-heading\">QueryMapping as suspend fun<\/h2>\n<p>Following, let\u2019s learn how the Spring for GraphQL works with Kotlin coroutines. <\/p>\n<p>According to the documentation:<\/p>\n<p>Kotlin coroutine and Flow are adapted to Mono and Flux.<\/p>\n<p>Which means that whenever we use the Spring WebFlux, <strong>Spring automatically converts our suspended functions Mono and functions that return Flow to Flux<\/strong> .<\/p>\n<h3 class=\"wp-block-heading\">Implement Article Service<\/h3>\n<p>Before we head to the GraphQL part, let\u2019s make some small preparations.<\/p>\n<p>Let\u2019s insert the ArticleService:<\/p>\n<p>@Component<br \/>\nobject ArticleService {<\/p>\n<p>    private val articles = mutableListOf(<br \/>\n        Article(<br \/>\n            id = &#171;article-id-1&#187;,<br \/>\n            title = &#171;article-title-1&#187;,<br \/>\n            content = &#171;article-content-1&#187;,<br \/>\n            authorId = &#171;user-id-2&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n        Article(<br \/>\n            id = &#171;article-id-2&#187;,<br \/>\n            title = &#171;article-title-2&#187;,<br \/>\n            content = &#171;article-content-2&#187;,<br \/>\n            authorId = &#171;user-id-2&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n        Article(<br \/>\n            id = &#171;article-id-3&#187;,<br \/>\n            title = &#171;article-title-3&#187;,<br \/>\n            content = &#171;article-content-3&#187;,<br \/>\n            authorId = &#171;user-id-3&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n    )<\/p>\n<p>    suspend fun findArticleById(id: String): Article? {<br \/>\n        delay(100)<br \/>\n        return articles.firstOrNull { it.id == id }<br \/>\n    }<\/p>\n<p>}<\/p>\n<p>As we can see, we use my favorite, in-memory database called <strong>mutable list<\/strong> () and expose findArticleById \u2013 a suspended function.<\/p>\n<p>This function will either find the desired article by ID or return a null value.<\/p>\n<h3 class=\"wp-block-heading\">Add ArticleController<\/h3>\n<p>With that done, let\u2019s add the ArticleController:<\/p>\n<p>@Controller<br \/>\nclass ArticleController(<br \/>\n    private val articleService: ArticleService,<br \/>\n) {<\/p>\n<p>    @QueryMapping<br \/>\n    suspend fun article(@Argument id: String): Article? =<br \/>\n        articleService.findArticleById(id)<\/p>\n<p>}<\/p>\n<p>As we can see, thanks to the Spring GraphQL, we can leverage the annotation-based approach.<\/p>\n<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>\n<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>\n<p>Additionally, in Spring, we use the <strong>@Argument<\/strong> annotation to bind GraphQL input arguments to our Kotlin instances. <\/p>\n<h3 class=\"wp-block-heading\">Enable GraphiQL &amp; Test<\/h3>\n<p>Nextly, let\u2019s turn on the <strong>GraphiQL<\/strong>\u2013 the IDE for testing GraphQL APIs (like Postman, Bruno, or Insomnia).<\/p>\n<p>To do so, let\u2019s navigate to the resources directory and change the application.properties into the application.yaml file:<\/p>\n<p>spring:<br \/>\n  application:<br \/>\n    name: graphsandbox<br \/>\n  graphql:<br \/>\n    graphiql:<br \/>\n      enabled: true<\/p>\n<p>Then, let\u2019s open up the browser, go to http:\/\/localhost:8080\/graphiql, and put the following: <\/p>\n<p>query SomeRandomQuery {<br \/>\n  article(id: &#171;article-id-1&#187;) {<br \/>\n    id<br \/>\n    title<br \/>\n  }<br \/>\n}<\/p>\n<p>As we can see, the <strong>query<\/strong> name is up to us, but inside it, we must define which of the \u201cexposed\u201d queries we would like to use. As the input parameter, we pass the String value- article-id-1\u2013 and lastly, we define what fields we would like to get in response (that\u2019s what the whole GraphQL is a about, right?).<\/p>\n<p>So, as the next step, let\u2019s run the query and check out the result: <\/p>\n<p>{<br \/>\n  &#171;errors&#187;: [<br \/>\n    {<br \/>\n      &#171;message&#187;: &#171;INTERNAL_ERROR for f337f3f3-5&#187;,<br \/>\n      &#171;locations&#187;: [<br \/>\n        {<br \/>\n          &#171;line&#187;: 2,<br \/>\n          &#171;column&#187;: 3<br \/>\n        }<br \/>\n      ],<br \/>\n      &#171;path&#187;: [<br \/>\n        &#171;article&#187;<br \/>\n      ],<br \/>\n      &#171;extensions&#187;: {<br \/>\n        &#171;classification&#187;: &#171;INTERNAL_ERROR&#187;<br \/>\n      }<br \/>\n    }<br \/>\n  ],<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;article&#187;: null<br \/>\n  }<br \/>\n}<\/p>\n<p>Unfortunately, that is not what we expected. <\/p>\n<p>Moreover, when we check out logs, we should see this:<\/p>\n<p>s.g.e.ExceptionResolversExceptionHandler : Unresolved NoClassDefFoundError for executionId f337f3f3-5<br \/>\njava.lang.NoClassDefFoundError: org\/springframework\/data\/util\/KotlinReflectionUtils<br \/>\n\tat org.springframework.graphql.data.method.InvocableHandlerMethodSupport.invokeSuspendingFunction(InvocableHandlerMethodSupport.java:141) ~[spring-graphql-1.3.4.jar:1.3.4]<br \/>\n\tat org.springframework.graphql.data.method.InvocableHandlerMethodSupport.doInvoke(InvocableHandlerMethodSupport.java:108) ~[spring-graphql-1.3.4.jar:1.3.4]<\/p>\n<h3 class=\"wp-block-heading\">Fix Spring GraphQL Coroutines Issue<\/h3>\n<p>As we could see, the issue is caused by the missing dependency, and we can easily fix that.<\/p>\n<p>To do so, let\u2019s open up the build.gradle.kts and add the following dependency:<\/p>\n<p>implementation(&#171;org.springframework.data:spring-data-commons&#187;)<\/p>\n<p>Then, let\u2019s sync the gradle project and rerun the application.<\/p>\n<p>After we run the test again, we should see the following:<\/p>\n<p>{<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;article&#187;: {<br \/>\n      &#171;id&#187;: &#171;article-id-1&#187;,<br \/>\n      &#171;title&#187;: &#171;article-title-1&#187;<br \/>\n    }<br \/>\n  }<br \/>\n}<\/p>\n<p>Splendid! Everything works perfectly fine<\/p>\n<h2 class=\"wp-block-heading\">Spring GraphQL with Kotlin Flow <\/h2>\n<p>With that done, let\u2019s learn how to work with Kotlin Flow and Spring GraphQL.<\/p>\n<p>As the first step, let\u2019s insert a new function to our ArticleService:<\/p>\n<p>fun findAllArticles(): Flow&lt;Article&gt; = articles.asFlow()<\/p>\n<p>This way, we produce a <strong>cold flow<\/strong> from our list of articles.<\/p>\n<p>Then, let\u2019s add a new handler:<\/p>\n<p>@QueryMapping<br \/>\nfun articles(): Flow&lt;Article&gt; =<br \/>\n    articleService.findAllArticles()<\/p>\n<p>Nothing new this time. Just like previously, we add a new function marked with <strong>@QueryMapping<\/strong>.<\/p>\n<p>When we run the following query in GraphiQL: <\/p>\n<p>query AnotherOne {<br \/>\n  articles {<br \/>\n    id<br \/>\n    title<br \/>\n    createdAt<br \/>\n  }<br \/>\n}<\/p>\n<p>We should see the exact result: <\/p>\n<p>{<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;articles&#187;: [<br \/>\n      {<br \/>\n        &#171;id&#187;: &#171;article-id-1&#187;,<br \/>\n        &#171;title&#187;: &#171;article-title-1&#187;,<br \/>\n        &#171;createdAt&#187;: &#171;2025-04-12T13:06:29.142469200&#187;<br \/>\n      },<br \/>\n      {<br \/>\n        &#171;id&#187;: &#171;article-id-2&#187;,<br \/>\n        &#171;title&#187;: &#171;article-title-2&#187;,<br \/>\n        &#171;createdAt&#187;: &#171;2025-04-12T13:06:29.142469200&#187;<br \/>\n      },<br \/>\n      {<br \/>\n        &#171;id&#187;: &#171;article-id-3&#187;,<br \/>\n        &#171;title&#187;: &#171;article-title-3&#187;,<br \/>\n        &#171;createdAt&#187;: &#171;2025-04-12T13:06:29.142469200&#187;<br \/>\n      }<br \/>\n    ]<br \/>\n  }<br \/>\n}<\/p>\n<h2 class=\"wp-block-heading\">@SchemaMapping<\/h2>\n<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>\n<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>\n<p>But when we test that functionality right now:<\/p>\n<p>query AnotherOne {<br \/>\n  articles {<br \/>\n    id<br \/>\n    title<br \/>\n    createdAt<br \/>\n    author {<br \/>\n      id<br \/>\n      name<br \/>\n    }<\/p>\n<p>    comments {<br \/>\n      id<br \/>\n      content<br \/>\n      author {<br \/>\n        name<br \/>\n      }<br \/>\n    }<br \/>\n  }<br \/>\n}<\/p>\n<p>The only thing we will get in response will be a looooong list of errors:<\/p>\n<p>{<br \/>\n  &#171;errors&#187;: [<br \/>\n    {<br \/>\n      &#171;message&#187;: &#171;The field at path &#8216;\/articles[0]\/author&#8217; 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 &#8216;User&#8217; within parent type &#8216;Article'&#187;,<br \/>\n      &#171;path&#187;: [<br \/>\n        &#171;articles&#187;,<br \/>\n        0,<br \/>\n        &#171;author&#187;<br \/>\n      ],<br \/>\n      &#171;extensions&#187;: {<br \/>\n        &#171;classification&#187;: &#171;NullValueInNonNullableField&#187;<br \/>\n      }<br \/>\n    }<\/p>\n<p>&#8230; other errors<\/p>\n<p>Of course, with Spring we can easily fix it, but we will need small preparation first.<\/p>\n<h3 class=\"wp-block-heading\">Update Service<\/h3>\n<p>Firstly, let\u2019s get back to our service and update it: <\/p>\n<p>@Component<br \/>\nobject ArticleService {<\/p>\n<p>    private val articles = mutableListOf(<br \/>\n        Article(<br \/>\n            id = &#171;article-id-1&#187;,<br \/>\n            title = &#171;article-title-1&#187;,<br \/>\n            content = &#171;article-content-1&#187;,<br \/>\n            authorId = &#171;user-id-2&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n        Article(<br \/>\n            id = &#171;article-id-2&#187;,<br \/>\n            title = &#171;article-title-2&#187;,<br \/>\n            content = &#171;article-content-2&#187;,<br \/>\n            authorId = &#171;user-id-2&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n        Article(<br \/>\n            id = &#171;article-id-3&#187;,<br \/>\n            title = &#171;article-title-3&#187;,<br \/>\n            content = &#171;article-content-3&#187;,<br \/>\n            authorId = &#171;user-id-3&#187;,<br \/>\n            createdAt = LocalDateTime.now().toString()<br \/>\n        ),<br \/>\n    )<\/p>\n<p>    private val users = mutableListOf(<br \/>\n        User(id = &#171;user-id-1&#187;, name = &#171;user-name-1&#187;),<br \/>\n        User(id = &#171;user-id-2&#187;, name = &#171;user-name-2&#187;),<br \/>\n        User(id = &#171;user-id-3&#187;, name = &#171;user-name-3&#187;),<br \/>\n    )<\/p>\n<p>    private val comments = mutableListOf(<br \/>\n        Comment(id = &#171;comment-id-1&#187;, content = &#171;comment-content-1&#187;, articleId = &#171;article-id-1&#187;, userId = &#171;user-id-3&#187;),<br \/>\n        Comment(id = &#171;comment-id-2&#187;, content = &#171;comment-content-2&#187;, articleId = &#171;article-id-2&#187;, userId = &#171;user-id-3&#187;),<br \/>\n        Comment(id = &#171;comment-id-3&#187;, content = &#171;comment-content-3&#187;, articleId = &#171;article-id-2&#187;, userId = &#171;user-id-2&#187;),<br \/>\n        Comment(id = &#171;comment-id-4&#187;, content = &#171;comment-content-4&#187;, articleId = &#171;article-id-3&#187;, userId = &#171;user-id-3&#187;),<br \/>\n    )<\/p>\n<p>    suspend fun findArticleById(id: String): Article? {<br \/>\n        delay(100)<br \/>\n        return articles.firstOrNull { it.id == id }<br \/>\n    }<\/p>\n<p>    fun findAllArticles(): Flow&lt;Article&gt; = articles.asFlow()<\/p>\n<p>    suspend fun findUserById(id: String): User? {<br \/>\n        delay(100)<br \/>\n        return users.firstOrNull { it.id == id }<br \/>\n    }<\/p>\n<p>    fun findCommentsByArticleId(id: String): Flow&lt;Comment&gt; =<br \/>\n        comments.filter { it.articleId == id }.asFlow()<br \/>\n}<\/p>\n<p>As we can see, we added two more \u201ctables\u201d along with simple functions to obtain data from them. <\/p>\n<h3 class=\"wp-block-heading\">Update @Controller<\/h3>\n<p>Then, let\u2019s update our controller class:<\/p>\n<p>@SchemaMapping<br \/>\nsuspend fun author(article: Article): User =<br \/>\n    articleService.findUserById(article.authorId)!!<\/p>\n<p>@SchemaMapping<br \/>\nsuspend fun author(comment: Comment): User =<br \/>\n    articleService.findUserById(comment.userId)!!<\/p>\n<p>@SchemaMapping<br \/>\nfun comments(article: Article): Flow&lt;Comment&gt; =<br \/>\n    articleService.findCommentsByArticleId(article.id)<\/p>\n<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>\n<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 article -&gt; author parent-child relationship, we define the function that takes the article instance as an argument. And that\u2019s it!<\/p>\n<p>Of course, please the double exclamation (!!) should not land in the real, production-ready code<\/p>\n<p>Anyway, when we rerun our test now, we will see the following: <\/p>\n<p>{<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;articles&#187;: [<br \/>\n      {<br \/>\n        &#171;id&#187;: &#171;article-id-1&#187;,<br \/>\n        &#171;title&#187;: &#171;article-title-1&#187;,<br \/>\n        &#171;createdAt&#187;: &#171;2025-04-12T13:21:17.584507600&#187;,<br \/>\n        &#171;author&#187;: {<br \/>\n          &#171;id&#187;: &#171;user-id-2&#187;,<br \/>\n          &#171;name&#187;: &#171;user-name-2&#187;<br \/>\n        },<br \/>\n        &#171;comments&#187;: [<br \/>\n          {<br \/>\n            &#171;id&#187;: &#171;comment-id-1&#187;,<br \/>\n            &#171;content&#187;: &#171;comment-content-1&#187;,<br \/>\n            &#171;author&#187;: {<br \/>\n              &#171;name&#187;: &#171;user-name-3&#187;<br \/>\n            }<br \/>\n          }<br \/>\n        ]<br \/>\n      }<\/p>\n<p>&#8230; more thingies <\/p>\n<p>And that\u2019s what we expected. Awesome!<\/p>\n<h2 class=\"wp-block-heading\">GraphQL Mutations <\/h2>\n<p>When we run the application right now, we see that we still miss two things that we defined- <strong>mutations<\/strong>.<\/p>\n<p>So, before we add a new handler, let\u2019s implement the following function in our service:<\/p>\n<p>suspend fun createArticle(input: CreateArticleInput): Article {<br \/>\n    delay(100)<\/p>\n<p>    return Article(<br \/>\n        id = UUID.randomUUID().toString(),<br \/>\n        title = input.title,<br \/>\n        content = input.content,<br \/>\n        authorId = input.userId,<br \/>\n        createdAt = LocalDateTime.now().toString(),<br \/>\n    ).also { articles.add(it) }<br \/>\n}<\/p>\n<p>As can be seen, we take the input, create a new Article instance, and insert that to the list.<\/p>\n<p>With Kotlin also ,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: .also(articles::add)  <\/p>\n<p>Then, let\u2019s add the appropriate handler to our controller:<\/p>\n<p>@MutationMapping<br \/>\nsuspend fun createArticle(@Argument input: CreateArticleInput): Article =<br \/>\n    articleService.createArticle(input)<\/p>\n<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>\n<p>So, given that, let\u2019s rerun the app and test our functionality:<\/p>\n<p>mutation someMutation {<br \/>\n  createArticle(<br \/>\n    input: {<br \/>\n      title: &#171;Awesome codersee Kotlin article&#187;,<br \/>\n      content: &#171;Some content&#187;,<br \/>\n      userId: &#171;user-id-3&#187;<br \/>\n    }<br \/>\n  ) {<br \/>\n    id<br \/>\n    title<br \/>\n    content<br \/>\n    author {<br \/>\n      id<br \/>\n      name<br \/>\n    }<br \/>\n  }<br \/>\n}<\/p>\n<p>As a result, we should get the following:<\/p>\n<p>{<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;createArticle&#187;: {<br \/>\n      &#171;id&#187;: &#171;0e51a902-835b-4e55-9b4a-f2dccd242ea7&#187;,<br \/>\n      &#171;title&#187;: &#171;Awesome codersee Kotlin article&#187;,<br \/>\n      &#171;content&#187;: &#171;Some content&#187;,<br \/>\n      &#171;author&#187;: {<br \/>\n        &#171;id&#187;: &#171;user-id-3&#187;,<br \/>\n        &#171;name&#187;: &#171;user-name-3&#187;<br \/>\n      }<br \/>\n    }<br \/>\n  }<br \/>\n}<\/p>\n<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>\n<h2 class=\"wp-block-heading\">Error Handling in Spring GraphQL<\/h2>\n<p>As the last step, let\u2019s add one more mutation to learn more about <strong>error handling<\/strong>.<\/p>\n<p>First, let\u2019s add the new custom exception ot our codebase in Models.kt:<\/p>\n<p>data class GenericNotFound(val msg: String) : RuntimeException(msg)<\/p>\n<p>Then, let\u2019s implement the following function in ArticleService:<\/p>\n<p>suspend fun addComment(id: String, input: AddCommentInput): Comment {<br \/>\n        delay(100)<\/p>\n<p>        users.firstOrNull { it.id == input.userId }<br \/>\n            ?: throw GenericNotFound(&#171;User not found&#187;)<\/p>\n<p>        return articles.firstOrNull { it.id == id }<br \/>\n            ?.let {<br \/>\n                Comment(<br \/>\n                    id = UUID.randomUUID().toString(),<br \/>\n                    articleId = id,<br \/>\n                    content = input.content,<br \/>\n                    userId = input.userId,<br \/>\n                ).also { comments.add(it) }<br \/>\n            }<br \/>\n            ?: throw GenericNotFound(&#171;Article not found&#187;)<\/p>\n<p>    }<\/p>\n<p>This time, we can see a more production-ready code.<\/p>\n<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>\n<p>If that is not the case, then we throw our custom exception.<\/p>\n<p>Following, let\u2019s add a new mutation handler: <\/p>\n<p>@MutationMapping<br \/>\nsuspend fun addComment(<br \/>\n    @Argument id: String,<br \/>\n    @Argument input: AddCommentInput,<br \/>\n): Comment =<br \/>\n    articleService.addComment(id, input)<\/p>\n<p>Now, when we rerun the app and execute the following test: <\/p>\n<p>mutation anotherMutation {<br \/>\n  addComment(<br \/>\n    articleId: &#171;non-existing&#187;<br \/>\n    input: {userId: &#171;user-id-1&#187;, content: &#171;My comment&#187;}<br \/>\n  ) {<br \/>\n    id<br \/>\n    content<br \/>\n  }<br \/>\n}<\/p>\n<p>We will see the following error: <\/p>\n<p>{<br \/>\n  &#171;errors&#187;: [<br \/>\n    {<br \/>\n      &#171;message&#187;: &#171;INTERNAL_ERROR for f62a4c7e-1&#187;,<br \/>\n      &#171;locations&#187;: [<br \/>\n        {<br \/>\n          &#171;line&#187;: 52,<br \/>\n          &#171;column&#187;: 3<br \/>\n        }<br \/>\n      ],<br \/>\n      &#171;path&#187;: [<br \/>\n        &#171;addComment&#187;<br \/>\n      ],<br \/>\n      &#171;extensions&#187;: {<br \/>\n        &#171;classification&#187;: &#171;INTERNAL_ERROR&#187;<br \/>\n      }<br \/>\n    }<br \/>\n  ],<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;addComment&#187;: null<br \/>\n  }<br \/>\n}<\/p>\n<p>And this is not what we wanted, right? <\/p>\n<h3 class=\"wp-block-heading\">GraphQlExceptionHandler and ControllerAdvice<\/h3>\n<p>One of the solutions we can have in such a situation is a combination of ControllerAdvice and GraphQlExceptionHandler.<\/p>\n<p>If you would like to learn more about ControllerAdvice and RestControllerAdvice, then check out my <a href=\"https:\/\/codersee.com\/controlleradvice-vs-restcontrolleradvice\/\">other article<\/a>.<\/p>\n<p>So, as the next step, let\u2019s add the GlobalExceptionHandler class to the project:<\/p>\n<p>@ControllerAdvice<br \/>\nclass GlobalExceptionHandler {<\/p>\n<p>    @GraphQlExceptionHandler<br \/>\n    fun handleGenericNotFound(ex: GenericNotFound): GraphQLError =<br \/>\n        GraphQLError.newError()<br \/>\n            .errorType(ErrorType.DataFetchingException)<br \/>\n            .message(ex.msg)<br \/>\n            .build()<br \/>\n}<\/p>\n<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>\n<p>As a result, when we retest the application, we should get the following JSON:<\/p>\n<p>{<br \/>\n  &#171;errors&#187;: [<br \/>\n    {<br \/>\n      &#171;message&#187;: &#171;Article not found&#187;,<br \/>\n      &#171;locations&#187;: [],<br \/>\n      &#171;extensions&#187;: {<br \/>\n        &#171;classification&#187;: &#171;DataFetchingException&#187;<br \/>\n      }<br \/>\n    }<br \/>\n  ],<br \/>\n  &#171;data&#187;: {<br \/>\n    &#171;addComment&#187;: null<br \/>\n  }<br \/>\n}<\/p>\n<h2 class=\"wp-block-heading\">Spring GraphQL with Kotlin Coroutines Summary<\/h2>\n<p>And that\u2019s all for this article on how to work with Kotlin coroutines and Flows in the Spring Boot GraphQL project.<\/p>\n<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=\"noopener\">GitHub repostory<\/a>.<\/p>\n<p>The post <a href=\"https:\/\/codersee.com\/spring-graphql-kotlin-coroutines\/\">Spring for GraphQL with Kotlin Coroutines<\/a> appeared first on <a href=\"https:\/\/codersee.com\/\">Codersee &#8212; Kotlin on the backend<\/a>.<\/p>","protected":false},"excerpt":{"rendered":"<p>At the end of this article, you will know precisely how to create a Spring Boot GraphQL application that exposes queries and mutations with the help of Kotlin suspended functions and Flows. Long story short, we will see: how to prepare a GraphQL schema, what imports are necessary to utilize &#8230; <\/p>\n<div><a class=\"more-link bs-book_btn\" href=\"https:\/\/imcodinggenius.com\/?p=464\">Read More<\/a><\/div>\n","protected":false},"author":0,"featured_media":465,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-464","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-news"],"_links":{"self":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts\/464"}],"collection":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=464"}],"version-history":[{"count":0,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/posts\/464\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=\/wp\/v2\/media\/465"}],"wp:attachment":[{"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=464"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=464"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/imcodinggenius.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=464"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}