Skip to content
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

File upload plugin #126

Closed
artecoop opened this issue Feb 19, 2020 · 32 comments
Closed

File upload plugin #126

artecoop opened this issue Feb 19, 2020 · 32 comments

Comments

@artecoop
Copy link

Hello again,
following #125 I'm trying to create a plugin to make possible to upload a file thru this amazing library, based on GraphQL multipart request spec.

I'm using fastify-plugin and fastify-multipart. I have created this stub:

'use strict';

const fp = require('fastify-plugin');

function GQLUpload(fastify, opts, next) {
    if (!fastify.hasRequestDecorator('multipart')) {
        fastify.register(require('fastify-multipart'), opts);
    }

    fastify.addHook('onResponse', (request, reply, done) => {
        if (request && request.body) {
            console.log(request.body);
        }

        done();
    });

    next();
}

module.exports = fp(GQLUpload, {
    fastify: '>= 2.x',
    name: 'fastify-gql-upload',
    dependencies: ['fastify-multipart']
});

With this code, I'm able to see the body in the console, like this:

{
  '1': [
    {
      data: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 02 00 00 00 03 00 08 06 00 00 00 52 0f df 4e 00 00 00 04 73 42 49 54 08 08 08 08 7c 08 64 88 00 ... 35718 more bytes>,
      filename: 'no_image.png',
      encoding: '7bit',
      mimetype: 'image/png',
      limit: false
    }
  ],
  operations: '{"operationName":null,"variables":{"id":"F8JgzO4Wvp5TeicbO1mC","file":null},"query":"mutation ($id: ID!, $file: Upload!) {\\n  addNewsImage(newsId: $id, file: $file)\\n}\\n"}',
  map: '{"1":["variables.file"]}'
}

Now, the endpoint return this error with HTTP 400
Must provide Source. Received: undefined
and the resolver is not executing.

My question is: how to pass the data from the response.body to the resolver's function?

I'm very sorry to post such open and broad question with little code, but I'm really not understanding how to complete the flow.

@mcollina
Copy link
Collaborator

You should be able to access that in your resolver as context.reply.request.body.

@artecoop
Copy link
Author

Ciao Matteo,
thanks for your reply.

The problem is the request never hit the resolver. the error
Must provide Source. Received: undefined
happens earlier in the fastify pipeline.

@artecoop
Copy link
Author

Well, I did it.
Thanks to the debugger and the ability to step upward during debug, I understood my mistakes.
Body was not compliant with fastifyGraphQl function in order to be parsed correctly.
I'll do some refinements now and then I'd love to share my work with you.
@mcollina Do you prefer that I send the code to you or I publish the plug in on my own?

@bmullan91
Copy link
Collaborator

@artecoop Nice one! Do you have a branch somewhere where I can look? I'm also digging into this issue :)

@artecoop
Copy link
Author

artecoop commented Feb 20, 2020

@bmullan91 working on it.
The code is now a bit of a mess, and for debugging purposes I've copied files from grapqhl-upload package what needs to be imported (as dependencies should work lmao)

@bmullan
Copy link

bmullan commented Feb 20, 2020 via email

@artecoop
Copy link
Author

Yeah sorry for that!

@bmullan91
Copy link
Collaborator

@artecoop After a lot of head scratching and try and error... I got it working with this before registering the fastify-gql plugin:

 fastify.addContentTypeParser("multipart", (req, done) => {
    req.isMultipart = true;
    done();
  });

  fastify.addHook("preValidation", async function(request, reply) {
    if (!request.req.isMultipart) {
      return;
    }

    // processRequest from `graphql-upload`
    request.body = await processRequest(request.req, reply.res, options);
  });

Here's a codesandbox with the code https://codesandbox.io/s/fastify-gql-upload-eg-7gsz9 - note It fails to write the file on sandbox, not sure if that's possible on codesandbox.

When I moved the code into a separate plugin it no longer worked - not sure why tbh 😕

@artecoop
Copy link
Author

@bmullan91 how could this work? graphql-upload's processRequest uses, for example, request.once and response.once that aren't available in fastify's request/response.

I'm prepping a repo with my code to share with all of you.

@bmullan91
Copy link
Collaborator

@artecoop Im passing the raw req, and resp via:

await processRequest(request.req, reply.res, options);

@bmullan91
Copy link
Collaborator

@artecoop I've pushed up that sandbox into an actual repo: https://github.com/bmullan91/fastify-gql-upload-eg

