Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Microservices With GraphQL

DZone 's Guide to

Microservices With GraphQL

A developer goes through some Java and XML code used to create a basic microservice application that uses GraphQL to fetch data.

· Microservices Zone ·
Free Resource

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>
    <packaging>jar</packaging>
    <name>spring-graphql</name>
    <description>GraphQL is invented by Facebook as a better replacement of REST for Web APIs</description>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath />
    </parent>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-spring-boot-starter</artifactId>
            <version>3.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.graphql-java</groupId>
            <artifactId>graphql-java-tools</artifactId>
            <version>3.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</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
  }


type Query{
allBooks: [Book]
Book(id: String): Book
 }

type Book{
bookId: String
bookName: String
publishedDate: String
writer: [String]
publisher: String
} 


BookSearchController.java

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;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.arun.spring.graphql.api.datafetcher.BookDataFetcher;
import com.arun.spring.graphql.api.datafetcher.AllBookDataFetcher;
import com.arun.spring.graphql.api.entity.Book;
import com.arun.spring.graphql.api.service.BookService;

import graphql.ExecutionResult;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import graphql.schema.idl.RuntimeWiring;
import graphql.schema.idl.SchemaGenerator;
import graphql.schema.idl.SchemaParser;
import graphql.schema.idl.TypeDefinitionRegistry;

@RestController
@RequestMapping("/bookstore")
public class BookSearchController {
 @Autowired
 private BookService service;
 // load graphqls file
 @Value("classpath:book.schema")
 private Resource schemaResource;
 @Autowired
 private AllBookDataFetcher allBookDataFetcher;
 @Autowired
 private BookDataFetcher bookDataFetcher;

 private GraphQL graphQL;

 // load schema at application start up
 @PostConstruct
 public void loadSchema() throws IOException {
  // get the schema
  File schemaFile = schemaResource.getFile();
  // parse schema
  TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(schemaFile);
  RuntimeWiring wiring = buildRuntimeWiring();
  GraphQLSchema schema = new SchemaGenerator().makeExecutableSchema(typeRegistry, wiring);
  graphQL = GraphQL.newGraphQL(schema).build();
 }

 private RuntimeWiring buildRuntimeWiring() {
  return RuntimeWiring.newRuntimeWiring().type("Query", typeWiring -> typeWiring
   .dataFetcher("allBooks", allBookDataFetcher).dataFetcher("book", bookDataFetcher)).build();
 }

 @GetMapping("/booksList")
 public List < Book > getBooksList() {
  return service.findAllBooks();
 }

 /*
  * In PostMan use Post URL: localhost:8080/bookstore/getAllBooks
  * and Body: query{
   allBooks{
 bookId,
 bookName
   }
 }
  */
 @PostMapping("/getAllBooks")
 public ResponseEntity < Object > getAllBooks(@RequestBody String query) {
  ExecutionResult result = graphQL.execute(query);
  return new ResponseEntity < Object > (result, HttpStatus.OK);
 }

 @GetMapping("/search/{bookId}")
 public Book getBookInfo(@PathVariable String movieId) {
  return service.findBookById(movieId);
 }

 @PostMapping("/getBookById")
 public ResponseEntity < Object > getBookById(@RequestBody String query) {
  ExecutionResult result = graphQL.execute(query);
  return new ResponseEntity < Object > (result, HttpStatus.OK);
 }
}


AllBookDataFetcher.java

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;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;

@Component
public class AllBookDataFetcher implements DataFetcher < List < Book >> {
 @Autowired
 private BookRepository repository;

 @Override
 public List < Book > get(DataFetchingEnvironment environment) {
  return repository.findAll();
 }
}


BookDataFetcher.java

package com.arun.spring.graphql.api.datafetcher;

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;

import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;

@Component
public class BookDataFetcher implements DataFetcher < Book > {
 @Autowired
 private BookRepository repository;

 @Override
 public Book get(DataFetchingEnvironment environment) {
  String movieId = environment.getArgument("id");
  return repository.findOne(movieId);
 }
}


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;

@ToString
@AllArgsConstructor
@NoArgsConstructor
@Table
@Entity
public class Book {
 @Id
 private String bookId;
 private String bookName;
 private String publishedDate;
 private String[] writer;
 private String publisher;
}


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.*;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.arun.spring.graphql.api.entity.Book;
import com.arun.spring.graphql.api.repository.BookRepository;

@Service
public class BookService {
 @Autowired
 private BookRepository repository;

 @PostConstruct
 public void initBooks() {
  List < Book > books = new ArrayList < > ();
  books.add(new Book("101", "The Science of Marvel",
   "22-12-2017", new String[] {
    "Sebastian"
   },
   "Infinity Stones"));
  books.add(new Book("102", "The Sixth Extinction",
   "22-12-2017", new String[] {
    "Sebastian",
    "Elizabeth"
   },
   "Infinity Stones"));
  books.add(new Book("103", "The Science of Marvel -2",
   "22-12-2019", new String[] {
    "Sebastian"
   },
   "Infinity Stones"));
  repository.save(books);
 }

 public List < Book > findAllBooks() {
  return repository.findAll();
 }

 public Book findBookById(String movieId) {
  return repository.findOne(movieId);
 }
}


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) {
  SpringApplication.run(SpringGraphqlApplication.class, args);
 }
}

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 }} 

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.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}