Search code examples
javagraphqlquarkussmallrye

Quarkus GraphQL typesafe query is building an unexpected nested query


I am building a Quarkus app that queries the Linear GraphQL API at: https://api.linear.app/graphql

When I use the dynamic query like the following it builds the correct query and returns the correct result:

@Inject
@GraphQLClient("linear")
DynamicGraphQLClient dynamicClient;

@GET
@Path("/dynamicteams")
@Produces(MediaType.APPLICATION_JSON)
@Blocking
public JsonObject getTeams() throws Exception {

    Document query = document(
        operation(
            field("teams",
                field("nodes",
                    field("id"),
                    field("name")
                )
            )
        )
    );

    Log.info("Query: " + query.build());
    // ie Query: query { teams { nodes { id name } } }

    Response response = dynamicClient.executeSync(query);

    return response.getData();
}

sample response (anonymised):

{"teams":{"nodes":[{"id":"my-id-1","name":"Team 1"},{"id":"my-id-2","name":"Team 2"}]}}

But when I try the same thing using the TypeSafe variant , it builds a query that looks like the following. Note that the correct query is now nested inside a "teams" property.

query teams { teams { nodes { id name } } }

@Inject
LinearClientApi typesafeClient;

@GET
@Path("/safeteams")
@Produces(MediaType.APPLICATION_JSON)
@Blocking
public TeamsResponse getSafeTeams() {
    TeamsResponse resp = typesafeClient.getTeams();
    Log.info("Response: " + resp.toString());
    return resp;
}

and the relevant classes are:

@GraphQLClientApi(configKey = "linear-typesafe")
public interface LinearClientApi {

    TeamsResponse getTeams();
}

public class TeamsResponse {

  private List<TeamNode> nodes;

  public List<TeamNode> getNodes() {
      return nodes;
  }

  public void setNodes(List<TeamNode> nodes) {
      this.nodes = nodes;
  }
}

public class TeamNode {
    private String id;
    private String name;

    // Getters and setters
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The "teams" label is coming from the TeamsResponse getTeams();. If I override the name using the @Query annotation:

@Query(value="XXX")
TeamsResponse getTeams();

the query is built as:

query XXX { XXX {nodes {id name}} }

So, how am I generating that "nesting"? It must be obvious but I cannot see it.

Thanks, Murray


Solution

  • That's not 'nesting' your query into another query, that is just a feature of the GraphQL language. The typesafe client automatically wraps your query into a named operation (and it uses the name of the query as the name of the operation, hence the 'duplication'), whereas dynamic client, unless you specify a name, wraps it into an unnamed operation. So, generally, you get query OPERATION_NAME { QUERY_NAME { nodes { id name } } }, but in the dynamic client case, because you didn't specify a name, the OPERATION_NAME is omitted.