javagraphqlgraphql-spqr

Custom Scalar Type in graphql-spqr


I graphql-spqr, java.util.Date is defined as Scalar. Is it possible to overwrite the serialization/deserialization of java.util.Date to get a different String representation of the date?

The ScalarStrategy mentioned in this answer was removed with the latest release.

public class Order {

    private String id;
    private Date orderDate; //GraphQLScalarType "Date"

    public Order() {
    }

    public Order(String id, String bookId, Date orderDate) {
        this.id = id;
        this.orderDate = orderDate;
    }

    public String getId() {
        return id;
    }

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

    public Date getOrderDate() {
        return orderDate;
    }

    public void setOrderDate(Date orderDate) {
        this.orderDate = orderDate;
    }
}

GraphQL Response:

{
  "data": {
    "createOrder": {
      "id": "74e4816c-f850-4d63-9855-e4601fa125f4",
      "orderDate": "2019-05-26T08:25:01.349Z", // --> 2019-05-26
    }
  }
}

Solution

  • ScalarStrategy isn't the proper way to achieve what you want. When you want to change how a Java type gets mapped to GraphQL, you normally provide a new (or customize an existing) TypeMapper.

    Take a look at the existing Date scalar implementation and implement your own in a similar way. Then implement a custom TypeMapper that simply always returns a static instance of that scalar from both toGraphQLType and toGraphQLInputType methods.

    public class CustomTypeMapper implements TypeMapper {
    
        private static final GraphQLScalarType GraphQLCustomDate = ...;
    
        @Override
        public GraphQLOutputType toGraphQLType(...) {
            return GraphQLCustomDate;
        }
    
        @Override
        public GraphQLInputType toGraphQLInputType(...) {
            return GraphQLCustomDate;
        }
    
        @Override
        public boolean supports(AnnotatedType type) {
            return type.getType() == Date.class; // This mapper only deals with Date
        }
    }
    

    To register it, call generator.withTypeMappers(new CustomTypeMapper().

    That said, since you're only trying to cut off the time part, you'd ideally use LocalDate here. You can make SPQR do that transparently by registering a TypeAdapter (which is nothing but a mapper + converter) but a simple mapper as explained above is a more efficient solution in your case. If you still decide to go the adapter way, you can inherit AbstractTypeAdapter<Date, LocalDate> and implement the conversion logic (should be trivial). Register via generator.withTypeAdapters or by registering it as a mapper and converters separately.