createReadStream() throwing RangeError: Maximum call stack size exceeded when uploading file
Asked Answered
W

9

21

I am trying to use Apollo Server's Upload scalar to send files to S3 directly. My schema:

const { gql } = require('apollo-server-express')

module.exports = gql`

extend type Mutation {
  createPicture(
    name: String!
    picture: Upload!
  ): Picture!
}

type Picture {
  name: String!
  picture: String!
}
`

Resolver:

const { combineResolvers } = require('graphql-resolvers')
const isAuthenticated = require('./auth')
const { uploadPhoto } = require('../services/picture')

module.exports = {
  Mutation: {
    createPicture: combineResolvers(
      isAuthenticated,
      async (
        parent,
        { name, picture = null },
        { models, me }
      ) => {
        const { createReadStream, filename, mimetype, encoding } = await picture
        // Does not get past this line
        const stream = createReadStream()

        uploadPhoto(stream, filename)

        const pictureModel = models.Picture.create({
           name,
           picture
        })
        return pictureModel
      }
    )
  }
}

But my code errors like this:

internal/util.js:55
  function deprecated(...args) {
                     ^

RangeError: Maximum call stack size exceeded
    at ReadStream.deprecated [as open] (internal/util.js:55:22)
    at ReadStream.open ([truncated]/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 ([truncated]/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 ([truncated]/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 ([truncated]/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 ([truncated]/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 ([truncated]/node_modules/fs-capacitor/lib/index.js:90:11)

Note: I am sure the image was sent correctly, as filename is correct

Watt answered 7/1, 2020 at 0:49 Comment(2)
Where do createReadStream() come from? What is it?Reliance
@Reliance From here: github.com/jaydenseric/graphql-upload#type-fileuploadWatt
W
13

Turns out it was this bug in graphql-upload. Downgraded to node 12 and it's fixed (solution listed there did not help me)

Watt answered 7/1, 2020 at 1:55 Comment(2)
don't need this anymore, use this answer instead: https://mcmap.net/q/599767/-createreadstream-throwing-rangeerror-maximum-call-stack-size-exceeded-when-uploading-file, worked for meGermin
I tried everything but notihng works :-( Can anyone help me pls?Disarrange
M
13

Add this to package.json:

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

source: https://github.com/jaydenseric/graphql-upload/issues/170#issuecomment-641938198

Manville answered 13/6, 2020 at 1:16 Comment(1)
This is a palliative solution. What is the package that loads the wrong versions in the first place? Edit: right ticket on Apollo Server Github: github.com/apollographql/apollo-server/issues/4190Appressed
F
9

This error occured to me in node version 14 too! I solved it as follows:

  1. Install latest version of graphql-upload !

  2. use graphqlUploadExpress middleware to define the maximum file limit.

    import { graphqlUploadExpress } from "graphql-upload";
    const app = express()
    app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
    
  3. Set uploads to false while initializing the ApolloServer

      const server = new ApolloServer({
        uploads: false,
        schema,
      });
    
Franklyn answered 11/6, 2020 at 4:44 Comment(7)
could you explain why we need to set the uploads to false? why does the OP need to install a separate library for this to work now?Pulpy
@JohhanSantana Apollo server bundles inside a clone of graphql-upload that is broken on Node 14. To use the newer fixed graphql-upload package you need to tell apollo not to use it's bundled one. and then use it yourself by registering the body handler with express and adding the upload schema stuff.Allative
This answer is really the correct one, just remember to call app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 })); before applying Apollo sever middleware.Diameter
Hi dude.I did this but now I'm getting another error: "message": "args.file.then is not a function", Here is my code: const singleUpload = (parent, args) => { return args.file.then((file) => { const { createReadStream, filename } = file const fileStream = createReadStream() fileStream.pipe(fs.createWriteStream(../../assets/img/grid/${filename})) return file }) }Disarrange
@JulianoCosta you probably forgot to add this to the resolvers you pass into your schema: resolvers: { Upload: GraphQLUpload, } As per the documentation: Upload: github.com/jaydenseric/graphql-upload#class-graphqlupload In version 8, it worked without this resolver.Klemm
@Narretz, yes, it was the problem. I did it yet and it solved my problem. Tnx 4 ur helpDisarrange
This worked for me. Its also the right approach and better than the approach of fixing fs-capacitor version. As it removes the dependency on Apollo on the version of apollo-upload to use. Nicely done!!Ellenaellender
D
3

Since it is a bug with fs-capacitor, this would help as we can see in this issue

Worked for me in node 14.16.1 with the following configuration:

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

I needed to add force-resolutions inside "scripts"

"scripts": {
    ....
    "preinstall": "npx npm-force-resolutions"
}

Remove ./node_modules and install dependencies again with npm install.

Deary answered 13/4, 2021 at 22:8 Comment(2)
Thanks, this saves me a lotGermin
Ok but which package is loading them in the first place?Appressed
D
1

Add this to your package.json

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

then add this inside scripts

"scripts": {
    ....
    "preinstall": "npx npm-force-resolutions", //This one
    ....
}

then you need to do npm install. Done

Source: graphql-upload

Daudet answered 15/2, 2021 at 1:0 Comment(1)
Thanks, this saves me a lotGermin
P
1

I've read the graphql-upload documentation but couldn't find one solid example that shows how to use graph-upload with apollo-server on NodeJS 14. Based on a sample application (that worked on the old graphql-upload version of apollo-server, failed on NodeJS 14) and answers from stack overflow I've managed to create a fully function example.

The original example: https://github.com/DNature/apollo-upload

I used apollo-server-express, instead of apollo-server.

With NodeJS 14, [email protected] and [email protected]:

index.js

import { ApolloServer } from "apollo-server-express";
import express from "express";
import { graphqlUploadExpress } from "graphql-upload";
import typeDefs from "./typeDefs";
import resolvers from "./resolvers";

// Import your database configuration
import connect from "./db";

export default (async function () {
  try {
    await connect.then(() => {
      console.log("Connected 🚀 To MongoDB Successfully");
    });

    const server = new ApolloServer({
      uploads: false, // Disables the bundled ApolloServer old graphql-upload that doesn't work on NodeJS 14
      typeDefs,
      resolvers,
    });
    await server.start();

    const app = express();
    app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));
    server.applyMiddleware({ app });

    await new Promise((resolve) => app.listen({ port: 7000 }, resolve));
    console.log(
      `🚀 Server ready at http://localhost:7000${server.graphqlPath}`
    );
  } catch (err) {
    console.error(err);
  }
})();

In typeDefs.gql the scalar Upload needs to be defined:

import { gql } from "apollo-server-express";

export default gql`
  scalar Upload

  type File {
    id: ID!
  ...

In resolvers.js the resolver for Upload needs to be defined:

...

import { GraphQLUpload } from 'graphql-upload';

...

export default {
  Upload: GraphQLUpload,

  Query: {
    hello: () => "Hello world"
  },
  Mutation: {
...

Finally uploading from postman:

Note: the key 0 is of type File, not Text! Upload a file to GraphQL server from postman

Pillory answered 9/6, 2021 at 20:20 Comment(0)
G
1

TLDR: Default Apollo server file upload doesn't work in node >=13. Fix it by overwriting Apollo's server file upload with the graphql-upload server file upload

npm install graphql-upload

Overwrite default Apollo Upload type in TypeDefs

const typeDefs = gql`
  scalar Upload
  ...
 `

Overwrite default Apollo Upload type in index.js

const { GraphQLUpload } = require('graphql-upload')
const { graphqlUploadExpress } = require('graphql-upload')
const express = require('express')
 
const app = express()

app.use(graphqlUploadExpress({ 
  maxFileSize: 10000000, 
  maxFiles: 10 
}))

const server = new ApolloServer({
  typeDefs,
  resolvers: {
    Upload: GraphQLUpload,
    ...resolvers
  }
  uploads: false
  ...
})

server.applyMiddleware({
  app
})
Gillyflower answered 21/10, 2021 at 21:13 Comment(0)
M
0

I've been dealing with this problem multiple times

the problem is old graphql-upload more accurately version 8 uses of node open stream function which is deprecated and no longer available in 13.x and older node versions.

so the solution is you can either use nvm to set your node version to 12 or use resolutions in your package.json file. first solution(in your favorite terminal):

nvm install 12; nvm use 12

second solution(in package.json):

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

also this is a problem I've been dealing with for months and apollo team really doesn't like updating their dependencies I think.

Moriyama answered 2/4, 2021 at 13:49 Comment(0)
J
-2

Using:

    "apollo-server": "^2.18.2",
    "apollo-server-express": "2.18.2",
    "aws-sdk": "^2.771.0",
    "express-fileupload": "^1.2.0",
    "graphql": "^15.3.0",
    "graphql-upload": "^11.0.0",
    "fs-capacitor": "^6.2.0",

I solve this by installing the latest fs-capacitor as a dependency

yarn add fs-capacitor
Jural answered 20/10, 2020 at 14:36 Comment(1)
Adding fs-capacitor 6.2.0 as a resolution worked for me.Curative

© 2022 - 2024 — McMap. All rights reserved.