-
Notifications
You must be signed in to change notification settings - Fork 257
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 object is empty in federated gateway but not if called directly on the service #427
Comments
So when the file is uploaded it hits the gateway first. File uploads are written to temporary files and the data can be streamed into your application. You need to either resolve the upload in your gateway and pass to your downstream service as a buffer or find a different mechanism of forwarding the file data to your service. If you inspect the request variables at the gateway level you should see the same promise object mentioned above. Hope this helps |
@lbaker2 are you offering a solution in the context of the gateway, explaining how it currently works, or something else? |
@henry74 a little bit of hinting at a solution and explaining how it currently works. |
I believe this misses the spirit/purpose of the federated gateway if we have to add our own user code:
|
@henry74 I completely agree about not adding application logic or user code into the gateway. There is an option for RemoteGraphQLDataSource called willSendRequest where any unresolved promises can be resolved prior to sending the request to a downstream service. One could always extend RemoteGraphQLDataSource to achieve this behavior by default. |
Has anyone gotten file upload through the gateway working? I'm not familiar enough with RemoteGraphQLDataSource and an example would be great. |
Yes. It’s very easy if both your gateway and service that needs the upload are on the same server. I am in the process of writing about it. |
Share what you're thinking, but we live in the world of docker containers and kubernetes so requiring the upload service to live in the same "server" won't work for our use case. |
Same... The upload/images services are not even owned by the same teams... Hence the point of federation... |
@henry74 @jacob-ebey both great points. I meant to say it is easy to illustrate a solution when both the gateway and service live on the same "server". @henry74 we aren't using containers, so I hadn't thought about that. However, if there is a shared volume I think you can achieve the same behavior... That being said, every solution I have found so far has required some tweaking of the downstream services that consume the files. I'm going to throw some code in here, so please be kind. I'm just trying to help. Starting from a very simple gateway configuration -
You have a few options at this point: forward the file as buffer, send the temp file location, or stream the file to shared volume and send temp file location. All the examples exclude error handling and assume the upload to be at Sending as a buffer
Sharing the file via /tmp
Sharing the file via shared volume
Obviously, it's not ideal to change resolvers in your services... hopefully, this helps someone. One last thing... you can extend RemoteGraphQLDataSource to iterate over your variables and use Promise.all to resolve them. |
There is one other strategy I'd like to try. It would require the |
@lbaker2, thanks for the response. I'll spend some time this weekend looking into the solutions. |
Our team is also interested in supporting GraphQL file uploads with Apollo federation. We're using Hapi which has it's own quirks, unfortunately (apollographql/apollo-server#1680, hapijs/hapi#3465). I was finally able to make file uploads and federation work by implementing a custom One downside of multipart file uploads in Node.js seems to be that files may be fully loaded into memory or temporarily stored on disk (see Eran Hammer's comment here: hapijs/hapi#3465 (comment)). With multiple GraphQL gateways (like with Apollo federation) files need to be handled in this way on multiple servers. 😕 |
@mfellner Very nice!. I was about to implement the solution this morning, but now I can just enjoy my coffee :) |
I have an ApolloGateway server as well as a federated service - on separate boxes. On the service I'm able to upload directly following this spec: https://github.com/jaydenseric/graphql-multipart-request-spec. I want to be able to make some changes on the gateway, with minimal changes to the spec, to allow uploads from the gateway. How do I go about doing this? |
@ziplizard does the solution posted by mfellner meet your criteria? |
@lbaker2 I have implemented precisely how @mfellner has outlined, and it does exactly as he has specified. However, I've found his example is not complete. I'm able to return the uploaded filename, mimetype, and encoding. However, when I try to stream the data to S3, I get the following error: |
It appears the header |
Has anyone actually gotten an upload into S3 (for example) using https://github.com/mfellner/apollo-server-upload-example/blob/master/frontend-server/file-upload-data-source.js ? This example's outcome is just to return the properties of the file. I'm unable to get the actual streamed upload in order to save it. |
@mfellner were you able to save the stream from the federated service? I have tried very simply this:
And get the following error: I'm very interested in how you are able to solve this. Thanks. |
I feel like the gateway should just forward the file on by default to the federated service unless some other method of handling it is provided. I also feel this "method of handling it" should be a set of interfaces, one that exists on the gateway and one on the federated service. This interface could be implemented in packages such as "@apollo/federated-files-fs" and "@apollo/federated-files-s3", etc... Apollo could provide the "fs" and default forwarding implementations, and I'm sure we will build out the rest as needed. With files being scalar types, and having these proposed packages provide a "middleware" for both the gateway and federated service, each of these packages could utilize whatever data-format is appropriate for the implementation while still allowing files to be sent directly to the federated service. For example, the info sent from the gateway to a federated service for the "s3" package may just contain the bucket and key name of the file, allowing for tiny transfers between the gateway and the federated services. The "tricky" part here will be determining what variables these "middlewares" provided by the packages would be responsible for. If the gateway has access to the schema types of the variables in the query, this should not be an issue. I have yet to dig into the gateway source though, so I'm not familiar with it. Thoughts on this? |
To consider: The federated service should allow uploads directly without the gateway. There should be no dependencies to the gateway. This makes local development much much easier. |
@ziplizard did you have any success with this? I am facing the same issue and would be interested in knowing |
https://gist.github.com/bl42/881d85ee0dd92bbe68e93dcd0c99cfdb So I got the
EDIT: Fixed the response issue and I got my S3 use case working. |
@bl42 I have not gotten to work yet, but will checkout your gist and work on it. Thanks. |
It works! Thanks so much @bl42! |
graphql-tools-fork v8.1.0 now provides support for proxying remote file uploads for those still using schema stitching who want to proxy all the things. Instead of using Buffers to read the stream into memory, we extend the FormData class to allow streams of unknowable length, and intercept all form.getLength() and getLengthSync() calls to return undefined, fixing use with node-fetch without any patches. This may be helpful to Federation users as a tweak to the above gist. See: https://github.com/yaacovCR/graphql-tools-fork/blob/master/src/links/createServerHttpLink.ts#L15-L63 Feedback/suggestions welcome. |
@bl42 Thanks for the gist. I think its working for me. At least the files are being uploaded correctly now. However, I'm running into this an error on this line:
Here is the error:
The file still gets uploaded, but the response comes back with an error. Any thoughts on this? |
Fyi, graphql-tools-fork has been merged into graphql-tools v5 and may help you stitch all the things. Fixed FormData is also available there. |
I got it working by making this change: from:
to:
Now the gateway is uploading correctly and returning the right response. Here is my working gist for anyone else who runs into this: Thanks! |
This seems to be broken now with Node v14 via apollographql/apollo-server#3508 |
apollographql/apollo-server#3508 (comment) this link describes on how to update the graphql-upload dep that is causing the issue (the one used in apollo-server is 10! major versions behind. The gists here would need some tweaking to work with this change though... |
Yes, we could not upgrade the built in version of graphql-upload further due to backwards compatibility concerns. (Because of this, we are removing the direct integration from Apollo Server 3 and allowing you to integrate the version of your choice yourself instead.) |
After federating a service that implements the upload scalar, for some reason, if I hit that resolver from the federated gateway, the file object is empty.
But then if I hit the service directly, everything is fine.
Is there something I am doing wrong with the apollo-gateway?
For reference,
For now, I've worked around it by registering another client that directly hits the service instead of the gateway just to do file uploads, but I feel like this defeats the point of federating in the first place.
Any suggestions/guidance would be greatly appreciated!
The text was updated successfully, but these errors were encountered: