Summary
We're trying to utilize FileUpload functionality to send images to our backend. We receive Field 'photo' of variable 'photo' has coerced Null value for NonNull type 'File!' error message every time we try to do so.
Version
2.4.5
Description
We have scalar File in our schema. As documentation states we added "File" to "com.apollographql.apollo.api.FileUpload" to custom mappings in apollo task as following:
apollo {
customTypeMapping.set(
mapOf(
// other mappings
"File" to "com.apollographql.apollo.api.FileUpload"
)
)
}
We have following mutation:
mutation UploadPhotoMutation($photo: File!) {
uploadPhoto(photo: $photo)
}
and use it like this:
val fileUpload = object : FileUpload(mimeType, Uri.fromFile(file).path!!) {
override fun contentLength() = file.length()
override fun fileName() = file.name
override fun writeTo(sink: BufferedSink) {
sink.writeAll(file.source().buffer())
}
}
apolloClient.mutate(UploadPhotoMutation(fileUpload))
.execute()
.first()
Here is you can see operation.variables() state during OperationRequestBodyComposer.compose call. So all variables are present.

Bus as soon as InputFieldJsonWriter.writeCustom is called it returns GraphQLNull value for call customTypeAdapter.encode(value) and writes null to photo field.

And in default implementation DEFAULT_ADAPTERS for scalarTypeAdapters to which defaults scalarTypeAdapters.adapterFor
mapOf("com.apollographql.apollo.api.FileUpload" to object : CustomTypeAdapter<FileUpload> {
override fun decode(value: CustomTypeValue<*>): FileUpload {
// TODO: is there a valid use case for decoding a FileUpload or should we throw here?
return FileUpload("", value.value?.toString() ?: "")
}
override fun encode(value: FileUpload): CustomTypeValue<*> {
return GraphQLNull
}
})
So call to encode will always return GraphQLNull and field photo is mapped to null instead of real value
Although worth to mention that we use it in multiplatform module.
After further investigation it seems that problem is json returned from composeRequestBody(scalarTypeAdapters: ScalarTypeAdapters) call in generated UploadPhotoMutation.
It returns {“operationName”:“UploadPhotoMutation”,“variables”:{“photo”:null},“query”:“mutation UploadPhotoMutation($photo: File!) { uploadPhoto(photo: $photo) }“}
But it also should contain map="{\"photo\": [\"variables.photo\"]}" and photo=@"/path/to/image/image.jpg" as formdata parameters
Multiplatform doesn't have support for FileUpload yet. FileUpload is working with reflection on the JVM (see #2570) so it'll have to be implemented differently in multiplatform.
Oh, I see, thanks. Btw is FileUpload is implemented on iOS? If so we could switch to native versions instead of multiplatform
It is! https://www.apollographql.com/docs/ios/mutations/#uploading-directly-with-apollo
thank you so much!
I also recommend reading this caveat right above what Martin linked 😇 - that also might be a way to think about file uploads on multiplatform that could be easier to achieve.