-
Notifications
You must be signed in to change notification settings - Fork 312
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Extension to bring back file upload support #638
Comments
Can someone please share the requirements for file uploads at runtime? I've never done it myself. |
hi, wdym by requirements? dependencies? |
Hey, I mean what does the implementation of file uploads need to do. Knowing details about that will help drive the extension design, because we'll know what kinds of knobs and levers need to be exposed. |
For one thing, you have to change the type of request that's made from normal JSON data to multipart/form-data, you need to replace files in the payload with a reference to the file, etc, which means recursively searching the body for files. It's not that hard, just fiddly to get everything 100%. See this code for reference: Written by @lynxtaa which uses https://www.npmjs.com/package/extract-files Spec: https://github.com/jaydenseric/graphql-multipart-request-spec |
Thanks, I'll check those links out when the time comes.
what would "payload" be and what would "files in payload" mean? Does recursively searching the body mean the graphql document selection set? |
By payload, I mean the object being POSTed; You can see the variables get swapped out for nulls, then a |
Yep, also please accept a Readable stream from node or the web api, loading the entire file in memory using a Buffer should be optional and not the only way. |
I am getting close to reviewing that the new extension system has everything needed to support file uploads, and if not, make it so. I want this to happen give the demand from community so unless it would force major hacks into the lib I expect this supported. As for an actual extension for file uploads, that would be a different issue and I would welcome community help with that. I do think some first party extensions make sense for popular topics like this. But it won't be my immediate focus as I work to get the foundation done. |
are there any docs for extensions? |
Things I see needed here:
For an extension to pull this off, it will need to alter the Raw support could be added added. Only difference is that it would not have access to structured document to compare to the runtime schema map. Overall I see a viable path here. |
You won't be able to tell its TS type at runtime, this is how the other library handles it: |
For modern environments it seems like an overkill, I'm planning to make it simpler in my lib:
And I suggest you the same since NodeJS 18 supports |
Thanks, yeah saw that and the predicate helper. Actually, with Graffle, we can, because we have a runtime schema map (that is how custom scalars are transformed through their respective codecs). However, raw mode will not, and I'm inclined toward making it work across object and raw modes, even though using the schema map is the safest option. I may use both approaches, thus just making the raw mode a bit less safe which is pretty in line with its trade offs in the first place. |
Ok, I took a first stab at sketching the code and reviewing the referenced libraries. It turns out that our current extension system is powerful enough, if I got this right. Looks pretty simple actually! const UploadExtension = Graffle
.createExtension({ name: 'Upload' })
.anyware(({ pack }) => {
if (!isUsingUploadScalar(pack.input.variables)) return pack()
// TODO we can probably get file upload working for in-memory schemas too :)
if (pack.input.transport !== 'http') throw new Error('Must use http transport for uploads.')
const { exchange } = await pack()
const request = new Request(exchange.input.request, {
body: createUploadBody({
query: input.query,
variables: input.variables
}),
headers: {
'content-type': 'multipart/form-data'
}
})
return exchange({
...exchange.input,
request,
})
})
/** @see https://github.com/lynxtaa/awesome-graphql-client/blob/a3ecec2fdd3b72b005268a24b2364dd647dc6efd/packages/awesome-graphql-client/src/AwesomeGraphQLClient.ts#L79 */
const createUploadBody = () => '...' Can anyone point out any major flaws (to the extent you can make sense of the code :))? |
@jasonkuhrt Not a major flaw but the |
@lynxtaa, thanks, that sounds good, however the cloned request will have had the content type header explicitly set, so I wonder in that case what happens, can try it sometime later. |
You can find implementation and tests here https://github.com/jasonkuhrt/graphql-request/pull/937/files#diff-5900de164f1c79c9265931d2e60cb70651486646ea5ebddf46429dfbc72c10dc. I will likely merge this PR this weekend. Any feedback welcome. The extension is small but some adjustments to the extension system to support its needs added to the size of the diff. |
Perceived Problem
Loss of file upload support #400
Ideas / Proposed Solution(s)
The text was updated successfully, but these errors were encountered: