graphql-spring-webclient
graphql-spring-webclient copied to clipboard
Using custom scalars in variables of GraphQLRequest
Problem
I am currently studying on a Spring Boot to Spring Boot GraphQL usage and found out GraphQL Spring Webclient (client-side) is lacking of a feature that GraphQL Spring Boot (server-side) has.
It is not possible to set a variable of scalar types in a GraphQLRequest
which will be sent to GraphQL Server where custom scalars (ie. Date, Void, Long) defined in its schema.
Client Side Mutation Query Example with Custom Scalar
However, when we try to execute a query (or mutation) as in the following Kotlin example, we have to supply serialized values to variables.
Below example is working because date as parsed to string in the format of GraphQL Server coercing expects.
private fun createAnnouncementsForDate(date: Date): Mono<Array<Announcement>> {
val graphqlRequest = GraphQLRequest.builder(Array<Announcement>::class.java)
.operationName("createAnnouncementByDate")
.query(mutationCreateAnnouncementByDate)
.variables(mapOf("date" to SimpleDateFormat("dd.MM.yyyy").format(date)))
.build()
return graphQLWebClient.post(graphqlRequest)!!
}
Below code block fails because Date value is not converted to String in a required format.
private fun createAnnouncementsForDate(date: Date): Mono<Array<Announcement>> {
val graphqlRequest = GraphQLRequest.builder(Array<Announcement>::class.java)
.operationName("createAnnouncementByDate")
.query(mutationCreateAnnouncementByDate)
.variables(mapOf("date" to date))
.build()
return graphQLWebClient.post(graphqlRequest)!!
}
Offered Solution
Just like in server-side, custom scalar type implementations can be supplied as GraphQLScalarType
beans that implements Coercing
interfaces in the client-side.
This can be done by a request interceptor in pre-execution layer.
Server Side Custom Scalar Definition Example
In server side, defining GraphQLScalarsType
beans by supplying implemented Coercing
interfaces allows you to handle the serialization and parsing values explicitly for custom scalars.
An example configration class below for java.util.Date
class :
@Configuration
public class GraphQLScalarsConfig {
@Bean
public GraphQLScalarType graphQLDateScalar() {
return GraphQLScalarType.newScalar().name("Date").coercing(new DateCoercing()).build();
}
}
DateCoercing class:
public class DateCoercing implements Coercing<Date, String> {
@Override
public String serialize(Object o) throws CoercingSerializeException {
return convertDateToString((Date) o);
}
@Override
public Date parseValue(Object o) throws CoercingParseValueException {
try {
return convertStringToDate(o + "");
} catch (ParseException e) {
throw new CoercingParseValueException(e);
}
}
@Override
public Date parseLiteral(Object o) throws CoercingParseLiteralException {
try {
return convertStringToDate(o + "");
} catch (ParseException e) {
throw new CoercingParseLiteralException(e);
}
}
}
Additionaly
I haven't tested what happens if a response has any scalar type. However, it seems deserialization and serialization must be done manually in that time either.
I may be missing some design concerns or basics, please blame me in such cases.