graphql-zeus
graphql-zeus copied to clipboard
Native support for file uploads
Hello!
I would love it if Zeus natively supported file uploads. I'll probably open a PR in the few weeks.
Related works:
Wait Ill try to improve the plug-in system so produce lib script can take them into consideration
What's the status of this? Is there a PR or a branch about file uploads?
Hey @BlueSialia!
Aexol is (at this point) against adding dependencies: the generated code should be standalone until Zeus has a plugin system.
I created a userland implementation for a few features that I needed:
https://git.inpt.fr/inp-net/centraverse/-/blob/main/packages/app/src/lib/zeus.ts#L51-86
It relies on extract-files
I cannot figure out how you are managing to send files like that. I keep geting errors about the map fields of https://github.com/jaydenseric/graphql-multipart-request-spec being defined incorrectly. Or the variable not being recognized as a variable.
As of now, I have this monstrosity:
const isExtractableFile = (value: any) => {
return value instanceof Buffer;
}
const scalars = ZeusScalars({
JSONObject: {
encode: (e: unknown) => stringifyObject(e, { indent: '', singleQuotes: false }),
},
});
const graphql = (type: 'query' | 'mutation', options?: { scalars: any }) => {
const headers: { [key: string]: string } = {};
headers['Authorization'] = `Bearer 0123456789`;
return Thunder(async (query, variables) => {
let preQuery: string, body: BodyInit;
const { clone, files } = extractFiles(variables, isExtractableFile, 'variables');
if (files.size > 0) {
const map: { [key: string]: string } = {};
preQuery = '(';
[...files.values()].forEach((value, i) => {
map[`${i}`] = value;
preQuery = `${preQuery}$${value[0].replace('variables.', '')}: ${value[0].split(GRAPHQL_TYPE_SEPARATOR)[1]}!, `;
});
preQuery = preQuery.replace(/, $/, ')');
body = new FormData();
body.set('operations', JSON.stringify({ query, variables: clone }).replace('mutation {', `mutation ${preQuery} {`).replace(/__\$GRAPHQL__/g, '__GRAPHQL__'));
body.set('map', JSON.stringify(map));
for (const [i, [file]] of [...files].entries()) body.set(`${i}`, file);
} else {
headers['Content-Type'] = 'application/json';
body = JSON.stringify({ query, variables });
}
const response = await fetch(`${process.env.API}/graphql`, { body, method: 'POST', headers });
return response
.json()
.catch((e) => {
throw new Error(`The server returned an error. ${inspect(e)}`);
})
.then((graphqlResponse: GraphQLResponse) => {
if (graphqlResponse.errors || !response.ok) throw new GraphQLError(graphqlResponse);
return graphqlResponse.data;
});
})(type as any, options);
};
const sendNode = (body: {
id: string;
name?: string;
description?: string;
data: object;
objects?: Buffer[];
}) => {
const variables: { [key: string]: Buffer } = {};
for (let i = 0; i < body.objects.length; i++) {
const object = body.objects[i];
variables[`ZEUS_VARobject${i}${GRAPHQL_TYPE_SEPARATOR}Upload`] = object;
}
return graphql('mutation', { scalars })({
createNode: [{ ...body, objects: body.objects.map((_, i) => $(`object${i}`, 'Upload')) }, { createdAt: true }],
}, { variables }
).catch(() => {
graphql('mutation', { scalars })({
updateNode: [{ ...body, objects: body.objects.map((_, i) => $(`object${i}`, 'Upload')) }, { createdAt: true }]
}, { variables }
).catch(error => {
console.error(inspect(error).split('\n').map(str => str.trim()).join(' - '));
});
});
}
@aexol What is the $ function for? I'm using it and I feel it only makes my life more difficult. I need to do multiple weird stuff to complete/fix the use of variables. See the preQuery variable or the ZEUS_VARobject${i}${GRAPHQL_TYPE_SEPARATOR}Upload line.
Am I just using it completely wrong? Am I missing something else?
Thanks.