apollo-server: Calling createReadStream() for uploaded file crashes in NodeJS 13

Running in NodeJS 13.1.0:

const { createReadStream, filename } = await file // uploaded file
const stream = createReadStream()

Result:

RangeError: Maximum call stack size exceeded
        at _openReadFs (internal/fs/streams.js:1:1)        
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)
        at _openReadFs (internal/fs/streams.js:123:12)     
        at ReadStream.<anonymous> (internal/fs/streams.js:116:3)
        at ReadStream.deprecated [as open] (internal/util.js:70:15)
        at ReadStream.open (D:\db-server\node_modules\fs-capacitor\lib\index.js:90:11)

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 70
  • Comments: 53 (9 by maintainers)

Commits related to this issue

Most upvoted comments

I know a lot of people are looking to be un-blocked here, so I want to offer this update and some instructions I put together as a suggestion, in hopes that it can be validated and that it works for those that need answers today, but also as a proof of concept for how we hope it will work in the future.

At a high level, while we believe that graphql-upload has provided tremendous value, we believe that the package itself stands really well on its own (see it’s own repository for the details, including the specification that supports it). It has been architected in a way where it can be added to a GraphQL server and doesn’t need to be baked into Apollo Server to function. Continuing along those lines, we think it will be able to serve the community if it is not closely coupled / bundled into Apollo Server as a direct dependency (and enabled by default).

