What is GraphQL?

GraphQL, an open-source data query and manipulation language for APIs, provides a more efficient, powerful, and flexible alternative to REST. When implemented in Java, GraphQL can dramatically enhance data fetching capabilities. This article delves into implementing GraphQL in Java, featuring code examples and practical tips.

GraphQL was developed by Facebook in 2012 and released publicly in 2015. It allows clients to request exactly the data they need, which reduces over-fetching and under-fetching issues often associated with REST APIs. GraphQL provides a robust type system for describing data, queries, and mutations, enabling developers to build APIs that are easier to evolve over time.

Setting Up Your Java Project

To start implementing GraphQL in Java, you need to set up a Java project. This guide uses Maven for project management. Create a new Maven project and add the necessary dependencies.

Dependencies

Add the following dependencies to your pom.xml file:

xml

<dependencies>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java</artifactId>
<version>17.3</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>11.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.5.4</version>
</dependency>
</dependencies>

Defining Your GraphQL Schema

GraphQL schemas define the structure of your API. Schemas describe the types of data and their relationships. In this example, we’ll create a simple schema for a library system with Book and Author types.

Create a file named schema.graphqls in the src/main/resources directory:

graphql

type Author {
id: ID!
name: String!
books: [Book!]
}
type Book {
id: ID!
title: String!
author: Author!
}type Query {
allBooks: [Book!]
bookById(id: ID!): Book
allAuthors: [Author!]
authorById(id: ID!): Author
}type Mutation {
createAuthor(name: String!): Author
createBook(title: String!, authorId: ID!): Book
}

Creating Data Models

Define the data models corresponding to your schema. Create Author and Book classes in your project.

Author.java

java

package com.example.graphql.model;

import java.util.List;

public class Author {
private String id;
private String name;
private List<Book> books;

// Constructors, getters, and setters
}

Book.java

java

package com.example.graphql.model;

public class Book {
private String id;
private String title;
private Author author;

// Constructors, getters, and setters
}

Creating Repositories

For simplicity, we’ll use in-memory repositories. Create AuthorRepository and BookRepository classes.

AuthorRepository.java

java

package com.example.graphql.repository;

import com.example.graphql.model.Author;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class AuthorRepository {
private Map<String, Author> authors = new HashMap<>();

public Author findById(String id) {
return authors.get(id);
}

public void save(Author author) {
authors.put(author.getId(), author);
}

public Map<String, Author> findAll() {
return authors;
}
}

BookRepository.java

java

package com.example.graphql.repository;

import com.example.graphql.model.Book;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
public class BookRepository {
private Map<String, Book> books = new HashMap<>();

public Book findById(String id) {
return books.get(id);
}

public void save(Book book) {
books.put(book.getId(), book);
}

public Map<String, Book> findAll() {
return books;
}
}

Implementing GraphQL Resolvers

Resolvers are responsible for fetching the data for your GraphQL queries and mutations. Create resolver classes for queries and mutations.

QueryResolver.java

java

package com.example.graphql.resolver;

import com.coxautodev.graphql.tools.GraphQLQueryResolver;
import com.example.graphql.model.Author;
import com.example.graphql.model.Book;
import com.example.graphql.repository.AuthorRepository;
import com.example.graphql.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Component
public class QueryResolver implements GraphQLQueryResolver {

@Autowired
private BookRepository bookRepository;

@Autowired
private AuthorRepository authorRepository;

public List<Book> allBooks() {
return bookRepository.findAll().values().stream().collect(Collectors.toList());
}

public Book bookById(String id) {
return bookRepository.findById(id);
}

public List<Author> allAuthors() {
return authorRepository.findAll().values().stream().collect(Collectors.toList());
}

public Author authorById(String id) {
return authorRepository.findById(id);
}
}

MutationResolver.java

java

package com.example.graphql.resolver;

import com.coxautodev.graphql.tools.GraphQLMutationResolver;
import com.example.graphql.model.Author;
import com.example.graphql.model.Book;
import com.example.graphql.repository.AuthorRepository;
import com.example.graphql.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.UUID;

@Component
public class MutationResolver implements GraphQLMutationResolver {

@Autowired
private AuthorRepository authorRepository;

@Autowired
private BookRepository bookRepository;

public Author createAuthor(String name) {
Author author = new Author();
author.setId(UUID.randomUUID().toString());
author.setName(name);
authorRepository.save(author);
return author;
}

public Book createBook(String title, String authorId) {
Author author = authorRepository.findById(authorId);
if (author == null) {
throw new IllegalArgumentException(“Author not found”);
}
Book book = new Book();
book.setId(UUID.randomUUID().toString());
book.setTitle(title);
book.setAuthor(author);
bookRepository.save(book);
return book;
}
}

Configuring Spring Boot

Spring Boot simplifies setting up a GraphQL server. Add the following configuration class.

GraphQLConfig.java

java

package com.example.graphql.config;

import com.coxautodev.graphql.tools.SchemaParser;
import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;

@Configuration
public class GraphQLConfig {

private GraphQL graphQL;

@PostConstruct
public void init() {
GraphQLSchema schema = SchemaParser.newParser()
.file(“schema.graphqls”)
.resolvers(new QueryResolver(), new MutationResolver())
.build()
.makeExecutableSchema();
this.graphQL = GraphQL.newGraphQL(schema).build();
}

@Bean
public GraphQL graphQL() {
return graphQL;
}
}

Running Your GraphQL Server

Now, you can run your Spring Boot application. Once the application starts, navigate to http://localhost:8080/graphiql to access the GraphiQL interface, which allows you to interact with your GraphQL API.

Example Queries and Mutations

Here are some example queries and mutations you can try:

Query: Fetch All Books

graphql

{
allBooks {
id
title
author {
id
name
}
}
}

Query: Fetch a Book by ID

graphql

{
bookById(id: "1") {
id
title
author {
id
name
}
}
}

Mutation: Create a New Author

graphql

mutation {
createAuthor(name: "George Orwell") {
id
name
}
}

Mutation: Create a New Book

graphql

mutation {
createBook(title: "1984", authorId: "1") {
id
title
author {
id
name
}
}
}

Conclusion

Implementing GraphQL in Java provides a powerful and flexible approach to building APIs, offering precise data fetching capabilities and efficient interactions between clients and servers. This guide walked you through setting up a Java project with Spring Boot, defining a GraphQL schema, creating data models, repositories, and resolvers, and configuring the GraphQL server.

By adopting GraphQL, developers can overcome the limitations of traditional REST APIs, resulting in more performant and user-friendly applications. As you continue to explore GraphQL, you’ll discover more advanced features like subscriptions, custom directives, and schema stitching, which further enhance your API’s capabilities.

GraphQL is a valuable addition to your toolkit, especially when working on complex systems with varied data needs. With its growing ecosystem and community support, integrating GraphQL into your Java applications is a future-proof choice that promises to streamline data handling and improve client-server communication.