Blob data not included in multipart request in React Native
agj32mrgibbits opened this issue · 3 comments
I've been able to get apollo-upload-client setup and sending multipart requests for uploads but I cannot get it to actually send the payload along with the request.
Versions:
"@apollo/client": "^3.0.1",
"apollo-upload-client": "^17.0.0",
"graphql": "^15.3.0",
"graphql.macro": "^1.4.2",
"react": "16.13.1",
"react-native": "0.63.2",
Client setup:
const cache = new InMemoryCache();
const httpLink = createUploadLink({
uri: Config.API_URL,
});
const authLink = setContext((_, {headers}) =>
getItem('@clientToken')
.then((token) => {
if (!token) {
return;
}
return {
headers: {
...headers,
'X-Auth-Token': `${token}`,
},
};
})
.catch((err) => console.error(err)),
);
const errorLink = some error handling
const defaultOptions = {
query: {
fetchPolicy: 'no-cache',
errorPolicy: 'all',
},
};
const link = ApolloLink.from([authLink, errorLink, httpLink]);
export const client = new ApolloClient({
link,
cache,
defaultOptions,
});
Mutation:
mutation SaveTicketSignature(
$TicketID: Int!
$FullName: String!
$Signature: Upload!
) {
SaveTicketSignature(TicketID: $TicketID, FullName: $FullName, Signature: $Signature)
}
Action to send Mutation:
const TICKET_SAVE_SIGNATURE = loader('../../../gql/mutations/SaveTicketSignature.gql');
export const Actions = (update) => ({
...other actions...
saveTicketSignature: (TicketID, FullName, Signature) => {
console.info(`saving ticket signature for ticket ${TicketID}`);
client
.mutate({
mutation: TICKET_SAVE_SIGNATURE,
variables: {
TicketID: TicketID,
FullName: FullName,
Signature: Signature,
},
})
.then((result) => {
console.log('result', result);
})
.catch((err) => {
console.log('error',err);
});
},
});
Save Handler:
const handleSave = (sigResult) => {
console.log('sig saved');
const upload = new Blob([Buffer.from(sigResult.encoded,'base64')],{type: 'image/png'});
console.log('upload:',upload);
actions.saveTicketSignature(
ticketID,
fullName,
upload,
);
};
Console log:
LOG sig saved
LOG upload: {"_data": {"__collector": {}, "blobId": "330bffa9-ba34-4633-93ae-6f1375564e3f", "lastModified": undefined, "offset": 0, "size": 13029, "type": "image/png"}}
INFO saving ticket signature for ticket 18803
ERROR [Network error]: TypeError: Network request failed
LOG error [Error: Network request failed]
Server log:
[DEBUG] REQUEST:
POST /graphql HTTP/1.1
Host: x
Accept: */*
Accept-Encoding: gzip
Connection: Keep-Alive
Content-Length: 592
Content-Type: multipart/form-data; boundary=3326165f-d882-43df-817e-acd3fa604ae8
User-Agent: okhttp/3.12.1
X-Auth-Token: x
--3326165f-d882-43df-817e-acd3fa604ae8
content-disposition: form-data; name="operations"
Content-Length: 297
{"operationName":"SaveTicketSignature","variables":{"TicketID":18803,"FullName":"test","Signature":null},"query":"mutation SaveTicketSignature($TicketID: Int!, $FullName: String!, $Signature: Upload!) {\n SaveTicketSignature(TicketID: $TicketID, FullName: $FullName, Signature: $Signature)\n}\n"}
--3326165f-d882-43df-817e-acd3fa604ae8
content-disposition: form-data; name="map"
Content-Length: 29
{"1":["variables.Signature"]}
--3326165f-d882-43df-817e-acd3fa604ae8--
[DEBUG] status=422 body={"errors":[{"message":"failed to get key 1 from form"}],"data":null}
As you can see, it constructs the multipart request and variable map but it doesn't include the payload. Any ideas?
I'm rusty about the state of React Native and their non-standard FormData
implementation (it doesn't properly use the File
and Blob
globals), but are you sure you shouldn't be using the class ReactNativeFile
?
You might have to use a special scheme in the uri
to inline the blob, idk.
Note that in future versions I'll be removing out of the box hacks and workarounds for React Native; I only intend to support runtime environments that support web standards moving forwards.
Using ReactNativeFile with a base64 uri gives [Network error]: TypeError: Network request failed
and the request doesn't reach the server.
const upload = new ReactNativeFile({
uri: `data:image/png;base64,${sigResult.encoded}`,
type: 'image/png',
name: 'signature.png',
});
I've already disabled flipper in case that was messing with things.
apollo-upload-client
v18 has been published, which removes out of the box support for React Native. As per the release notes, you should be able to configure it to get things working in React Native but it won't be something I will be explicitly endorsing or supporting moving forwards.
It’s the responsibility of Facebook to adhere to web standards and implement spec-complaint Blob
, File
, and FormData
globals in the React Native environment.
If you manage to figure out the solution to your React Native issue, feel free to leave it here in a comment. Likewise, anyone else feel free to contribute suggestions.