File uploads support via middleware #650
phegman
started this conversation in
Show and tell
Replies: 1 comment
-
I had a bit of issues with the above solution as it does not take nested inputs into account yet. And also I had to pack the file into a blob to make it work for me. This is my modified version: import { RequestMiddleware } from "graphql-request";
import { isArray, set } from "lodash";
import { once } from "node:events";
const toBlob = async (data: any) => {
if ("pipe" in data) {
const stream = data as NodeJS.ReadableStream;
if (!stream) throw new Error("");
const chunks: any[] = [];
const handler = (data: any) => {
chunks.push(data);
};
stream.on("data", handler);
await once(stream, "end");
stream.removeListener("data", handler);
return new Blob(chunks);
}
return new Blob([data]);
};
const isExtractableFile = <ValueType>(value: ValueType) => {
return (
(typeof File !== "undefined" && value instanceof File) ||
(typeof Blob !== "undefined" && value instanceof Blob) ||
(typeof Buffer !== "undefined" && value instanceof Buffer) ||
(typeof value === `object` && value !== null && `pipe` in value && typeof value.pipe === `function`)
);
};
//@ts-ignore
const isPlainObject = <T>(value: T): value is Object => value && [undefined, Object].includes(value.constructor);
const recursiveExtractFiles = (variableKey: string, variableValue: any, prefix: string): any => {
if (isExtractableFile(variableValue)) {
return [
{
variableKey: [`${prefix}.${variableKey}`],
file: variableValue,
},
];
}
if (isArray(variableValue) && variableValue.every((item) => isExtractableFile(item))) {
return variableValue.map((file, fileIndex) => {
return {
variableKey: [`${prefix}.${variableKey}.${fileIndex}`],
file,
};
});
}
if (isPlainObject(variableValue)) {
const ggg = Object.entries(variableValue)
.map(([key, value]: any) => recursiveExtractFiles(key, value, `${prefix}.${variableKey}`))
.flat();
return ggg;
}
return [];
};
export const requestMiddlewareUploadFiles: RequestMiddleware = async (request) => {
const files = Object.entries(request.variables || {}).flatMap(([variableKey, variableValue]) => {
return recursiveExtractFiles(variableKey, variableValue, "variables");
});
if (!files.length) {
return request;
}
const form = new FormData();
const parsedBody = JSON.parse(request.body as string);
for (const file of files) {
//remove file here to reduce request size
set(parsedBody, file.variableKey[0], null);
}
form.append("operations", JSON.stringify(parsedBody));
const map = files.reduce((accumulator, { variableKey }, index) => {
return {
...accumulator,
[index.toString()]: variableKey,
};
}, {});
form.append("map", JSON.stringify(map));
for (let index = 0; index < files.length; index++) {
const file = files[index];
form.append(index.toString(), await toBlob(file.file));
}
const { "Content-Type": _, ...newHeaders } = request.headers as Record<string, string>;
return {
...request,
body: form,
headers: newHeaders,
};
}; |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
File uploads were removed in #500 but I was able to add support to my setup via middleware. Hope this helps anyone that needs file uploads and wants to use this package.
Beta Was this translation helpful? Give feedback.
All reactions