Create a GraphQL API using Spring Boot
Tags: tutorial, java, graphql
Published: 2019-07-21 11:22:38

# Introduction
In this tutorial we will build a Pokemon API that consumes data from a Postgres database, with a simple endpoint that performs a search by id.

The final code is in this [Github repository](https://github.com/fabiothiroki/spring-boot-pokedex-graphql).

# Postgres setup
If you already have Postgres installed locally, you can skip this part, otherwise the easiest way to do it is by running a Docker image. Just install [Docker](https://docs.docker.com/install/) and then:
```
docker run -p5432:5432 -d postgres:11.4-alpine
```
This command will start a Postgres instance on port 5432 with default user `postgres` and default database `postgres`.

# Spring Boot setup
We will start by creating the initial project files using [Spring Initializr](https://start.spring.io/). I've selected:
- Gradle
- Java
- Spring Boot 2.1.6
- Spring Web Starter
- Spring Data JPA
- PostgreSQL Driver

Besides Spring dependencies, we need to add the GraphQL libraries:
- GraphQL Spring Boot Starter: will automatically create an `/graphql` endpoint
- GraphQL Spring Boot Starter Test: for our unit tests
- GraphQL Java Tools: from its own [documentation](https://www.graphql-java-kickstart.com/tools/schema-definition/): "maps fields on your GraphQL objects to methods and properties on your java objects". This library requires version `1.3.*` of Kotlin, so you need to create a `gradle.properties` file on the project root directory with content:
```
kotlin.version=1.3.10
```

## Database connection
After adding the dependencies, you can edit the `src/main/resources/application.properties` file to add the Postgres configuration. If you are using the Docker command above to start Postgres locally, your file should be like this:
```
## PostgreSQL
spring.datasource.url=jdbc:postgresql://localhost:5432/postgres
spring.datasource.username=postgres
spring.datasource.password=

#drop n create table again, good for testing, comment this in production
spring.jpa.hibernate.ddl-auto=create-drop
```

Run your application to test if everything is working so far: `./gradlew bootRun`.

# GraphQL Schema
GraphQL has a great schema language that adds type declatarations to its request and return values and couples this to the API implementation. Which means that what you declare on the schema must be implemented.

If we want to add an endpoint to search a pokemon by its id we should declare on `src/main/resources/schema.graphqls` file:
```graphql
type Pokemon {
    id: ID!
    name: String!
}

type Query {
    pokemon(id: ID!): Pokemon
}
```

Our next step now must be the database search of a Pokemon instance by its id, or else the application won't run.

# Query resolver
The declared schema expects to returns a Pokemon `type` that contains required attributes `id` and `name`. 

To our application, that means `Pokemon` is a Java class with `id` and `name` properties but also a database table. We can use `javax.persistence` annotations to automatically map Pokemon to database table with columns `id` and `name`:
```java
@Entity
@Table(name = "pokemon")
public class Pokemon {

    public Pokemon(final Long id, final String name) {
        this.id = id;
        this.name = name;
    }

    @Id
    public Long id;

    @Column
    public String name;
}
```

The other expected class should be a `Spring Bean` that implements `GraphQLQueryResolver` interface and should have a method with name `getPokemon`, that matches the parameters and response exactly like we defined in the scheme:
```java
@Component
public class Query implements GraphQLQueryResolver {

    public Pokemon getPokemon(Long id) {
        return new Pokemon(1L, "Pikachu");
    }
}
```
We can now perform an request at our new endpoint to check if its response is our Pikachu.

![Pikachu appearing](https://media.giphy.com/media/xuXzcHMkuwvf2/giphy.gif)

# GraphiQL
GraphiQL configures an endpoint at our API that allow us to test any query. In our project it will run on address `http://localhost:8080/graphiql`.

The left column is where we should write the queries, and the right column is the results. For example, if we enter the query:
```graphql
# Searches a Pokemon with id 25 and returns its field 'name'
query {
  pokemon(id: 25){
    name
  }
}
```
We should expect the result on right column:
```json
{
  "data": {
    "pokemon": {
      "name": "Pikachu"
    }
  }
}
```
So far it doesn't matter which parameter `id` we pass because we've fixed the response object, but now we will implement a database search.

# Fetch Pokemons from database
Currently our application is not doing a real database search but returning a fixed instance. Let's now implement this part.

First we create a `PokemonRepository` interface that extends `JpaRepository`:
```java
@Repository
public interface PokemonRepository extends JpaRepository<Pokemon, Long> {
}
```

Then we change our `Query` class to autowire this bean and perform the real database fetch:
```java
@Component
public class Query implements GraphQLQueryResolver {

    @Autowired
    private PokemonRepository repository;

    public Pokemon getPokemon(Long id) {
        // Not returning a fixed instance anymore
        return repository.findById(id).orElse(null);
    }
}
```

# Unit test
Our automated test will make use of `GraphQLTestTemplate` class which allow us to enter a `query` and verify its response. For example, if we want to test the search pokemon by id query, we first have to create a file in `src/test/resources` with this query:
```graphql
# src/test/resources/get-pokemon-by-id.graphql
query {
    pokemon(id: "1") {
        id
        name
    }
}
```

The test class should be annotated with `@GraphQLTest` so it can resolve the `GraphQLTestTemplate` instance, and `PokemonRepository` should be annotated with `@MockBean` so we can mock its response using `Mockito`.
```java
@RunWith(SpringRunner.class)
@GraphQLTest
public class DemoApplicationTests {

	@Autowired
	private GraphQLTestTemplate graphQLTestTemplate;

	@MockBean
	private PokemonRepository pokemonRepository;

	@Test
	public void getById() throws IOException {
		Pokemon pokemon = new Pokemon(1L, "Pikachu");
		when(pokemonRepository.findById(any()))
				.thenReturn(Optional.of(pokemon));

		GraphQLResponse response =
				graphQLTestTemplate.postForResource("get-pokemon-by-id.graphql");

		assertTrue(response.isOk());
		assertEquals("1", response.get("$.data.pokemon.id"));
		assertEquals("Pikachu", response.get("$.data.pokemon.name"));
	}
}
```
Basically the scenario we are testing here is the following:
- Given the repository returns a pikachu when called the `findById` method
- When we query GraphQL Api with `get-pokemon-by-id.graphql`
- Then we expect the response to be a JSON containing the pikachu from repository

# Conclusion
The challenge of implementing a GraphQL Api using Spring Boot relies mostly in the configuration and small details of Spring Boot functionality. Overall I think the integration works very well, specially the GraphQL Java Tools that enforces the code implementation.