Annotation processor for feign-graphql that generates Java records from GraphQL schemas at compile time.
Given a @GraphqlSchema-annotated interface, this processor:
- Parses the referenced
.graphqlschema file - Validates all
@GraphqlQuerystrings against the schema - Generates Java records for result types, input types, and enums
- Maps custom scalars to Java types via
@Scalarannotations
See the feign-graphql README for usage examples.
Nested result types are generated as inner records scoped to each query result. This ensures each query gets exactly the fields it selects, even when different queries target the same GraphQL type.
For a query like:
{
starship(id: "1") {
id name
location { planet sector }
specs { lengthMeters classification }
}
}The processor generates a single file with inner records:
public record StarshipResult(String id, String name, Location location, Specs specs) {
public record Location(String planet, String sector) {}
public record Specs(Integer lengthMeters, String classification) {}
}Two different queries can select different fields from the same GraphQL type without conflict:
// Query 1: selects location { planet }
public record CharByPlanet(String id, Location location) {
public record Location(String planet) {}
}
// Query 2: selects location { sector region }
public record CharByRegion(String id, Location location) {
public record Location(String sector, String region) {}
}An operation selecting several root fields gets a record with one component per field, mirroring the operation's own selection set rather than a single field's type:
query authorPage($authorId: ID!) {
books(authorId: $authorId) { id title }
reviews(authorId: $authorId) { id rating }
}public record AuthorPage(Optional<List<Books>> books, Optional<List<Reviews>> reviews) {
public record Books(String id, String title) {}
public record Reviews(String id, Integer rating) {}
}With a single root field the record still mirrors that field's type, so { character(id: "1") { id name } } keeps generating record CharacterResult(String id, String name).
If two queries use the same return type name but select different fields, the processor reports compilation errors on both methods showing which fields each selects:
error: Conflicting return type 'CharResult': method selects [id, email] but method 'query1()' already selects [id, name]
CharResult query2();
^
error: Conflicting return type 'CharResult': method selects [id, name] but method 'query2()' selects [id, email]
CharResult query1();
^
Input types and enums are generated as top-level files since they represent the full schema type:
public record CreateCharacterInput(String name, String email, Episode appearsIn) {}public enum Episode {
NEWHOPE,
EMPIRE,
JEDI
}Set generateDeprecated = false on @GraphqlSchema, or generateDeprecated = Toggle.FALSE on a single @GraphqlQuery, to hide @deprecated schema elements. Generated types leave out deprecated fields, input fields and enum values. A query that uses a deprecated field, argument, input field or enum value fails to compile:
GraphQL validation error at line 1, column 22: Field 'email' is deprecated (use emails instead) and generateDeprecated is false
Add as a provided dependency so it runs during compilation but is not included at runtime:
<dependency>
<groupId>io.github.openfeign.experimental</groupId>
<artifactId>feign-graphql-apt</artifactId>
<version>${feign.version}</version>
<scope>provided</scope>
</dependency>