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

W. Reyna
  • 724
  • 2
  • 7
  • 24

9 Answers9

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)

W. Reyna
  • 724
  • 2
  • 7
  • 24
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

Meursault
  • 305
  • 3
  • 10
  • 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: https://github.com/apollographql/apollo-server/issues/4190 – Eric Burel Nov 23 '21 at 10:34
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,
      });
    
Ashish Bastola
  • 331
  • 2
  • 5
  • 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? – Johhan Santana Jan 08 '21 at 20:38
  • 2
    @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. – Tamir Daniely Feb 28 '21 at 08:14
  • This answer is really the correct one, just remember to call `app.use(graphqlUploadExpress({ maxFileSize: 1000000000, maxFiles: 10 }));` before applying Apollo sever middleware. – Marek Piechut Mar 30 '21 at 11:25
  • 1
    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 }) } – Juliano Costa Apr 22 '21 at 13:38
  • 1
    @JulianoCosta you probably forgot to add this to the resolvers you pass into your schema: `resolvers: { Upload: GraphQLUpload, }` As per the documentation: Upload: https://github.com/jaydenseric/graphql-upload#class-graphqlupload In version 8, it worked without this resolver. – Narretz Aug 31 '21 at 16:40
  • @Narretz, yes, it was the problem. I did it yet and it solved my problem. Tnx 4 ur help – Juliano Costa Sep 01 '21 at 19:08
  • 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!! – dreamerkumar Jan 28 '22 at 22:22
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.

Everton Castro
  • 129
  • 1
  • 8
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

pranav shinde
  • 1,260
  • 13
  • 11
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, graphql-upload@12.0.0 and apollo-server-express@2.25.1:

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

BigMan73
  • 1,344
  • 15
  • 14
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
})
Mike
  • 11
  • 1
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.

DevAddict
  • 1,583
  • 1
  • 10
  • 17
-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
msroot
  • 817
  • 11
  • 13