I am trying to create a custom input type using vanilla graphql (ie: non-apollo).
ref: https://github.com/jaydenseric/apollo-upload-server/issues/9
Got any examples for uploading files?
@appjitsu, for an app I'm working on where I need to be able to receive multiple uploaded files as part of a mutation, I came up with the idea of accepting a multipart/form-data request where the first part is application/graphql and the subsequent ones are arbitrary files that are associated with the mutation.
The multipart/form-data handling is a piece of middleware, so it ought to be reusable. I've uploaded it here along with a very simple usage example: https://gist.github.com/papandreou/abd7b8f00f7bff699a06e21330a2004a
I have no idea if there's a better, more "official" way of achieving the same thing, though :)
Perhaps not exactly what you are after since it does use Apollo, but you might find jaydenseric/apollo-upload-examples helpfull.
@appjitsu, please take a look at some of the suggestions for handling file upload. Closing this issue for, feel free to re-open if you believe it's unresolved.
memo for who wanna use other client with import { GraphQLUpload } from 'apollo-upload-server'
nodejs
import fs from 'fs'
import FormData from 'form-data'
import axios from 'axios'
let o = {
query: `mutation ($file: Upload!) {
uploadFile (file: $file)
}`,
variables: {
file: null
}
}
let map = {
'0': ['variables.file']
}
let fd = new FormData()
fd.append('operations', JSON.stringify(o))
fd.append('map', JSON.stringify(map))
fd.append(0, fs.createReadStream(`${__dirname}/app.js`), 'app.js')
let res = await axios.post('/graphql', fd, {
headers: {
...fd.getHeaders()
}
})
expect(res.status).to.be.equal(200)
expect(res.data).to.be.jsonSchema(schema)
Thanks so much @up9cloud. This is a really weird api if you ask me. Thanks to you I was able to make a function that correctly makes the file map from a graphql variables object. Here it is in case this helps someone: https://github.com/zackify/react-uploader/blob/master/src/clients/graphql/create-file-map.js
I no longer use Apollo on the server or client these days, and use just the vanilla GraphQL.js API.
There is a simple server side example of both approaches here:
https://github.com/jaydenseric/graphql-upload#class-graphqlupload
And the best reference for a client library that constructs a fetch request handling possible files is here:
An example of uploading a file from a server environment:
Alternatively, the GraphQL multipart request spec has example cURL requests:
https://github.com/jaydenseric/graphql-multipart-request-spec#curl-request
This is a really weird api if you ask me.
Rest assured, the spec has to be the way it is for good performance and to handle all sorts of situations like a client disconnecting half way through uploading several files, or using the same file as a variable in multiple mutations.
Only library authors really need to understand how it works; users of server and client implementations can take it for granted.
@jaydenseric thank you for the reply! The spec being that way now makes sense to me. Your client example helps a lot as well.
@jaydenseric have to thank you again! Spent hours online to find a client solution and not using Apollo. The client demo is very helpful. Thanks!
For future readers, note the header is using { Accept: 'application/json' } when you upload file. I was using 'Content-Type': 'application/json' which gave me error and I didn't notice in the beginning.
The operation input in the graphqlFetchOptions(operation) is something like
{
query: `
mutation UploadFile($file: Upload!) {
uploadFile(file: $file) {
id
}
}
`,
variables: {
file: event.target.files,
},
}
The backend you can use @jaydenseric's package graphql-upload if you do not want to use any framework like Apollo.
@up9cloud after 3 days looking for a solution, your solution worked for me, thanks my friend, you're the real MVP
@up9cloud thanks! Your approach was very useful to me as well 馃憤
@up9cloud Thank you very much !
For one who has error ...createReadStream is not a function...
I changed
fd.append(0, fs.createReadStream(${__dirname}/app.js), 'app.js')
to
fd.append(0, file)
It will work but I don't understand why or what is the different.
I appreciate some explanations. Thanks again !
Most helpful comment
memo for who wanna use other client with
import { GraphQLUpload } from 'apollo-upload-server'