@artecoop
Copy link
Author

@bmullan91 this is beatiful. Please publish it as npm package. It's way better than my implementation that rewrite the body.

@bmullan91
Copy link
Collaborator

@artecoop Will do, once I figure out why fastify doesn't like it when I put it inside its own plugin 😅

@artecoop
Copy link
Author

@bmullan91 check this out:
https://github.com/kennyrulez/fastify-gql-plugin
this is my implementation, maybe will help you

@bmullan91
Copy link
Collaborator

@artecoop https://www.npmjs.com/package/fastify-gql-upload - still need to write tests etc, but this should unblock you 😄

@artecoop
Copy link
Author

Thank you, it works like a charm

@ericlewis ericlewis mentioned this issue Feb 21, 2020
@PabloSzx
Copy link
Member

Made a fork of fastify-gql-upload with updated syntax for Fastify v3, updated dependencies + TypeScript support.

https://www.npmjs.com/package/fastify-gql-upload-ts

https://github.com/PabloSzx/fastify-gql-upload-ts

@artecoop
Copy link
Author

Brilliant work @PabloSzx !!!

@asciidisco
Copy link
Collaborator

Could this be something that should be documented in the README, with a reference to @PabloSzx npm package?
Once documented & accessible to everyone, we should be able to close this, I believe.

@mcollina
Copy link
Collaborator

Should we support this natively?

@PabloSzx
Copy link
Member

Apollo Server has out of the box support for File Uploads using graphql-upload, I think it would be nice to support it too, and it's not much code needed anyways, but there are some gotchas, like node version compatibility.

@artecoop
Copy link
Author

Should we support this natively?

IMHO, no. Keep going with the single responsibility principle, and let other packages, yours or made by other developers, plugs extra features.
Dont waste the "pluggable" effort you did with fastify

@asciidisco
Copy link
Collaborator

Should we support this natively?

Might be a good idea to do a quick Pro/Con list (please complete it, if I miss something important)

Pro

Neutral

Con

  • Additional 3rd party dependency graphql-upload, which itself has 5 more first level dependencies adding quite a bit of weight
  • As per @artecoop definition, it violates the single responsibility principle (I'm personally undecided on this fact)
  • Has been/Can be solved by a 3rd party module already

My subjective opinion, leave it out of core, but a note should be added to the README with a link to the npm module by @PabloSzx & a quick integration/usage example.

@smolinari
Copy link
Contributor

My vote goes to not adding it in the core and leaving it as a plugin (and yes, I'm new to fastify-gql and my 2 cents probably isn't worth much). 😊

Thing is, how one handles uploading files with a GraphQL server can differ depending on needs and nailing down one way into the core is sub-optimal for everyone else not needing that method. The upload feature is then just bloat. Using plugins means a developer makes the conscious decision to add that feature, and at that point it isn't bloat.

Scott

@mcollina
Copy link
Collaborator

closing, I think we have some sort of conclusion :).

@PabloSzx
Copy link
Member

PabloSzx commented Oct 7, 2020

btw, the package was renamed to mercurius-upload following the rebranding

https://github.com/PabloSzx/mercurius-upload

https://www.npmjs.com/package/mercurius-upload

@mcollina
Copy link
Collaborator

mcollina commented Oct 7, 2020

@PabloSzx would you like to move it inside the org to give it an official blessing?

@PabloSzx
Copy link
Member

PabloSzx commented Oct 7, 2020

@PabloSzx would you like to move it inside the org to give it an official blessing?

Sure 😁 I added you (matteo.collina) as a mantainer in the npm package, but when I try to "Transfer ownership" of the GitHub repository, this error comes up:
image

I just added you as a collaborator of I transferred the repo to you and maybe then you can transfer it to the org 👌 .

@mcollina
Copy link
Collaborator

mcollina commented Oct 8, 2020

Could you add me as an owner on npm as well? I am https://www.npmjs.com/~matteo.collina

@PabloSzx
Copy link
Member

PabloSzx commented Oct 8, 2020

Could you add me as an owner on npm as well? I am https://www.npmjs.com/~matteo.collina

Done 👌

@mcollina
Copy link
Collaborator

mcollina commented Oct 8, 2020

Great!!!!

@mcollina
Copy link
Collaborator

mcollina commented Oct 8, 2020

Maybe we can link the plugin from this project README as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants