AdBlock Detected

It looks like you're using an ad-blocker!

Our team work realy hard to produce quality content on this website and we noticed you have ad-blocking enabled. Advertisements and advertising enable us to continue working and provide high-quality content.

Example of GraphQL with Quarkus

In previous posts, we saw an example of using GraphQL with Spring and Netflix’s DGS. Today, in this Example of GraphQL with Quarkus, we bring the same example, but it’s done entirely with Quarkus.

To accomplish this example, we will use Quarkus’ own libraries for persistence, such as Panache, and the GraphQL library for Quarkus, SmallRye, instead of Netflix’s DGS.

What is GraphQL?

GraphQL is a language created by Facebook that allows us to query and manipulate data for APIs. This method of making queries and obtaining responses enables us to retrieve only the fields we desire. This way, in extensive queries or responses with a lot of data, we can display only the necessary fields.

Hands On

For our example, we will use an in-memory relational database with H2. Our example will consist of three very basic entities: Bank, User, and Bank Account, where a user has one or more bank accounts in a bank, and a bank can have one or more bank accounts.

In our application, we will make use of Lombok to eliminate Java boilerplate.

Similar to how Spring Data provides JPA to simplify database control, in Quarkus, we use Panache. Through Hibernate, it offers the advantages of JPA and makes entity development easier. So, if you are already familiar with Spring Data, Panache shares many similarities, and understanding it should not require much effort.

You can use https://code.quarkus.io/ to generate the structure of your project.

Maven Dependencies for Using GraphQL in Quarkus

   <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-smallrye-graphql</artifactId>
    </dependency>

The previous dependency, “smallrye,” provides the necessary logic for integrating GraphQL into our application.

Remember, you can use the following command directly from your terminal if you’ve already created the project:

quarkus:add-extension -Dextensions="graphql"

Maven Dependencies for Using Persistence in Quarkus

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-jdbc-h2</artifactId>
    </dependency>
    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-hibernate-orm-panache</artifactId>
    </dependency>

As we mentioned earlier, it’s necessary to add the Panache dependency to provide persistence to our application.

GraphQL Schema and Domain Entities

In our previous example created with Netflix’s DGS and Spring Boot, we had to create and add our GraphQL schemas to the resources folder. But in this case with Quarkus and the SmallRye library, there’s no need to create the schemas, only the model. So, in this case, we will only create the domain objects to demonstrate how it works. Although this is not a good structure, it serves to make the example more understandable. This could be a typical use case for the Hexagonal Architecture since we’ve changed the technology, but the logic remains the same.

Creating the User Entity

For creating the entity, we make use of Lombok and javax annotations.

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class User {
    @Id
    @GeneratedValue
    private UUID id;
    private String firstName;
    private String lastName;
    private int age;
    private String address;
    private String country;
    private String city;
    @OneToMany(mappedBy = "user")
    private Set<Account> accounts;
    @ManyToOne()
    private Bank bank;
}

Creation of the Bank Entity

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class Bank {
    @Id
    @GeneratedValue
    private UUID id;
    private String name;
    private String country;
    @OneToMany(mappedBy = "bank")
    private Set<User> users;
    @OneToMany(mappedBy = "bank")
    private Set<Account> accounts;
}

Construction of the Account Entity

@Entity
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class Account {
    @Id
    @GeneratedValue
    private UUID id;
    private String name;
    private String alias;
    private BigDecimal amount;
    @ManyToOne(fetch = FetchType.LAZY)
    private User user;
    @ManyToOne(fetch = FetchType.LAZY)
    private Bank bank;
}

Creation of Mutation or Input Objects

