Open jasonkuhrt opened 4 years ago
Until there's some built-in solution, for those looking to incorporate https://github.com/jaydenseric/graphql-upload, they can do so by the following:
import { server, schema } from 'nexus'
import { GraphQLUpload, graphqlUploadExpress } from 'graphql-upload'
server.express.use(
'/graphql',
graphqlUploadExpress(),
)
schema.scalarType(GraphQLUpload)
And to test it out, use a GraphQL Client like Altair that supports file uploads and create a mutation like so:
import { schema } from 'nexus'
schema.mutationType({
definition(t) {
t.field('uploadFile', {
args: {
file: schema.arg({ type: 'Upload', required: true}),
},
type: 'Boolean',
resolve: async (root, { file }, ctx) => {
const { filename } = await file
console.log(filename)
return true
},
})
},
})
@leungandrew Does this solution still work on ^0.26.0-next.14
I have the exact implementation and receiving error nexus:server:graphql Missing multipart field ‘operations’
Yeah, I just attempted using ^0.26.0-next.14
and seems to have broke the workaround. I don't have a solution around this.
It appears https://github.com/graphql-nexus/nexus/releases/tag/0.26.0-next.8
introduced this error. It works from .7
and below. Maybe you can dive a bit deeper into what got introduced in .8 that broke this?
0.26.0 natively supports file uploads, as they've switched to apollo-server. You can now do this:
import { GraphQLUpload } from 'apollo-server-core';
import type { FileUpload } from 'graphql-upload';
export type UploadRoot = Promise<FileUpload>;
schema.scalarType({
// Why we need the bang: https://github.com/apollographql/apollo-server/blob/570f548b88750a06fbf5f67a4abe78fb0f870ccd/packages/apollo-server-core/src/index.ts#L49-L56
...GraphQLUpload!,
rootTyping: 'UploadRoot',
})
Unfortunately, the version of graphql-upload that apollo-server uses causes crashes on Node 13+. I used resolutions for forcibly bump it, as the API is compatible and there's no way to turn that version off to inject your own (Nexus would need to thread through a setting).
@bkrausz maybe do you have a full example also showing the resolver implementation for a storange in the file system? would really appreciate it. Thanks anyway for this mention! Do you also know the start date of 0.26.0? Will it also fix the Error code 500 bugs? Will we still be able to use express middlewares?
a full example also showing the resolver implementation for a storange in the file system?
async resolve(_root, { file }, ctx) {
const { filename, mimetype, createReadStream } = await file as UploadRoot,
// Do something with filename, call createReadStream() to get a stream
// ...
}
You can lookup how to use Node streams to do what you want with a file (I pipe it into an S3 upload). StackOverflow has examples.
Do you also know the start date of 0.26.0?
No clue, I don't work directly on Nexus, just a user
Will it also fix the Error code 500 bugs?
I don't know what 500 bugs you're referring to...
Will we still be able to use express middlewares?
Yes, server.express.use
is still available.
Thanks alot @bkrausz ! Regarding the 500'er bugs: out of the box when u rise an errror on the backend it will be send to the frontend as a 500er error. The common way of sending errors with graphql however is to rise an error but the server should send a 200er to the frontend. I managed this issue with a custom middleware right now. Switching to apollo server could fix this issue too.
I don't want to pollute this upload thread with it but i hope @jasonkuhrt could reveal this to us.
@leungandrew Does this solution still work on
^0.26.0-next.14
I have the exact implementation and receiving errornexus:server:graphql Missing multipart field ‘operations’
i also got this error. Could you solve it?
i am using nexus 0.26.1 and @bkrausz 's code snippet
Are you adding GQL upload middleware yourself? You shouldn’t because then you’d have 2 copies running at the same time. My 2 snippets are the only code I’m using to enable uploads.
@bkrausz no, i just use nexus inside nextjs (according to the example), maybe the middleware is missing in that setup?
@macrozone I'm quite unfamiliar with the nextjs side of Nexus, it's possible.
how nexus uses apollo server in case of nextjs? It uses
const app = require("nexus").default;
export default app.server.handlers.graphql;
so this handler is responsible for graphql and forwards it to apollo-server. Does this use any middleware? Can i wrap it with the middleware?
update: setting
export const config = {
api: {
bodyParser: false
}
}
on the nextjs api route does not trigger the Missing multipart field ‘operations’
-error, but the resolver will not be called and the request hangs.
Edit: it then successfuly reads the file, but after that its passed to the graphql part of the multipart request. There it tries to parse the query from the body and this hangs. It will eventuelly passed torawBody
and then hangs, similar as described here: https://github.com/stream-utils/raw-body/issues/57
it seems that its then already parsed, but nexus does not handle this correctly
ok i found the problems:
first, this is required i the api rout:
export const config = {
api: {
bodyParser: false
}
}
second, the uploadMiddleware is indeed called and sets req.filePayload
, but it was forgotten to use that in the serverless version.
Similarly to https://github.com/apollographql/apollo-server/blob/main/packages/apollo-server-micro/src/microApollo.ts#L46, this has to be used in graphqlHandler
. Something like that:
const query = req.method === 'POST' ? (req.filePayload ? {right: req.filePayload} : await parse_body_1.parseBody(req)) : parse_body_1.parseQuery(req);
I will send in a PR later that fixes the bug
nexus 0.26 native uploads work for me well Examples from bkrausz are very helpful https://github.com/graphql-nexus/nexus/issues/844#issuecomment-666699822 https://github.com/graphql-nexus/nexus/issues/844#issuecomment-667588707
I also had to fixate graphql-upload version that have upgraded fs-capacitor
"resolutions": {
"graphql-upload": "^11.0.0",
}
remove multipart parser if any used
app.server.express.use(multipart)
app.server.express.use(bodyParser.json())
to eliminate this error
nexus:server:graphql Missing multipart field ‘operations’
Thank you all! =)
i am currently trying to dig in to nexus 0.26.1 and want to use file uploads but I couldn´t stick the right parts together using the comments and code from here.
There is also a small sentence in docs mentioning the Upload scalar but I could not find out on how to use it. https://nexusjs.org/components-standalone/schema/api/api-scalarType#example-of-upload-scalar
@alexichepura: May I ask you to give an example of code (or the steps for implementation) you`ve used for the current version if possible please ?
@TommyCoding I didn't use docs, I used exact code from the bkrausz' comment https://github.com/graphql-nexus/nexus/issues/844#issuecomment-666699822
i have used it too but I don`t know how to use that scalar for a mutation?
schema.extendType({
type: 'Mutation',
definition(t) {
t.field('upload', {
type: 'UploadTask',
args: {
// <-- how to use upload as arg?
// or is there antoher way?
},
resolve(_root, args, ctx) {
// ...
}
})
})
used the code from https://github.com/graphql-nexus/nexus/issues/844#issuecomment-666699822 before using the code above.
First I would search for an "Upload" scalar in schema.
"""The `Upload` scalar type represents a file upload."""
scalar Upload
Then it's possible to use it as an argument.
t.field("upload", {
type: "String",
args: {
image: arg({ type: "Upload", required: true }),
},
resolve: async (_parent, args, _ctx) => await gcp_upload(args.image),
})
Note: I send image
variable using this spec https://github.com/jaydenseric/graphql-multipart-request-spec
@alexichepura I used this but i encountered a problem with the underlying dependency that nexus is using https://github.com/graphql-nexus/nexus/issues/1413 how did you overcome the bug?
Perceived Problem
Ideas / Proposed Solution(s)
some thoughts about packaging however...
References