diff --git a/spring-boot-modules/spring-boot-libraries-2/pom.xml b/spring-boot-modules/spring-boot-libraries-2/pom.xml
index 04c09754b4..f62a341efc 100644
--- a/spring-boot-modules/spring-boot-libraries-2/pom.xml
+++ b/spring-boot-modules/spring-boot-libraries-2/pom.xml
@@ -57,6 +57,11 @@
${awaitility.version}
test
+
+ io.leangen.graphql
+ spqr
+ 0.11.2
+
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java
new file mode 100644
index 0000000000..c6ff9e515a
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/Book.java
@@ -0,0 +1,57 @@
+package com.baeldung.sprq;
+
+import java.util.Objects;
+
+public class Book {
+ private Integer id;
+ private String author;
+ private String title;
+
+ public Book(Integer id, String author, String title) {
+ this.id = id;
+ this.author = author;
+ this.title = title;
+ }
+
+ public Book() {
+ }
+
+ public Integer getId() {
+ return id;
+ }
+
+ public void setId(Integer id) {
+ this.id = id;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ Book book = (Book) o;
+ return id.equals(book.id);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java
new file mode 100644
index 0000000000..747d52f0af
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookResolver.java
@@ -0,0 +1,42 @@
+package com.baeldung.sprq;
+
+import io.leangen.graphql.annotations.GraphQLArgument;
+import io.leangen.graphql.annotations.GraphQLMutation;
+import io.leangen.graphql.annotations.GraphQLQuery;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+@Service
+public class BookResolver {
+
+ @Autowired
+ IBookService bookService;
+
+ @GraphQLQuery(name = "getBookWithTitle")
+ public Book getBookWithTitle(@GraphQLArgument(name = "title") String title) {
+ return bookService.getBookWithTitle(title);
+ }
+
+ @GraphQLQuery(name = "getAllBooks", description = "Get all books")
+ public List getAllBooks() {
+ return bookService.getAllBooks();
+ }
+
+ @GraphQLMutation(name = "addBook")
+ public Book addBook(@GraphQLArgument(name = "newBook") Book book) {
+ return bookService.addBook(book);
+ }
+
+ @GraphQLMutation(name = "updateBook")
+ public Book updateBook(@GraphQLArgument(name = "modifiedBook") Book book) {
+ return bookService.updateBook(book);
+ }
+
+ @GraphQLMutation(name = "deleteBook")
+ public void deleteBook(@GraphQLArgument(name = "book") Book book) {
+ bookService.deleteBook(book);
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java
new file mode 100644
index 0000000000..6dbfe9c6f9
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/BookService.java
@@ -0,0 +1,48 @@
+package com.baeldung.sprq;
+
+import org.springframework.context.annotation.Profile;
+import org.springframework.stereotype.Service;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+public class BookService implements IBookService {
+
+ Set books = new HashSet<>();
+
+ @Override
+ public Book getBookWithTitle(String title) {
+ return books.stream()
+ .filter(book -> book.getTitle()
+ .equals(title))
+ .findFirst()
+ .orElse(null);
+ }
+
+ @Override
+ public List getAllBooks() {
+ return books.stream()
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public Book addBook(Book book) {
+ books.add(book);
+ return book;
+ }
+
+ @Override
+ public Book updateBook(Book book) {
+ books.remove(book);
+ books.add(book);
+ return book;
+ }
+
+ @Override
+ public boolean deleteBook(Book book) {
+ return books.remove(book);
+ }
+}
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java
new file mode 100644
index 0000000000..b62bdbd6a8
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/GraphqlController.java
@@ -0,0 +1,35 @@
+package com.baeldung.sprq;
+
+import graphql.ExecutionResult;
+import graphql.GraphQL;
+import graphql.GraphQLException;
+import graphql.schema.GraphQLSchema;
+import io.leangen.graphql.GraphQLSchemaGenerator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Profile;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.Map;
+
+@RestController
+public class GraphqlController {
+
+ private final GraphQL graphQL;
+
+ @Autowired
+ public GraphqlController(BookResolver bookResolver) {
+ GraphQLSchema schema = new GraphQLSchemaGenerator().withBasePackages("com.baeldung")
+ .withOperationsFromSingleton(bookResolver)
+ .generate();
+ this.graphQL = new GraphQL.Builder(schema).build();
+ }
+
+ @PostMapping(value = "/graphql")
+ public Map execute(@RequestBody Map request, HttpServletRequest raw) throws GraphQLException {
+ ExecutionResult result = graphQL.execute(request.get("query"));
+ return result.getData();
+ }
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java
new file mode 100644
index 0000000000..1c1257c178
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/main/java/com/baeldung/sprq/IBookService.java
@@ -0,0 +1,15 @@
+package com.baeldung.sprq;
+
+import java.util.List;
+
+public interface IBookService {
+ Book getBookWithTitle(String title);
+
+ List getAllBooks();
+
+ Book addBook(Book book);
+
+ Book updateBook(Book book);
+
+ boolean deleteBook(Book book);
+}
\ No newline at end of file
diff --git a/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java b/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java
new file mode 100644
index 0000000000..65b6ff1e0b
--- /dev/null
+++ b/spring-boot-modules/spring-boot-libraries-2/src/test/java/com/baeldung/sprq/GraphqlControllerIntegrationTest.java
@@ -0,0 +1,60 @@
+package com.baeldung.sprq;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.web.servlet.MockMvc;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest
+@AutoConfigureMockMvc
+public class GraphqlControllerIntegrationTest {
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ BookService bookService;
+
+ private static final String GRAPHQL_PATH = "/graphql";
+
+ @Test
+ public void givenNoBooks_whenReadAll_thenStatusIsOk() throws Exception {
+
+ String getAllBooksQuery = "{\n" + " getAllBooks {\n" + " id\n" + " author\n" + " title\n" + " }\n" + "}\n";
+
+ this.mockMvc.perform(post(GRAPHQL_PATH).content(toJSON(getAllBooksQuery))
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.getAllBooks").isEmpty());
+ }
+
+ @Test
+ public void whenAddBook_thenStatusIsOk() throws Exception {
+
+ String addBookMutation = "mutation {\n" + " addBook(newBook: {id: 123, author: \"J.R.R. Tolkien\", title: \"The Lord of the Rings\"}) {\n" + " id\n" + " author\n" + " title\n" + " }\n" + "}\n";
+
+ this.mockMvc.perform(post(GRAPHQL_PATH).content(toJSON(addBookMutation))
+ .contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andExpect(jsonPath("$.addBook.id").value("123"))
+ .andExpect(jsonPath("$.addBook.author").value("J.R.R. Tolkien"))
+ .andExpect(jsonPath("$.addBook.title").value("The Lord of the Rings"));
+ }
+
+ private String toJSON(String query) throws JSONException {
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("query", query);
+ return jsonObject.toString();
+ }
+}
\ No newline at end of file