Skip to main content

GraphQL Upload

In gqless you can add File Upload support easily, following the GraphQL multipart request specification.

Preparation#

Normally, in the API servers, they implement graphql-upload, inbuilt in Apollo Server, or via mercurius-upload for Fastify.

Meanwhile in the client, to follow the GraphQL multipart spec, you can easily use extract-files.

npm install extract-files
npm install -D @types/extract-files

And then, use it in your QueryFetcher, like the following example:

Compatible with browser's File and Blob, and React Native using ReactNativeFile

import { extractFiles } from 'extract-files';
// ...
const queryFetcher: QueryFetcher = async function (query, variables) {
const extracted = extractFiles({
query,
variables,
});
if (extracted.files.size > 0) {
const form = new FormData();
form.append('operations', JSON.stringify(extracted.clone));
const map: Record<number, string[]> = {};
let i = 0;
extracted.files.forEach((paths) => {
map[++i] = paths;
});
form.append('map', JSON.stringify(map));
i = 0;
extracted.files.forEach((_paths, file) => {
form.append(++i + '', file as File);
});
const response = await fetch('/api/graphql', {
method: 'POST',
headers: {},
body: form,
mode: 'cors',
});
const json = await response.json();
return json;
}
// Fallback to regular queries
const response = await fetch('/api/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
variables,
}),
mode: 'cors',
});
const json = await response.json();
return json;
};

Usage#

Core#

Since the images are not serializable, gqless might be confused while using them, that's why you have to specify a special option nonSerializableVariables in your resolveds

import { resolved, mutation } from '../gqless';
resolved(
() => {
return mutation.uploadFile({
file,
})!;
},
{
//...
// You should specify this flag
nonSerializableVariables: true,
}
)
.then((data) => {
// ...
})
.catch((error) => {
// ...
});

React#

Since the images are not serializable, gqless might be confused while using them, that's why you have to specify a special option nonSerializableVariables in your useMutations

const [uploadFile, { isLoading, data, error }] = useMutation(
(mutation, file: File) => {
return mutation.uploadFile({
file,
})!;
},
{
// ...
// You should specify this flag
nonSerializableVariables: true,
}
);
Last updated on by Sam Denty