In Apollo Server 3.0, we will not ship graphql-upload (or upload support) by default. While there are a number of ways to do uploads, the graphql-upload package is not the only way to do it. Some other suggestions have been outlined on our blog.. As for Apollo Server 2.x: due to (necessary) breaking changes in newer versions (see https://github.com/apollographql/apollo-server/pull/4039), the version of graphql-upload that exists today will not be updated further. (Semantically version speaking: the update would require a bump to v3, but we already plan on de-coupling it in v3.)

As for today / now: Anyone who wants to use a newer version of graphql-upload today, please try using the package’s own installation instructions, rather than using the baked-in Apollo Server version of graphql-upload.

I put these instructions together quickly and I believe they will work, but please help me correct any inaccuracies! 😄

  1. Switch from using apollo-server to one of the Apollo Server integration packages, like apollo-server-express.

    ❗ This step can be skipped if you’re already using apollo-server-koa, apollo-server-hapi, apollo-server-express, etc.!

    This is necessary because apollo-server (which uses apollo-server-express internally) doesn’t expose it’s HTTP server directly. If switching to the Express integration, this should be as simple as creating your own Express application:

    const express = require('express')
    const app = express();
    

    And then replacing the existing (roughly) server.listen(ListenOpts).then(OnListeningCallback) call with:

    server.applyMiddleware({ app });
    app.listen(ListenOpts, OnListeningCallback);
    

    …taking care to maintain the ListenOpts and OnListeningCallback as they were before, but in the new locations (note: not a Promise chain!)

    Note: some options which were normally provided on the ApolloServer constructor, like cors and onHealthCheck, will move to applyMiddleware’s options.

  2. Disable the upload support provided by the older graphql-upload version included in Apollo Server 2.x by setting uploads: false on the ApolloServer constructor’s “options”

    const server = new ApolloServer({
      typeDefs,
      resolvers,
      uploads: false, // Here!
    });
    
  3. Install the latest version of graphql-upload

    npm install graphql-upload@latest
    
  4. Import the necessary primitives from graphql-upload into the module where applyMiddleware is called

    const {
      GraphQLUpload, // The GraphQL "Upload" Scalar
      graphqlUploadExpress, // The Express middleware.
    } = require('graphql-upload');
    

    There is also a Koa middleware available. The processRequest function can be used for other implementations. The documentation is quite clear, but there are examples in our Apollo Server integration packages source code for how they’ve been implemented in Apollo Server 2.x.

  5. Add the Upload scalar to the schema by adding to the typeDefs SDL appropriately.

    scalar Upload
    
  6. Add a resolver for the Upload scalar

    const resolvers = {
      // Add this line to use the `GraphQLUpload` from `graphql-upload`.
      Upload: GraphQLUpload,
    
      /*
     ...
     Other resolvers remain the same.
     ...
      */
    
    },
    
  7. Add the graphql-upload middleware

    Add the graphqlUploadExpress middleware before calling into the ApolloServer instance’s applyMiddleware method with the app:

    const app = express(); // Existing.
    app.use(graphqlUploadExpress()); // New!
    server.applyMiddleware({ app }); // Existing.
    

    As noted above, there is also a Koa middleware and processRequest

And I think that should do it. I really hope this helps! While this does add a few lines of boiler-plate when you need uploads, it’s only a few lines.

If someone could try it out, it’d be greatly appreciated if it could be validated! Thanks!

This seems like a pretty serious bug. I’m surprised that it is still open…

It’s been almost a year since this issue has been created. Very disappointed that this is still not resolved. Considering the lack of maintenance I’m not quite sure if apollo-server is still the way to go.

A fix in my case was to force a version pf graphql-upload:

in your package.json add this:

  "resolutions": {
    "graphql-upload": "^11.0.0"
  },

If you are using yarn, that should work. npm users should add this to their “scripts” in package.json:

"preinstall": "npx npm-force-resolutions"

Then run npm install. You do need a lockfile for it to work.

graphql-upload 9.0.0 is out with node 13 support

This seems to be a problem with an older version of fs-capacitor

According to https://github.com/jaydenseric/graphql-upload/issues/170 the fix is checked in to master already and awaiting publish soon

until then, merging this into package.json will work. The preinstall script is only needed if using npm, as yarn supports dependencies natively

  "resolutions": {
    "fs-capacitor": "5.0.0"
  },
  "scripts": {
    "preinstall": "npx npm-force-resolutions"
  }

A fix in my case was to force a version pf graphql-upload:

in your package.json add this:

  "resolutions": {
    "graphql-upload": "^11.0.0"
  },

If you are using yarn, that should work. npm users should add this to their “scripts” in package.json:

"preinstall": "npx npm-force-resolutions"

Then run npm install. You do need a lockfile for it to work.

Thanks 👍 Shocking that they still haven’t fixed this officially

Isn’t it about time to fix this? Node v14 is out with release status LTS, and here we are still finding support for v13 😕

I downgraded to Node 12 and worked.

I know a lot of people are looking to be un-blocked here, so I want to offer this update and some instructions I put together as a suggestion, in hopes that it can be validated and that it works for those that need answers today, but also as a proof of concept for how we hope it will work in the future.

At a high level, while we believe that graphql-upload has provided tremendous value, we believe that the package itself stands really well on its own (see it’s own repository for the details, including the specification that supports it). It has been architected in a way where it can be added to a GraphQL server and doesn’t need to be baked into Apollo Server to function. Continuing along those lines, we think it will be able to serve the community if it is not closely coupled / bundled into Apollo Server as a direct dependency (and enabled by default).

In Apollo Server 3.0, we will not ship graphql-upload (or upload support) by default. While there are a number of ways to do uploads, the graphql-upload package is not the only way to do it. Some other suggestions have been outlined on our blog.. As for Apollo Server 2.x: due to (necessary) breaking changes in newer versions (see #4039), the version of graphql-upload that exists today will not be updated further. (Semantically version speaking: the update would require a bump to v3, but we already plan on de-coupling it in v3.)

As for today / now: Anyone who wants to use a newer version of graphql-upload today, please try using the package’s own installation instructions, rather than using the baked-in Apollo Server version of graphql-upload.

I put these instructions together quickly and I believe they will work, but please help me correct any inaccuracies! 😄

1. **Switch from using `apollo-server` to one of the Apollo Server integration packages, like `apollo-server-express`.**
   > ❗ This step can be skipped if you're already using `apollo-server-koa`, `apollo-server-hapi`, `apollo-server-express`, etc.!
   
   
   This is necessary because `apollo-server` (which uses `apollo-server-express` internally) doesn't expose it's HTTP server directly.  If switching to the Express integration, this should be as simple as creating your own Express application:
   ```js
   const express = require('express')
   const app = express();
   ```
   
   
   And then replacing the existing (roughly) `server.listen(ListenOpts).then(OnListeningCallback)` call with:
   ```js
   server.applyMiddleware({ app });
   app.listen(ListenOpts, OnListeningCallback);
   ```
   
   
   ...taking care to maintain the `ListenOpts` and `OnListeningCallback` as they were before, but in the new locations (note: _not_ a `Promise` chain!)
   > Note: some options which were normally provided on the `ApolloServer` constructor, like `cors` and `onHealthCheck`, will move to `applyMiddleware`'s options.

2. **Disable the upload support provided by the older `graphql-upload` version included in Apollo Server 2.x by setting `uploads: false` on the `ApolloServer` constructor's "options"**
   ```js
   const server = new ApolloServer({
     typeDefs,
     resolvers,
     uploads: false, // Here!
   });
   ```

3. **Install the latest version of `graphql-upload`**
   ```
   npm install graphql-upload@latest
   ```

4. **Import the necessary primitives from `graphql-upload` into the module where `applyMiddleware` is called**
   ```js
   const {
     GraphQLUpload, // The GraphQL "Upload" Scalar
     graphqlUploadExpress, // The Express middleware.
   } = require('graphql-upload');
   ```
   
   
   > There is also [a Koa middleware](https://github.com/jaydenseric/graphql-upload#function-graphqluploadkoa) available. The [`processRequest` function](https://github.com/jaydenseric/graphql-upload#function-processrequest) can be used for other implementations.  The documentation is quite clear, but there are examples in our Apollo Server integration packages source code for how they've been implemented in Apollo Server 2.x.

5. **Add the `Upload` scalar to the schema by adding to the `typeDefs` SDL appropriately.**
   ```graphql
   scalar Upload
   ```

6. **Add a resolver for the `Upload` scalar**
   ```js
   const resolvers = {
     // Add this line to use the `GraphQLUpload` from `graphql-upload`.
     Upload: GraphQLUpload,
   
     /*
    ...
    Other resolvers remain the same.
    ...
     */
   
   },
   ```

7. **Add the `graphql-upload` middleware**
   Add the `graphqlUploadExpress` middleware _before_ calling into the `ApolloServer` instance's `applyMiddleware` method with the `app`:
   ```js
   const app = express(); // Existing.
   app.use(graphqlUploadExpress()); // New!
   server.applyMiddleware({ app }); // Existing.
   ```
   
   
   > As noted above, there is also a [Koa middleware](https://github.com/jaydenseric/graphql-upload#function-graphqluploadkoa) and [`processRequest`](https://github.com/jaydenseric/graphql-upload#function-processrequest)

And I think that should do it. I really hope this helps! While this does add a few lines of boiler-plate when you need uploads, it’s only a few lines.

If someone could try it out, it’d be greatly appreciated if it could be validated! Thanks!

This WORK perfectly!!! Don’t scroll more here the answer!!

Still crashing on v14

None of the solutions worked for me. The resolutions fix and the graphql manual update didn’t work as I’m using apollo-server-lambda. Downgrading my node version to 12 just so I can get apollo working does not seem like a reasonable thing to do. I guess I’m on an island here.

This bug is really a blocker for this module and should be considered top priority for maintainers.

A workaround is to force resolution for module graphql-upload to a more recent version :

in package.json

 "resolutions": {
    "graphql-upload": "11.0.0"
  },

be aware that resolutions property is currently only handled by yarn package manager, not by npm

with npm, you have to preinstall an aditionnal module to force resolutions :

"scripts": {
    "preinstall": "npx npm-force-resolutions",

of cours, check that your CI support preinstall scripts (our don’t)

@abernix I don’t know if this is feasible, but did you consider releasing the breaking change fix on v3, while the upcoming planned release be v4?

facing same issue with node 14x

I have the same problem with node 14x

@laooola I’m with you here. With 2.x being the only “stable” version available, it’s weird not to see core deps be updated anymore, especially when version 3 literally could be a year away.

Having the same issue. However check this workaround instead of downgrading: MichalLytek/type-graphql#37 (comment) more specifically for people using type-graphql.

Thanks!

Half a year passed since I opened this issue and no response from maintainers. I consider switching to https://github.com/mcollina/fastify-gql which is significantly faster , supports subscriptions and have built-in support for loaders

Same here, downgrading to 12.

I am having the sam issue, but only when app is in production. @lynxtaa did you find any workaround?

Thanks a lot @glasser for this detailled explanation. We plan to implement direct use of graphql-upload as you explained, thx.

However I see that all the issues related the the incompatibility with node 13 are closed with a link to the version 2.21 of appolo-server-express.

I fail to see how version 2.21 is related to this very problem : version 2.21 allows as of now to use graphql@15, but is still incompatible with node 14 (as you have confirmed above).

It was not clear for me what the current situation was (because I don’t know what you have modified in your fork of graphql-upload version 8) and I suspect many users of this module are confused as well. I think the most important part here is “This is essentially closed as WONTFIX” and should be mentionned in the readme, because newcomers with this library or users migrating from node <13 are likely to run into the same issue as well.

And thx again for all your work on this library 👍

I’m gonna roll this one into #4865 too. I hope to make some rapid progress on this soon.

This seems to work in Node.js v15.0.1. However, the following warning pops up:

[DEP0135] DeprecationWarning: ReadStream.prototype.open() is deprecated
(Use `node --trace-deprecation ...` to show where the warning was created)

Adding this here in case it helps someone else.

After trying everything listed above I couldn’t get it to work (even on Node 12??). Anyways I discovered I had graphql-upload in my node_modules, but apollo-server-core had its own and was using that one. Updating THAT one (just by FTP) has fixed it.

Far from a perfect solution, but ¯_(ツ)_/¯

@abenhamdine This is essentially closed as WONTFIX.

Upgrading the version of graphql-upload that Apollo Server 2 relies on in order to support Node 14 would require taking other backwards-incompatible changes from graphql-upload, which we aren’t comfortable doing within the single major version of AS2.

On the other hand, the plan for AS3 is not to upgrade graphql-upload but to disentangle it from AS entirely and encourage folks to use graphql-upload directly; the role of AS3 here will be to provide whatever hooks are necessary for users to add arbitrary versions of graphql-upload to their app rather than to bundle a specific version.

I encourage you to use graphql-upload directly in your app (pass uploads: false to new ApolloServer and just use graphql-upload as described in its docs).

My guess is that this will work fine with, say, apollo-server-express, but that this will be challenging for apollo-server-lambda because there’s no “middleware” there to let you inject the call to graphql-upload. So somebody interested in getting that working should come up with a good hook or two to add to apollo-server-lambda in order to integrate graphql-upload.

Note that I just landed a PR to rewrite apollo-server-lambda to be an async function (and to inline the old graphqlLambda function) which I think will make it a lot easier to add integration points, as the old code structure was quite hard to follow.

This solved my problem

"resolutions": {
    "**/graphql-upload": "^11.0.0",
    "**/**/fs-capacitor": "^6.2.0"
  },

For npm do not forget to set preinstall as the conversation above

I’m moving to express-graphql since this is a blocker for me and it appears to be unresolved.

Annotation 2020-05-14 171750 Working fine with this Node version.

A fix in my case was to force a version pf graphql-upload: in your package.json add this:

  "resolutions": {
    "graphql-upload": "^11.0.0"
  },

If you are using yarn, that should work. npm users should add this to their “scripts” in package.json: "preinstall": "npx npm-force-resolutions" Then run npm install. You do need a lockfile for it to work.

Thanks 👍 Shocking that they still haven’t fixed this officially

Actually, this doesn’t fix the error in my case…

Any case like me? Any other solutions rather than downgrading to Node v12 ?

My Node version is v.14.9.0

Yes, as mentioned, I don’t think there’s currently a great solution for integrating a non-built-in use of graphql-upload with apollo-server-lambda.

AS3 is going to remove the built-in graphql-upload. I think it would be great if, before AS3, we had a way to use graphql-upload with apollo-server-lambda that isn’t the built-in integration! That probably involves looking at how apollo-server-lambda works, seeing where the graphql-upload integration gets slotted in, and adding a more generic integration point that doesn’t have to be graphql-upload-specific.

I’m the new lead maintainer of AS but I have to admit — I don’t use lambda myself, and we don’t use it much internally at Apollo. My philosophy is that while apollo-server-lambda is quite important (it’s the second most downloaded AS flavor, though the gap between it and Express is huge), it’s best for improvements to apollo-server-lambda to be driven by folks who actually use it themselves in practice rather than by folks like me who are mostly learning Lambda on the fly.

So what I’d love to see happen is a PR from somebody in the community that allows you to slot in the newest release of graphql-upload directly into apollo-server-lambda without tying the implementations directly to each other. Then we can get that into a v2 minor release, and so when v3 comes out without the built-in graphql-upload integration it doesn’t actually remove any functionality.

Taking a quick glance at apollo-server-lambda/ApolloServer.ts, it looks like it defines a fileUploadHandler function takes in a next function and closes over (and mutates!) event. So it seems like we could add a new option to new ApolloServer that’s like lambdaPreGraphQLMiddleware: (event: APIGatewayProxyEvent, next: Function) => something which if provided gets called before fileUploadHandler. And then there would hopefully be a simple way to plug graphql-upload into that lambdaMiddleware function (while also passing uploads: false).

The function also closes over response but looks like that’s just an elaborate way of being able to trigger graphql-upload’s cleanup. So probably there should be a second lambdaPostGraphQLMiddleware hook where you can invoke response.end() rather than that being part of the first middleware.

I’d love to review a PR that’s something like this! Of course I’m not a Lambda expert so there might be some other much more natural way of implementing this. But basically: let’s come up with some non-upload-specific hooks that let you use upstream graphql-upload with apollo-server-lambda, and let’s get that into AS2!

We are never going to upgrade graphql-upload in Apollo Server v2, and we are going to remove the built-in integration from Apollo Server v3. That’s not to say that we don’t like graphql-upload: we just think folks should be able to get every new improvement @jaydenseric puts out without needing to tightly couple it to the Apollo Server release cycle. Folks who want to use the newer version of graphql-upload now should follow @abernix 's suggestion in https://github.com/apollographql/apollo-server/issues/3508#issuecomment-662371289 to disable the built-in integration and install the graphql-upload version of your choice.

I recognize that there may be some integrations (eg Lambda) where there’s no easy place to plug in the graphql-upload processRequest function. If that’s the case, we can fix that by adding an appropriate hook to the integration that would allow you to insert processRequest (or any handler of your choice!) at the right place, without tightly coupling the implementation to graphql-upload.

I have the same issue, I tried on Node.js v15.0.1 with the latest graphql-upload and fs-capacitor and the file seems to arrive but when I pipe it to a WriteStream and try to write it to the disk It is empty.

The only workaround that I found after hours of trying is to use Node v12.19.0 😦.

Having the same issue. However check this workaround instead of downgrading: https://github.com/MichalLytek/type-graphql/issues/37#issuecomment-592467594 more specifically for people using type-graphql.

@safead For me it showed up in production build inside docker containers, downgrading node to the last LTS helped FROM node:12.16.1