StackStalk
  • Home
  • Java
    • Java Collection
    • Spring Boot Collection
  • Python
    • Python Collection
  • C++
    • C++ Collection
    • Progamming Problems
    • Algorithms
    • Data Structures
    • Design Patterns
  • General
    • Tips and Tricks

Friday, February 4, 2022

Getting started in GraphQL with Spring Boot

 February 04, 2022     Java, Microservices, Spring Boot     No comments   

In this article we will explore basic concepts on GraphQL and look at how to develop a microservice in Spring Boot with GraphQL support.

GraphQL Introduction

Reference: https://graphql.org/

GraphQL is a query language for the APIs, and a server-side runtime for executing queries using a type system defined for the data. GraphQL isn't tied to any specific database or storage engine and there are multiple libraries to help implement GraphQL in different languages.

In GraphQL, schema is used to describe the shape of available data. Schema defines a hierarchy of types with fields. The schema also specifies exactly which queries and mutations are available for clients to execute. We will look into details on schema in the next section.

Typically when using a REST API we will always get back a complete dataset. Referred as over-fetching we could not limit the fields REST API returns. In GraphQL, using the query language the request can be customized to what is needed down to specific fields within each entity. Once we limit the data the amount of processing needed is reduced and it starts to add up.

Some best practice recommendations for GraphQL services.
  • GraphQL is typically served over HTTP via a single endpoint which expresses the full set of capabilities of the service. This is in contrast to REST APIs which expose a suite of URLs each of which expose a single resource.
  • GraphQL services typically respond using JSON, however the GraphQL spec does not require it
  • While there's nothing that prevents a GraphQL service from being versioned just like any other REST API, GraphQL takes a strong opinion on avoiding versioning by providing the tools for the continuous evolution of a GraphQL schema.
  • The GraphQL type system allows for some fields to return lists of values, but leaves the pagination of longer lists of values up to the API designer.

GraphQL Schema Introduction

The GraphQL specification defines a human-readable schema definition language (or SDL) that can be used to define schema and store it as a string.

Let us start with an example and understand the concepts.
# Movie type has fields id, name and Director
type Movie {
  name: String!
  director: Director
}

# Director type has fields id, name and list of movies
type Director {
  name: String!
  movies: [Movie] # List of movies
}

type Query {
  movies: [Movie]
  directors: [Director]
}

type Mutation {
  addMovie(id: ID, name: String, director: String): Movie
}
In this example, we have two object types Movie and Director. Movie has the associated Director and the Director has the list of movies. Relationships are defined in a unified schema, which allows client developers to see what data is available and then request a specific subset of that data with a single optimized query.
  • Most of the types defined in a GraphQL schema are object types. An object type contains a collection of fields, each of which has its own type.
  • GraphQL's default scalar types are Int, String, Float, Boolean, ID. ID is typically a key for a cache
  • A field can return a list containing items of a particular type. This is indicated with [] square brackets
  • Non-nullability of a field can be specified with an exclamation mark !
  • The Query type is a special object type that defines all of the top-level entry points for queries that clients execute against the server
  • The Mutation type is similar to the Query type and defines entry points for write operations.
  • Input types are special object types that allow to provider data as arguments to fields as against flat scalar arguments
With this introduction let us implement an example.

MovieService example

In this example, we will implement a simple movie service to leverage GraphQL with Spring Boot. We will implement Query and Mutation support of GraphQL to list all movies, find a specific movie and to add a new movie. This example also integrates H2 database to store the movies.

GraphQL Schema

Let us now define the GraphQL schema which describes what data can be queried. Create a file "schema.graphqls" in the resources folder of the app and include the below schema.

In this schema we have "Movie" as object type. We define an input type "MovieInput" which helps in the mutation case to pass the object as whole. We also have "Query" and "Mutation" types to get movie(s) and add a movie.
type Movie {
   id: ID
   name: String
   director: String
}

input MovieInput {
   id: ID!
   name: String!
   director: String!
}

type Query {
   movies: [Movie]
   movieById(id: ID): Movie
}

type Mutation {
   addMovie(movieInput: MovieInput): Movie
}

Few additional points to note in the schema definition.
  • In the "movies" query we have not included any parenthesis since there are no arguments
  • The "!" for fields in MovieInput type indicates these are non-nullable and input is expected

Spring Boot dependencies

Create a Spring starter project and include the Spring Starter Web support, GraphQL Spring Boot starter, GraphQL Java, Starter for Spring Data JPA, H2 Database Engine, Project Lombak and GraphQL IDE for testing as dependencies to the pom.xml. For more details on Project Lombak refer to this article "Introduction to Project Lombak".
<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>			
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.projectlombok</groupId>
		<artifactId>lombok</artifactId>
		<version>1.18.22</version>
		<scope>provided</scope>
	</dependency>        
	<dependency>
		<groupId>com.graphql-java</groupId>
		<artifactId>graphql-spring-boot-starter</artifactId>
		<version>5.0.2</version>
	</dependency>		
	<dependency>
		<groupId>com.graphql-java</groupId>
		<artifactId>graphql-java-tools</artifactId>
		<version>5.2.4</version>
	</dependency>		
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-data-jpa</artifactId>
	</dependency>
	<dependency>
		<groupId>com.h2database</groupId>
		<artifactId>h2</artifactId>
	</dependency>		
	<dependency>
		<groupId>com.graphql-java</groupId>
		<artifactId>graphiql-spring-boot-starter</artifactId>
		<version>3.0.3</version>
	</dependency>	
