Microservices With GraphQL
dzone.com/articles/microservices-with-graphql
Like (17)
Comment (6)
Save
Tweet
GraphQL is an API that was invented and open sourced by Facebook as a better
replacement for REST. It can be understood as Querby language for APIs, which enables
declarative data fetching by exposing a single endpoint and responds to queries. In REST,
there is always a dedicated endpoint for each type of request and can't be customized.
In GraphQL, the client decides what data they need and that's the reason the client sends
a query (payload) to the server and the server sends the data back as per the query
request. There is where they get the name GraphQL
Let's look at an example to understand the technical details. In this example, we will build
a simple book store application using graphQL.
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.dugu.acc.dev</groupId>
<artifactId>spring-graphql</artifactId>
<version>0.0.1-SNAPSHOT</version>
1/25
<packaging>jar</packaging>
<name>spring-graphql</name>
<description>GraphQL is invented by Facebook as a better replacement of REST
for Web APIs</description>
10
<parent>
11
<groupId>org.springframework.boot</groupId>
12
<artifactId>spring-boot-starter-parent</artifactId>
13
<version>1.5.9.RELEASE</version>
14
<relativePath />
15
</parent>
16
<properties>
17
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
19
<java.version>1.8</java.version>
20
</properties>
21
2/25
<dependencies>
22
<dependency>
23
<groupId>org.springframework.boot</groupId>
24
<artifactId>spring-boot-starter-data-jpa</artifactId>
25
</dependency>
26
<dependency>
27
<groupId>org.springframework.boot</groupId>
28
<artifactId>spring-boot-starter-web</artifactId>
29
</dependency>
30
<dependency>
31
<groupId>org.springframework.boot</groupId>
32
<artifactId>spring-boot-devtools</artifactId>
33
<scope>runtime</scope>
34
</dependency>
35
3/25
<dependency>
36
<groupId>com.h2database</groupId>
37
<artifactId>h2</artifactId>
38
<scope>runtime</scope>
39
</dependency>
40
<dependency>
41
<groupId>org.projectlombok</groupId>
42
<artifactId>lombok</artifactId>
43
<optional>true</optional>
44
</dependency>
45
<dependency>
46
<groupId>com.graphql-java</groupId>
47
<artifactId>graphql-spring-boot-starter</artifactId>
48
<version>3.6.0</version>
49
4/25
</dependency>
50
<dependency>
51
<groupId>com.graphql-java</groupId>
52
<artifactId>graphql-java-tools</artifactId>
53
<version>3.2.0</version>
54
</dependency>
55
<dependency>
56
<groupId>org.springframework.boot</groupId>
57
<artifactId>spring-boot-starter-test</artifactId>
58
<scope>test</scope>
59
</dependency>
60
</dependencies>
61
<build>
62
<plugins>
63
5/25
<plugin>
64
<groupId>org.springframework.boot</groupId>
65
<artifactId>spring-boot-maven-plugin</artifactId>
66
</plugin>
67
</plugins>
68
</build>
69
</project>
The GraphQL schema is the main concept of GraphQL, which is based on SDL (Schema
Definition language). We can define the simple types as we can see 'type Book' in below
example, and relations as well (for example, 'type Query' has a relation with Book). The
relationship could be one to one, one to many, many to one, and many to many. In the
below example, 'type Query' has a one to many (allBooks) and a one to one (Book)
relationship. The schema is playing the major role to fetch the data.
The below file is under the src/main/resource folder of my GitHub repo (liked to at the
end of the article).
book.schema
schema{
query: Query
6/25
5
type Query{
allBooks: [Book]
Book(id: String): Book
10
11
type Book{
12
bookId: String
13
bookName: String
14
publishedDate: String
15
writer: [String]
16
publisher: String
17
BookSearchController.java
7/25
1
package com.arun.spring.graphql.api.controller;
import java.io.File;
import java.io.IOException;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
10
import org.springframework.beans.factory.annotation.Value;
11
import org.springframework.core.io.Resource;
12
import org.springframework.http.HttpStatus;
13
import org.springframework.http.ResponseEntity;
14
import org.springframework.web.bind.annotation.GetMapping;
8/25
15
import org.springframework.web.bind.annotation.PathVariable;
16
import org.springframework.web.bind.annotation.PostMapping;
17
import org.springframework.web.bind.annotation.RequestBody;
18
import org.springframework.web.bind.annotation.RequestMapping;
19
import org.springframework.web.bind.annotation.RestController;
20
21
import com.arun.spring.graphql.api.datafetcher.BookDataFetcher;
22
import com.arun.spring.graphql.api.datafetcher.AllBookDataFetcher;
23
import com.arun.spring.graphql.api.entity.Book;
24
import com.arun.spring.graphql.api.service.BookService;
25
26
import graphql.ExecutionResult;
27
import graphql.GraphQL;
28
import graphql.schema.GraphQLSchema;
9/25
29
import graphql.schema.idl.RuntimeWiring;
30
import graphql.schema.idl.SchemaGenerator;
31
import graphql.schema.idl.SchemaParser;
32
import graphql.schema.idl.TypeDefinitionRegistry;
33
34
@RestController
35
@RequestMapping("/bookstore")
36
public class BookSearchController {
37
@Autowired
38
private BookService service;
39
// load graphqls file
40
@Value("classpath:book.schema")
41
private Resource schemaResource;
42
@Autowired
10/25
43
private AllBookDataFetcher allBookDataFetcher;
44
@Autowired
45
private BookDataFetcher bookDataFetcher;
46
47
private GraphQL graphQL;
48
49
// load schema at application start up
50
@PostConstruct
51
public void loadSchema() throws IOException {
52
// get the schema
53
File schemaFile = schemaResource.getFile();
54
// parse schema
55
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
56
RuntimeWiring wiring = buildRuntimeWiring();
11/25
57
GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry,
wiring);
58
graphQL = GraphQL.newGraphQL(schema).build();
59
60
61
private RuntimeWiring buildRuntimeWiring() {
62
return RuntimeWiring.newRuntimeWiring().type("Query", typeWiring -> typeWiring
63
.dataFetcher("allBooks", allBookDataFetcher).dataFetcher("book",
bookDataFetcher)).build();
64
65
66
@GetMapping("/booksList")
67
public List < Book > getBooksList() {
68
return service.findAllBooks();
69
70
12/25
71
/*
72
* In PostMan use Post URL: localhost:8080/bookstore/getAllBooks
73
* and Body: query{
74
allBooks{
75
bookId,
76
bookName
77
78
79
*/
80
@PostMapping("/getAllBooks")
81
public ResponseEntity < Object > getAllBooks(@RequestBody String query) {
82
ExecutionResult result = graphQL.execute(query);
83
return new ResponseEntity < Object > (result, HttpStatus.OK);
84
13/25
}
85
86
@GetMapping("/search/{bookId}")
87
public Book getBookInfo(@PathVariable String movieId) {
88
return service.findBookById(movieId);
89
90
91
@PostMapping("/getBookById")
92
public ResponseEntity < Object > getBookById(@RequestBody String query) {
93
ExecutionResult result = graphQL.execute(query);
94
return new ResponseEntity < Object > (result, HttpStatus.OK);
95
96
AllBookDataFetcher.java
14/25
package com.arun.spring.graphql.api.datafetcher;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.arun.spring.graphql.api.entity.Book;
import com.arun.spring.graphql.api.repository.BookRepository;
10
11
import graphql.schema.DataFetcher;
12
import graphql.schema.DataFetchingEnvironment;
13
14
@Component
15
15/25
public class AllBookDataFetcher implements DataFetcher < List < Book >> {
16
@Autowired
17
private BookRepository repository;
18
19
@Override
20
public List < Book > get(DataFetchingEnvironment environment) {
21
return repository.findAll();
22
23
BookDataFetcher.java
package com.arun.spring.graphql.api.datafetcher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
16/25
6
import com.arun.spring.graphql.api.entity.Book;
import com.arun.spring.graphql.api.repository.BookRepository;
import graphql.schema.DataFetcher;
10
import graphql.schema.DataFetchingEnvironment;
11
12
@Component
13
public class BookDataFetcher implements DataFetcher < Book > {
14
@Autowired
15
private BookRepository repository;
16
17
@Override
18
public Book get(DataFetchingEnvironment environment) {
19
17/25
String movieId = environment.getArgument("id");
20
return repository.findOne(movieId);
21
22
Book.java
package com.arun.spring.graphql.api.entity;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.ToString;
10
18/25
11
@ToString
12
@AllArgsConstructor
13
@NoArgsConstructor
14
@Table
15
@Entity
16
public class Book {
17
@Id
18
private String bookId;
19
private String bookName;
20
private String publishedDate;
21
private String[] writer;
22
private String publisher;
23
19/25
BookRepository.java
package com.arun.spring.graphql.api.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.arun.spring.graphql.api.entity.Book;
public interface BookRepository extends JpaRepository<Book, String> {
BookService.java
package com.arun.spring.graphql.api.service;
import java.util.*;
20/25
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.arun.spring.graphql.api.entity.Book;
10
import com.arun.spring.graphql.api.repository.BookRepository;
11
12
@Service
13
public class BookService {
14
@Autowired
15
private BookRepository repository;
16
17
@PostConstruct
18
public void initBooks() {
19
21/25
List < Book > books = new ArrayList < > ();
20
books.add(new Book("101", "The Science of Marvel",
21
"22-12-2017", new String[] {
22
"Sebastian"
23
},
24
"Infinity Stones"));
25
books.add(new Book("102", "The Sixth Extinction",
26
"22-12-2017", new String[] {
27
"Sebastian",
28
"Elizabeth"
29
},
30
"Infinity Stones"));
31
books.add(new Book("103", "The Science of Marvel -2",
32
"22-12-2019", new String[] {
33
22/25
"Sebastian"
34
},
35
"Infinity Stones"));
36
repository.save(books);
37
38
39
public List < Book > findAllBooks() {
40
return repository.findAll();
41
42
43
public Book findBookById(String movieId) {
44
return repository.findOne(movieId);
45
46
23/25
SpringGraphqlApplication.java
package com.arun.spring.graphql.api;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringGraphqlApplication {
public static void main(String[] args) {
10
SpringApplication.run(SpringGraphqlApplication.class, args);
11
12
Start the SpringGraphqlApplication and then call the GraphQL endpoint as shown below
using Postman:
In Postman, use the Post URL: localhost:8080/bookstore/getAllBooks
and Body: query{ allBooks{bookId,bookName }}
24/25
You can add more fields in the body part and, accordingly, it will retrieve the data from
server.
That's all for this talk. Enjoy the power of GraphQL.
GitHub URL:
https://github.com/arunpandeycdac/MicroserviceWithSpringBootAndGraphQL
Topics:
java, microservice, graphql, microservices tutorial java
Opinions expressed by DZone contributors are their own.
25/25