When working with GraphQL, we will define a series of objects as input to our application. In this case, we will define a set of objects that will be stored in the database. In other words, we need input data for our application. The data defined below will be the data that gets stored.

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class UserInput {
    private String firstName;
    private String lastName;
    private int age;
    private String address;
    private String country;
    private String city;
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class BankInput {
    private String name;
    private String country;
}

@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
public class AccountInput {
    private String name;
    private String alias;
    private BigDecimal amount;
    private UUID userId;
    private UUID bankId;
}

Once we have finished all the domain objects and the input, it’s time to create the repository layer, which will be implemented with PanacheRepository.

Creation of Repositories with PanacheRepository

By using PanacheRepository, we will create the Repository layer. This interface implements query and save methods.

@ApplicationScoped
public class UserRepository implements PanacheRepository<User> {
}

@ApplicationScoped
public class BankRepository implements PanacheRepository<Bank> {
}

@ApplicationScoped
public class AccountRepository implements PanacheRepository<Organization> {
}

Queries and Mutations with GraphQL API in Quarkus

Once we have finished creating the entities and the repository, it’s time to add the necessary logic to perform queries and mutations with GraphQL in Quarkus. To do this, we will make use of the following annotations:

  • @GraphQLApi: This annotation at the beginning of the class indicates that it will be a GraphQL endpoint bean.
  • @Query: Indicates that this method will be a query.
  • @Mutation: Added to a method indicates that it’s a mutation request.
  • @Name: This annotation will be used for methods that have input parameters.

GraphQL Query in Quarkus

Next, we will see how to perform queries for the “account” schema.

@GraphQLApi
@RequiredArgsConstructor
public class AccountQuery {

    private final AccountRepository repository;

    @Query("accounts")
    public List<Account> findAll() {
        return repository.findAll().stream().collect(Collectors.toList());
    }

    @Query("account")
    public Account findById(@Name("id") Long id) {
        return repository.findById(id);
    }
}

This class, in which we will perform two different queries, is marked with @GraphQLApi to indicate that it is a GraphQL Query class. Then, in the methods responsible for making the query, we have added the @Query annotation with the name of the query.

Mutation in GraphQL with Quarkus

The Mutation type in GraphQL is responsible for performing a save or a request that can change the state, similar to what would be a POST, PATCH, or PUT request in REST.

To perform mutations in GraphQL, we will add the @GraphQLApi annotation to the class. In each method responsible for performing a mutation, we will add @Mutation(“name”). Let’s see an example:

@GraphQLApi
@RequiredArgsConstructor
public class AccountMutation {

    private final AccountRepository accountRepository;

    private final BankRepository bankRepository;

    private final UserRepository userRepository;

    @Mutation("createAccount")
    @Transactional
    public Account createAccount(@Name("account") AccountInput account) {
        User user = userRepository.findById(account.getUserId());
        Bank bank = bankRepository.findById(account.getBankId());

        var accountToSave = new Account(null,
                account.getName(), account.getAlias(), account.getAmount(), user, bank);
        accountRepository.persistAndFlush(accountToSave);
        return accountToSave;
    }
}

It’s important to note that in the method, we have added @Name because we are adding an input, meaning we want to save a new account.

Testing GraphQL with Quarkus

Once we have generated our code, the first thing we will do is run our Quarkus application.

mvn compile quarkus:dev

If everything is working correctly, we should see something like this:

__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2021-05-08 16:42:23,139 INFO  [io.quarkus] (Quarkus Main Thread) graphql-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 1.13.3.Final) started in 1.023s. Listening on: http://localhost:8080
2021-05-08 16:42:23,140 INFO  [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-05-08 16:42:23,141 INFO  [io.quarkus] (Quarkus Main Thread) Installed features: [agroal, cdi, hibernate-orm, hibernate-orm-panache, jdbc-h2, mutiny, narayana-jta, smallrye-context-propagation, smallrye-graphql]
2021-05-08 16:42:23,141 INFO  [io.qua.dep.dev.RuntimeUpdatesProcessor] (vert.x-worker-thread-7) Hot replace total time: 2.384s 

GraphQL with Quarkus starts at the endpoint http://localhost:8080/q/graphql-ui/. If we access that endpoint, we will see the GraphQL interface for making queries.

GraphQL Interface in Quarkus
 | Example of GraphQL with Quarkus
GraphQL Interface in Quarkus

Let’s run a few queries to see how it works. We’ll start with a mutation because it’s necessary to save a bank first.

mutation CREATE {
   createBank(bank:
      {name: "Santander", country:"Spain"}) {
        id
        name
        country
      }   
}
Mutation Response with GraphQL and Quarkus
|Example of GraphQL with Quarkus
Mutation Response with GraphQL and Quarkus

Once we’ve saved a bank, we’ll make a request to retrieve all of them:

{
 banks{
  id
  name
  country
 }
}
 

With the previous query, we will display all the banks in our database, which in our case is just one. The fields that will appear in the search will be those specified in the query, in this case, we will display id, name, and country.

Query con GraphQL en Quarkus
Query with GraphQL in Quarkus

and finally, let’s try to search for a bank by id.

{
 bank(id:1){
  id
  name
  country
 
 }
}
 
Query by Id with GraphQL and Quarkus
Query by Id with GraphQL and Quarkus

Now, we’re going to run the same query again, but with fewer fields in the request.

{
 bank(id:1){
  id
 
 }
}
Query showing a single field.
Query showing a single field.

In the previous query, only the ID field is included, so the response will contain only that field.

As mentioned near the beginning of the article, when using the smallrye library, GraphQL schemas are automatically generated without the need for manual implementation. If you want to view the generated schemas, you can access the endpoint http://localhost:8080/graphql/schema.graphql for the generated types.

Conclusion of GraphQL Example with Quarkus

Just like the Netflix DGS library, the SmallRye library for Quarkus provides an API for using GraphQL in Quarkus through annotations.

GraphQL has emerged as a good alternative for all applications and architectures that rely on REST services, bringing many improvements and conveniences to any API. We’ve seen some of these enhancements in our example. When combined with a framework like Quarkus, designed for cloud environments, it makes for a perfect combination to create a solid API.

If you want to see the complete example, you can find it on our GitHub.

Leave a Reply

Your email address will not be published. Required fields are marked *