</dependencies>

Configure application.properties

Update the application.properties to specify the H2 database.
spring.datasource.url=jdbc:h2:mem:moviesdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Define the data object

We start with the Movie POJO. We use the Lombok @Data annotation which generate the getters and setters needed.
package com.stackstalk;

import javax.persistence.Entity;

import javax.persistence.Id;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Movie {

	@Id
	private Integer id;
	private String name;
	private String director;
}


Define the repository

Now let us define the repository.
package com.stackstalk;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MovieRepository extends JpaRepository<Movie, Integer> {

}

Define the GraphQL query resolver

Now let us implement the GraphQL query resolver which serves the queries from the client. The GraphQLQueryResolver is a marker interface and the framework looks for method signatures matching the GraphQL schema. It is also feasible to write multiple query resolvers with a logical grouping of queries. In this example, we implement the two query methods to "Get a movie by id" and "Get all movies" matching the schema.
package com.stackstalk;

import java.util.List;
import java.util.Optional;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.coxautodev.graphql.tools.GraphQLQueryResolver;

@Component
public class MovieQueryResolver implements GraphQLQueryResolver {
	
	@Autowired
	private MovieRepository movieRepository;
	
	public Optional<Movie> movieById(Integer id) {
		return movieRepository.findById(id);
	}
	
	public List<Movie> movies() {
		return movieRepository.findAll();
	}
}

Define the GraphQL mutation resolver

Now let us implement the GraphQL mutation resolver which serves the writes from the client. In this example, we implement the add movie method which matches the mutation type defined the schema.
package com.stackstalk;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.coxautodev.graphql.tools.GraphQLMutationResolver;

@Component
public class MovieMutationResolver implements GraphQLMutationResolver {
	
	@Autowired
	private MovieRepository movieRepository;
	
	public Movie addMovie(Movie input) {
		Movie movie = new Movie(input.getId(), input.getName(), input.getDirector());
		return movieRepository.save(movie);
	}
}


Define the application

Finally let us define the application.
package com.stackstalk;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MovieServiceApplication {

	public static void main(String[] args) {
		SpringApplication.run(MovieServiceApplication.class, args);
	}
}

Putting it all together and testing

Now, let us test the application and examine.

Using the GraphQL IDE

In the project dependencies we have already included the GraphiQL dependency. This is an IDE which can be leveraged to perform GraphQL queries and do the mutuations. To launch the IDE use this URL http://localhost:8080/ in the browser.
If testing on Postman use the endpoint "http://localhost:8080/graphql" to do the GraphQL queries.

Add a Movie

This mutation is used to add a new movie.

GraphQL query
mutation {
  addMovie(movieInput: {
    id: 1
    name: "Scream"
    director: "Matt Bettinelli-Olpin, Tyler Gillett"
  }) {
    id
    name
    director
  }
}
Output
{
  "data": {
    "addMovie": {
      "id": "1",
      "name": "Scream",
      "director": "Matt Bettinelli-Olpin, Tyler Gillett"
    }
  }
}

Query a movie by identifier

This query is used to get a specific movie by identifier.

GraphQL query
query {
  movieById(id: 1) {
    id
    name
    director
  }
}
Output
{
  "data": {
    "movieById": {
      "id": "1",
      "name": "Scream",
      "director": "Matt Bettinelli-Olpin, Tyler Gillett"
    }
  }
}

Query all movies

This query is used to get a list of all movies.

GraphQL query
query {
  movies {
    id
    name
    director
  }
}
Output
{
  "data": {
    "movies": [
      {
        "id": "1",
        "name": "Scream",
        "director": "Matt Bettinelli-Olpin, Tyler Gillett"
      },
      {
        "id": "2",
        "name": "Spider-Man: No Way Home",
        "director": "Jon Watts "
      }
    ]
  }
}
Get access to the full source code of this example in GitHub.

In conclusion, typically when using a REST API we will always get back a complete dataset. Referred as over-fetching we could not limit the fields REST API returns. In GraphQL, using the query language the request can be customized to what is needed down to specific fields within each entity. With Spring Boot it is pretty easy to implement GraphQL with the available library support.

Refer to this link for more Java Tutorials.
  • Share This:  
Newer Post Older Post Home

0 comments:

Post a Comment

Follow @StackStalk
Get new posts by email:
Powered by follow.it

Popular Posts

  • Avro Producer and Consumer with Python using Confluent Kafka
    In this article, we will understand Avro a popular data serialization format in streaming data applications and develop a simple Avro Produc...
  • Monitor Spring Boot App with Micrometer and Prometheus
    Modern distributed applications typically have multiple microservices working together. Ability to monitor and manage aspects like health, m...
  • Server-Sent Events with Spring WebFlux
    In this article we will review the concepts of server-sent events and work on an example using WebFlux. Before getting into this article it ...
  • Implement caching in a Spring Boot microservice using Redis
    In this article we will explore how to use Redis as a data cache for a Spring Boot microservice using PostgreSQL as the database. Idea is to...
  • Python FastAPI microservice with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Spring Boot with Okta and OPA
    Authentication (AuthN) and Authorization (AuthZ) is a common challenge when developing microservices. In this article, we will explore how t...
  • Getting started with Kafka in Python
    This article will provide an overview of Kafka and how to get started with Kafka in Python with a simple example. What is Kafka? ...
  • Getting started in GraphQL with Spring Boot
    In this article we will explore basic concepts on GraphQL and look at how to develop a microservice in Spring Boot with GraphQL support. ...

Copyright © StackStalk