Introduction
This post will show you how to wrap REST API with GraphQL in Spring Boot framework. So here we will create a Java based GraphQL server. When you come to know that GraphQL has advantages over REST API and you want to start using GraphQL but you are stuck with legacy REST APIs, then this tutorial may help you to wrap your existing REST API with GraphQL. Hence you don’t need to rewrite your existing REST APIs into GraphQL but you can utilize them with GraphQL.
GraphQL is an open source query language introduced by Facebook in the year of 2015. I would not explain here about GraphQL as Facebook has already explained in details and can be found here. You will also find the differences between REST and GraphQL.
You may also like to read Wrap REST API with GraphQL in Node.js.
In this Spring Boot application we will also build a GraphQL IDE or Playground for performing query and mutation on GraphQL API. GraphQL IDE is similar to Postman or REST client but GraphQL IDE is used for GraphQL API, whereas Postman or REST client is used for REST API.
You can use any technology for implementing GraphiQL server. Here we will use Spring Boot and Java to implement GraphQL server.
Prerequisites
Knowledge on GraphQL, REST, Spring Boot and Java
Eclipse Neon, Spring Boot 2.1.6, Java 1.8, GraphQL 11.0
Go through Spring Boot Data JPA CRUD Example
Example with Source Code
Please go through the following sections to wrap REST API with GraphQL in Spring Boot application.
As I mentioned the URL in prerequisite section on Spring Boot Data JPA CRUD Example, so I will assume that you have similar kind of existing REST API for performing basic CRUD operations.
Creating Project
Create a gradle based project in Eclipse and the project structure may look to the below image:

