javagraphqlgraphql-spqrgraphql-spqr-spring-boot-starter

How to treat a child class like its parent in GraphQL SPQR


I created a wrapper around BigDecimal. Basically just putting a bunch of friendlier methods on top of it. I use it for some of my POJO's fields, and it generates a schema like this

# Built-in java.math.BigDecimal
scalar BigDecimal

input BestDecimalInput {
  scale: Int!
}

Ideally, when I go to make a query I'd be able to treat it the same as BigDecimal. I have a constructor

public BestDecimal(double val) {
        super(val);
    }

which I'd hope it would allow me to use a regular floating point number in my query/mutation, but it doesn't seem to work that way. Basically if the below is a BestDecimal object, I want this to work just as if it were a BigDecimal.

myquery(number: 10){
  whatever
}

Solution

  • There's 2 things you can do.

    Check out the existing implementations of TypeMapper or TypeAdapter, e.g. OptionalIntAdapter (that makes OptionalInt act as an Integer), and make a new one based on that. Or see this answer for a custom scalar.

    You can hook it up using generator.withTypeMappersor generator.withTypeAdapters.

    Using the Spring Starter, you can add a TypeMapper like this:

    @Bean
    public ExtensionProvider<GeneratorConfiguration, TypeMapper> mappers() {
        //After IdAdapter is generally a safe place to insert custom mappers
        return (config, mappers) -> mappers.insertAfter(IdAdapter.class, new YouCustomMapper());
    }
    

    You can't wire a TypeAdapter directly, unfortunately. You'd have to wire it as a TypeMapper, InputConverter and OutputConverter at the same time, which is annoying. So instead, it's easier to add a custom module that adds a TypeAdapter:

    @Bean
    public ExtensionProvider<GeneratorConfiguration, Module> modules() {
        return (config, modules) -> modules.append(context -> 
             context.getSchemaGenerator().withTypeAdapters(new YourCustomAdapter()));
    }
    

    You can (and should) of course make a proper Module class instead of an anonymous one like I did.