Getting started with micronaut and GraphQL-SPQR

Table of Content

In this tutorial I will explain step-by-step how to get started with micronaut and a GraphQL SPQR API. Tested with GraphiQL and JUnit.

What you need:

  • a JDK; we use OpenJDK-11
  • micronaut in your PATH (for the mn-command; version 1.2.5)
  • a Text-Editor or a source-code-editor like Visual Studio Code
  • a terminal in which you can enter your commands (Visual Studio Code already ships with a terminal)

Recommended VSCode-extensions for Java:

  • Debugger for Java (Microsoft)
  • Gradle Tasks (Richard Willis)
  • Java Dependency Viewer (Microsoft)
  • Java Extension Pack (Microsoft)
  • Java Test Runner (Microsoft)
  • Language Support for Java(TM) by Red Hat (Red Hat)

Step 1: Create a micronaut project

To create a micronaut project, we have to execute the mn command.
This command will generate a project-folder in the folder you are executing it.
Open up your prompt and navigate to the folder in which you want to generate the project and execute:

$ mn create-app com.example.getting-started-with-micronaut-and-graphql-spqr --features file-watch
$ cd getting-started-with-micronaut-and-graphql-spqr

This command creates a project-folder with the name getting-started-with-micronaut-and-graphql-spqr and uses com.example as the default package for our application.
Additionally, we are adding the file-watch-feature which will be needed for hot-reload.
It will watch for file-changes in the src/main-folder, stop our app (how to automatically restart we will see later).

Step 2: Add GraphQL with SPQR

To use GraphQL-SPQR in our example, we first need to add it to our dependencies. Add these lines into your dependencies-section of your gradle.build.

implementation "io.micronaut.graphql:micronaut-graphql"
implementation "io.leangen.graphql:spqr:0.10.0"

And don’t forget to add options.incremental = false to the tasks.withType(JavaCompile)-section to have a fully functional hot-reloadable project.

Add this to your application.yml in your src/main/resources-folder.

graphql:
  enabled: true
  path: /api
  graphiql:
    enabled: true

It will enable GraphQL and set the path to /api. Additionally we enable GraphiQL to have a Web-Interface in which we can test our API.

Step 3: GraphQL-Factory, HelloService and HelloMessage

Now we can implement our GraphQL-API.

Instead of creating a GraphQL-schema from a graphqls-file, we now use a domain-class called HelloMessage (/src/main/java/com/example)

package com.example;

import io.leangen.graphql.annotations.GraphQLQuery;

public class HelloMessage {
    private String name;

    HelloMessage(String name) {
        this.name = name;
    }

    @GraphQLQuery(name = "message")
    public String getMessage() {
        String retName = this.name;
        if (this.name == null || this.name.trim().length() == 0) {
            retName = "World";
        }
        return "Hello " + retName + "!";
    }

}

This domain contains a String name, which holds the name for our Hello-message and a method called getMessage, which returns Hello World!, if the name-String is empty.
Important for this method is the annotation, which maps its output to the message-key of our hello-query.

To get data (e.g. from a database), we need a service.
In the folder src/main/java/com/example create a file with the name HelloService.java and paste this content.

package com.example;

import io.leangen.graphql.annotations.GraphQLArgument;
import io.leangen.graphql.annotations.GraphQLQuery;

public class HelloService {

    @GraphQLQuery(name = "hello")
    public HelloMessage hello(@GraphQLArgument(name = "name") String name) {
        return new HelloMessage(name);
    }
}

The hello-method will be executed when the hello-query is getting executed by our client. It has one argument name.
The Service constructs and returns our domain-class.

Now as we have created the service and our domain, we can proceed putting it all together in the GraphQLFactory.

In the folder src/main/java/com/example create a file with the name GraphQLFactory.java and paste this content.

package com.example;

import javax.inject.Singleton;

import graphql.GraphQL;
import graphql.schema.GraphQLSchema;
import io.leangen.graphql.GraphQLSchemaGenerator;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;

@Factory
public class GraphQLFactory {

    @Bean
    @Singleton
    public GraphQL graphQL() {
        HelloService helloService = new HelloService();

        GraphQLSchema schema = new GraphQLSchemaGenerator()
            .withBasePackages("com.example")
            .withOperationsFromSingleton(helloService)
            .generate();

        return new GraphQL.Builder(schema).build();
    }

}

First, we create our Hello-Service, then construct our schema, using the generator, then adding the service and finally building and returning our GraphQL-API.
Important are the annotations, which make our methods available as a bean.

Step 4: Testing with GraphiQL

Run the example with:

$ gradlew run --continuous

And navigate your browser to http://localhost:8080/graphiql

In the left textarea enter following query:

{
  hello {
    message
  }
}

You will see the following output:

{
  "data": {
    "hello": {
      "message": "Hello World!"
    }
  }
}

Now let’s test the argument with

{
  hello(name:"Alice") {
    message
  }
}

You will see the following output:

{
  "data": {
    "hello": {
      "message": "Hello Alice!"
    }
  }
}

Step 5: Testing with JUnit (with Parameterized)

Add this entry to your build.gradle-dependencies. It will allow you to use Parameterized-tests:

testImplementation("org.junit.jupiter:junit-jupiter-params")

Create a JUnit-Test-Class HelloTest(HelloTest.java) in src/test/java/com/example and paste the content below.

package com.example;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;

import java.util.LinkedHashMap;

import javax.inject.Inject;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

import graphql.ExecutionResult;
import io.micronaut.test.annotation.MicronautTest;

@MicronautTest
public class HelloTest {

    @Inject
    public GraphQLFactory graphQL;

    @Test
    public void defaultMessage() {
        ExecutionResult result = graphQL.graphQL().execute("{ hello { message } }");

        assertNotNull(result.getData());

        LinkedHashMap<String, LinkedHashMap<String, String>> hello = result.getData();
        assertEquals(hello.get("hello").get("message"), "Hello World!");
    }

    @ParameterizedTest
    @ValueSource(strings = { "Alice", "Bob" })
    public void message(String name) {
        ExecutionResult result = graphQL.graphQL().execute("{ hello(name:\""+name+"\") { message } }");

        assertNotNull(result.getData());

        LinkedHashMap<String, LinkedHashMap<String, String>> hello = result.getData();
        assertEquals(hello.get("hello").get("message"), "Hello "+name+"!");
    }

}

If you are using an IDE like eclipse or VSCode
Don’t forget to run gradlew eclipse before executing the tests!

That’s it.

Best Page Builder Plugin
Best Digital Asset Library
Find amazing stock images
Feel free to leave a comment
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x