Updating Build Script
We will update the default generated build.gradle script to include the required dependencies for our Spring Boot based GraphQL application.
The last two dependencies are for GraphQL – one is for using GraphQL with Java and another one for using GraphQL with Spring Boot.
Also notice I have used implementation instead of compile, so you can read here about implementation in Gradle.
buildscript {
ext {
springBootVersion = '2.1.6.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
repositories {
mavenCentral()
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
dependencies {
implementation("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
implementation('com.graphql-java:graphql-java:11.0')
implementation('com.graphql-java:graphql-java-spring-boot-starter-webmvc:1.0')
}
Identifying REST Endpoints
The first task is to identify the existing REST end-points and the example we are using here Spring Boot Data JPA CRUD Example has the following endpoints:
GET /websites
GET /webste/{id}
POST /website
PUT /website
DELETE /website
The endpoint – GET /websites
– lists all the available website information from the server.
The endpoint – GET /website/{id}
– lists the available website information for a given website id from the server.
The endpoint – POST /website
– adds a new website information to the server. The website information is passed in the body as a JSON payload.
The endpoint – PUT /website
– updates an existing website information to the server. The website information is passed in the body as a JSON payload.
The endpoint – DELETE /website
– deletes an existing website information from the server. The website information is passed in the body as a JSON payload.
Identifying Data Model and Creating VO Class
Next task is to identify the data model of the existing REST API and we know that the existing REST API Spring Boot Data JPA CRUD Example has below information for a particular website:
id, name, url
The id
indicates the Id of the website, name
indicates the name of the website and url
indicates the URL of the website.
So we can create corresponding VO class that will help us to easily convert to JSON payload or vice-versa.
Below is the VO class that will represent a website information.
package spring.boot.graphql.rest.wrapper.vo;
public class Website {
private Integer id;
private String name;
private String url;
public Website() {
}
public Website(Integer id, String name, String url) {
this.id = id;
this.name = name;
this.url = url;
}
//getters and setters
}
Defining GraphQL Schema
We are creating a new file schema.graphql in src/main/resources with the following content:
type Query {
websites: [Website!]!
website(id: ID!): Website
}
type Mutation {
addWebsite(name: String!, url: String!): String!
updateWebsite(id: ID!, name: String!, url: String!): String!
deleteWebsite(id: ID!): String!
}
type Website {
id: ID
name: String
url: String
}
This schema defines top level fields (in the type Query
): websites
which returns the details of all available websites and website
that returns the details of a particular website for a given id.
It also defines the Website which has fields id, name and url.
The Domain Specific Language shown above which is used to describe a schema is called Schema Definition Language or SDL. More details about it can be found here.
We know that Query is used to query GraphQL server for fetching or reading data.
We also need to save new website information, update existing website information or delete existing website information. Therefore we used Mutation
to perform create, update and delete operations.
In the above Query, we have used []
and it means it will return a list of websites.
Notice we have also used !
to denote that the parameter is required. If you make a parameter required and if you do not pass value or get value from server on this parameter then GraphQL will throw errors.
Creating GraphQL DataFetcher
The most important concept for a GraphQL Java server is a DataFetcher that fetches the Data for one field when a query is executed.
While GraphQL Java is executing a query, it calls the appropriate DataFetcher for each field it encounters in query. A DataFetcher is an Interface with a single method, taking a single argument of type DataFetcherEnvironment:
public interface DataFetcher<T> {
T get(DataFetchingEnvironment dataFetchingEnvironment) throws Exception;
}
Every field from the schema has a DataFetcher associated with. If you don’t specify any DataFetcher for a specific field, then the default PropertyDataFetcher is used.
Now we will create a class GraphQLDataFetcher that will fetch data from REST API using Spring’s RestTemplate.
If you look at the methods in the below class, then you will find that any method returns a DataFetcher implementation which takes a DataFetcherEnvironment and returns an appropriate object.
package spring.boot.graphql.rest.wrapper.provider;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import graphql.schema.DataFetcher;
import spring.boot.graphql.rest.wrapper.vo.Website;
@Component
public class GraphQLDataFetcher {
private final String REST_URL = "http://localhost:8080/website";
@Autowired
private RestTemplate restTemplate;
public DataFetcher<List<Website>> getWebsiteList() {
return dataFetchingEnvironment -> {
return restTemplate
.exchange(REST_URL, HttpMethod.GET, null, new ParameterizedTypeReference<List<Website>>() {
}).getBody();
};
}
public DataFetcher<Website> getWebsiteById() {
return dataFetchingEnvironment -> {
String id = dataFetchingEnvironment.getArgument("id");
return restTemplate.getForObject(REST_URL + "/" + id, Website.class);
};
}
public DataFetcher<String> addWebsite() {
return dataFetchingEnvironment -> {
String name = dataFetchingEnvironment.getArgument("name");
String url = dataFetchingEnvironment.getArgument("url");
Website website = new Website();
website.setName(name);
website.setUrl(url);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Website> entity = new HttpEntity<>(website, headers);
return restTemplate.postForObject(REST_URL, entity, String.class);
};
}
public DataFetcher<String> updateWebsite() {
return dataFetchingEnvironment -> {
String id = dataFetchingEnvironment.getArgument("id");
String name = dataFetchingEnvironment.getArgument("name");
String url = dataFetchingEnvironment.getArgument("url");
Website website = new Website(Integer.parseInt(id), name, url);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Website> entity = new HttpEntity<>(website, headers);
return restTemplate.exchange(REST_URL, HttpMethod.PUT, entity, new ParameterizedTypeReference<String>() {
}).getBody();
};
}
public DataFetcher<String> deleteWebsite() {
return dataFetchingEnvironment -> {
String id = dataFetchingEnvironment.getArgument("id");
Website website = new Website();
website.setId(Integer.parseInt(id));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<Website> entity = new HttpEntity<>(website, headers);
return restTemplate.exchange(REST_URL, HttpMethod.DELETE, entity, new ParameterizedTypeReference<String>() {
}).getBody();
};
}
}
Creating GraphQL Provider
We had create GraphQL schema, now it’s also important to create a GraphQL provider class to read and parse the schema file for performing query and mutation.
In the below code the init()
method creates GraphQL instance.
We use Spring Resource to read the file from our classpath, then create a GraphQLSchema and GraphQL instance. This GraphQL instance is exposed as a Spring Bean via the graphQL()
method annotated with @Bean
. The GraphQL Java Spring adapter will use that GraphQL instance to make our schema available via HTTP on the default url /graphql.
We implement the buildSchema()
method which creates the GraphQLSchema instance and wires in code to fetch data.
TypeDefinitionRegistry is the parsed version of our schema file. SchemaGenerator combines the TypeDefinitionRegistry with RuntimeWiring to actually make the GraphQLSchema.
buildRuntimeWiring uses the dataFetcher
bean to actually register DataFetchers for performing Query and Mutation.
package spring.boot.graphql.rest.wrapper.provider;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
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;
import graphql.schema.idl.TypeRuntimeWiring;
@Component
public class GraphQLProvider {
private GraphQL graphQL;
@Autowired
private GraphQLDataFetcher dataFetcher;
@PostConstruct
public void init() throws IOException {
final Resource resource = new ClassPathResource("schema.graphql");
String sdl = null;
try {
sdl = new String(Files.readAllBytes(resource.getFile().toPath()), StandardCharsets.UTF_8);
} catch (IOException e) {
e.printStackTrace();
}
GraphQLSchema graphQLSchema = buildSchema(sdl);
this.graphQL = GraphQL.newGraphQL(graphQLSchema).build();
}
private GraphQLSchema buildSchema(String sdl) {
TypeDefinitionRegistry typeRegistry = new SchemaParser().parse(sdl);
RuntimeWiring runtimeWiring = buildWiring();
SchemaGenerator schemaGenerator = new SchemaGenerator();
return schemaGenerator.makeExecutableSchema(typeRegistry, runtimeWiring);
}
private RuntimeWiring buildWiring() {
return RuntimeWiring.newRuntimeWiring()
.type(TypeRuntimeWiring.newTypeWiring("Query").dataFetcher("websites", dataFetcher.getWebsiteList()))
.type(TypeRuntimeWiring.newTypeWiring("Query").dataFetcher("website", dataFetcher.getWebsiteById()))
.type(TypeRuntimeWiring.newTypeWiring("Mutation").dataFetcher("addWebsite", dataFetcher.addWebsite()))
.type(TypeRuntimeWiring.newTypeWiring("Mutation").dataFetcher("updateWebsite",
dataFetcher.updateWebsite()))
.type(TypeRuntimeWiring.newTypeWiring("Mutation").dataFetcher("deleteWebsite",
dataFetcher.deleteWebsite()))
.build();
}
@Bean
public GraphQL graphQL() {
return graphQL;
}
}
Creating Main Class
We need to create a main class with @SpringBootApplication
annotation that identifies as Spring Boot application and deploys into embedded Tomcat server.
package spring.boot.graphql.rest.wrapper;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = "spring.boot.graphql.rest.wrapper")
public class SpringGraphqlServerApp {
public static void main(String[] args) {
SpringApplication.run(SpringGraphqlServerApp.class, args);
}
}
Changing Server Port
Change the default server port from 8080 to 9000 as our existing REST API already running on port 8080.
The below line is put into application.properties file.
server.port=9000
Enough coding! Let’s test our application.
Testing the Application
Make sure that your application Spring Boot Data JPA CRUD Example is running on server.
Now run the above main class, your application will be deployed into embedded tomcat server on port 9000.
Your GraphQL endpoint, by default, will point to http://localhost:9000/graphql.
Using the above URL you can only execute Query but you won’t be able to execute Mutation.
So if you want to execute Query using the default GraphQL URL then you can execute below URLs for fetching all available websites and a particular website, respectively:
http://localhost:9000/graphql?query=%7Bwebsites%7Bid%20name%20url%7D%7D
http://localhost:9000/graphql?query=%7Bwebsite%28id:1%29%7Bid%7D%7D
The above URLs are encoded ones because HTTP request won’t allow you to use some characters. The equivalent human readable URLs would be as follows:
http://localhost:9000/graphql?query={websites{id name url}}
http://localhost:9000/graphql?query={website(id:1){id}}
Therefore, we need GraphQL IDE or Playground that will help us to execute Query and Mutations with our standard input formats. The next section explains how to create a GraphQL IDE or Playgroud.
Creating GraphQL IDE
Create an index.html file under src/main/resources/static folder.
Obviously the below file is not created by me.
<!--
* Copyright (c) 2019 GraphQL Contributors
* All rights reserved.
*
* This source code is licensed under the license found in the
* LICENSE file in the root directory of this source tree.
-->
<!DOCTYPE html>
<html>
<head>
<style>
body {
height: 100%;
margin: 0;
width: 100%;
overflow: hidden;
}
#graphiql {
height: 100vh;
}
</style>
<!--
This GraphiQL example depends on Promise and fetch, which are available in
modern browsers, but can be "polyfilled" for older browsers.
GraphiQL itself depends on React DOM.
If you do not want to rely on a CDN, you can host these files locally or
include them directly in your favored resource bunder.
-->
<script src="//cdn.jsdelivr.net/es6-promise/4.0.5/es6-promise.auto.min.js"></script>
<script src="//cdn.jsdelivr.net/fetch/0.9.0/fetch.min.js"></script>
<script src="//cdn.jsdelivr.net/react/15.4.2/react.min.js"></script>
<script src="//cdn.jsdelivr.net/react/15.4.2/react-dom.min.js"></script>
<!--
These two files can be found in the npm module, however you may wish to
copy them directly into your environment, or perhaps include them in your
favored resource bundler.
-->
<link rel="stylesheet" href="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.css" />
<script src="//cdn.jsdelivr.net/npm/graphiql@0.11.2/graphiql.js"></script>
</head>
<body>
<div id="graphiql">Loading...</div>
<script>
/**
* This GraphiQL example illustrates how to use some of GraphiQL's props
* in order to enable reading and updating the URL parameters, making
* link sharing of queries a little bit easier.
*
* This is only one example of this kind of feature, GraphiQL exposes
* various React params to enable interesting integrations.
*/
// Parse the search string to get url parameters.
var search = window.location.search;
var parameters = {};
search.substr(1).split('&').forEach(function (entry) {
var eq = entry.indexOf('=');
if (eq >= 0) {
parameters[decodeURIComponent(entry.slice(0, eq))] =
decodeURIComponent(entry.slice(eq + 1));
}
});
// if variables was provided, try to format it.
if (parameters.variables) {
try {
parameters.variables =
JSON.stringify(JSON.parse(parameters.variables), null, 2);
} catch (e) {
// Do nothing, we want to display the invalid JSON as a string, rather
// than present an error.
}
}
// When the query and variables string is edited, update the URL bar so
// that it can be easily shared
function onEditQuery(newQuery) {
parameters.query = newQuery;
updateURL();
}
function onEditVariables(newVariables) {
parameters.variables = newVariables;
updateURL();
}
function onEditOperationName(newOperationName) {
parameters.operationName = newOperationName;
updateURL();
}
function updateURL() {
var newSearch = '?' + Object.keys(parameters).filter(function (key) {
return Boolean(parameters[key]);
}).map(function (key) {
return encodeURIComponent(key) + '=' +
encodeURIComponent(parameters[key]);
}).join('&');
history.replaceState(null, null, newSearch);
}
// Defines a GraphQL fetcher using the fetch API. You're not required to
// use fetch, and could instead implement graphQLFetcher however you like,
// as long as it returns a Promise or Observable.
function graphQLFetcher(graphQLParams) {
// When working locally, the example expects a GraphQL server at the path /graphql.
// In a PR preview, it connects to the Star Wars API externally.
// Change this to point wherever you host your GraphQL server.
const isDev = !window.location.hostname.match(/(^|\.)netlify\.com$|(^|\.)graphql\.org$/)
const api = isDev ? '/graphql' : 'https://swapi.graph.cool/'
return fetch(api, {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(graphQLParams),
credentials: 'include',
}).then(function (response) {
return response.text();
}).then(function (responseBody) {
try {
return JSON.parse(responseBody);
} catch (error) {
return responseBody;
}
});
}
// Render <GraphiQL /> into the body.
// See the README in the top level of this module to learn more about
// how you can customize GraphiQL by providing different values or
// additional child elements.
ReactDOM.render(
React.createElement(GraphiQL, {
fetcher: graphQLFetcher,
query: parameters.query,
variables: parameters.variables,
operationName: parameters.operationName,
onEditQuery: onEditQuery,
onEditVariables: onEditVariables,
onEditOperationName: onEditOperationName
}),
document.getElementById('graphiql')
);
</script>
</body>
</html>
Naturally when you have made changes to the application, you need to restart your server.
Once you restart server, you can point the URL at http://localhost:9000 in the browser and you will see the following screen on the browser.
So now it’s very easy to execute Query or Mutation on this IDE.

Reading Website Information
Now when we execute below Query on left pane then we get nothing on the right pane because there is no available website in the server.

Creating Website Information
Now execute Mutation to save new website information.


Reading Website Information
Now execute Query to fetch all available websites.

You can also query a particular website as shown below.

Updating Website Information
You can update an existing website information.

Deleting Website Information
You can delete an existing website information.

Reading Website Information
The final Query output will be as shown below:

That’s all. Hope you got idea on how to wrap REST API with GraphQL in Spring Boot application.
You may also like to read Wrap REST API with GraphQL in Node.js.
Thanks for reading.
How to do custom exception handling for this
Sorry I have a question that is out of the topic of the course, if the teacher can or know is already a help. I have already configured the zuul server, which is working as the application’s gateway, however I would like to have a new layer with graphQL to serve as my gateway, does the teacher have any experience in this type of configuration?
Thanks in advance