Create a GraphQL API using Spring Boot

Fabio Hiroki - Jul 21 '19 - - Dev Community

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.

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 and then:

docker run -p5432:5432 -d postgres:11.4-alpine
Enter fullscreen mode Exit fullscreen mode

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. 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: "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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

type Pokemon {
    id: ID!
    name: String!
}

type Query {
    pokemon(id: ID!): Pokemon
}
Enter fullscreen mode Exit fullscreen mode

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:

@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;
}
Enter fullscreen mode Exit fullscreen mode

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:

@Component
public class Query implements GraphQLQueryResolver {

    public Pokemon getPokemon(Long id) {
        return new Pokemon(1L, "Pikachu");
    }
}
Enter fullscreen mode Exit fullscreen mode

We can now perform an request at our new endpoint to check if its response is our Pikachu.

Pikachu appearing

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:

# Searches a Pokemon with id 25 and returns its field 'name'
query {
  pokemon(id: 25){
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

We should expect the result on right column:

{
  "data": {
    "pokemon": {
      "name": "Pikachu"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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:

@Repository
public interface PokemonRepository extends JpaRepository<Pokemon, Long> {
}
Enter fullscreen mode Exit fullscreen mode

Then we change our Query class to autowire this bean and perform the real database fetch:

@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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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:

# src/test/resources/get-pokemon-by-id.graphql
query {
    pokemon(id: "1") {
        id
        name
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

@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"));
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

. . . . . . . . . . . . . . . . . . . . . . . . . . . .