Skip to content

GraphQL endpoints in Java

QLive inherits its basic GraphQL flavor – Spring Boot, Java and jOOQ – from DomainQL. What it replaces is DomainQL’s rudimentary data fetching: in its place sits a graph querying engine that analyzes a query and splits it into the optimal number of nicely joined SQL queries.

The wiring itself is Wire up a Spring application. This page is what the pieces are.

A logic bean is where your GraphQL methods live. Three annotations carry almost all of it:

QueryAndMutationExample.java
import io.github.qlivedev.qlive.api.annotation.QLiveLogic;
import io.github.qlivedev.qlive.api.annotation.QLiveQuery;
import io.github.qlivedev.qlive.api.annotation.QLiveMutation;
import jakarta.validation.constraints.NotNull;
@QLiveLogic
public class QueryAndMutationExample
{
@QLiveQuery
public MyResult myQuery( … )
{
// …
}
@QLiveMutation
public MutationResult myAction( … )
{
// …
}
}

@QLiveLogic is a Spring meta annotation: it declares that this is a Spring bean and that it contains GraphQL methods.

@QLiveQuery declares that the given method is part of the GraphQL schema. The input types, output types and scalars it uses are added to the schema automatically. @QLiveMutation is the same for write side. You should declare all changing actions as mutations.

Logic definitions take precedence over the definitions coming from the database POJOs. This allows overriding the types from the database to either disambiguate types or to extend functionality. See Replace a generated type.

The query document service is the central data access service for QLive. It analyzes the currently executed GraphQL query and builds a query plan spanning one or more SQL queries. QueryConfig instances control how that plan is executed – offset and page size for paging, a condition, and the sort fields.

buildQuery(type, env, config).execute() is the whole of the calling convention: type is the runtime row class, env is the GraphQL environment carrying the current selection, and config is the QueryConfig input. What comes back is a query document – the rows, plus the config they were fetched with, so that the client can apply a delta to it and ask again.

An application can have as many endpoints over the service as it wants, with as many configurations, and they may share row types. You are in control before you invoke the service and after you receive the result:

The tables are the starting point. jOOQ generates POJOs that mirror them; your logic beans and handwritten models sit alongside; and the schema is what all of that adds up to. Nothing declares the schema separately – see Unified Domain.