How to Process Uploaded Image with Graphql Apollo with SharpJS in NodeJS?

How to Process Uploaded Image with Graphql Apollo with SharpJS in NodeJS?


4

I have a graphql mutation that gets an image from the frontend, and that then is processed and optimized on my server.

But I can’t figure out how to pass my image to sharp.

Here is my code:

const Mutation = {
    createImage: async (_, { data }) => {
        const { file } = data
        const image = await file

        console.log(image)

        const sharpImage = sharp(image)
    }
}

I know the code doesn’t work and sharp throws an error saying that the input is invalid. So how can I work with createReadStream and to create an instance of sharp?

When I console.log(image), here is what I see:

image {
  filename: 'image.png',
  mimetype: 'image/png',
  encoding: '7bit',
  createReadStream: [Function: createReadStream]
}

Thanks a lot in advance!

6

  • stream != buffer ?

    – xadm

    May 28, 2021 at 9:53

  • @xadm Thanks for your input. I know that stream is not a buffer. I was just trying to provide some example to work with. I am trying to understand how to use createReadStream and process the image with sharp

    – Anatol

    May 28, 2021 at 9:58

  • check args … file is ready as awaited (and if passed corectly, of course) … console.log image or file … stackoverflow.com/a/61452904/6124657

    – xadm

    May 28, 2021 at 10:05

  • @xadm when I pass the awaited file result to sharp, I still get [Error: Input file is missing].I added the result of the console.log of the awaited file to my question

    – Anatol

    May 28, 2021 at 10:22

  • it seams you can use stream … stackoverflow.com/a/61786860/6124657

    – xadm

    May 28, 2021 at 10:49

2 Answers
2


4

So I figured out the solution to my question.

First, I found out that I needed to add scalar Upload to typeDefs.

Then, I need to add a resolver for Upload like this:

const { GraphQLUpload } = require('graphql-upload');

const server = new ApolloServer({
  resolvers: {
    Upload: GraphQLUpload,
  }
})

Then in my resolver, here is what I had to do:

// this is a utility function to promisify the stream and store the image in a buffer, which then is passed to sharp
const streamToString = (stream) => {
    const chunks = [];
    return new Promise((resolve, reject) => {
        stream.on('data', (chunk) => chunks.push(Buffer.from(chunk)));
        stream.on('error', (err) => reject(err));
        stream.on('end', () => resolve(Buffer.concat(chunks)));
    })
}

const Mutation = {
    createImage: async (_, { data }) => {
        const { file } = data
        const { createReadStream } = await file

        const image = await streamToString(createReadStream())

        const sharpImage = sharp(image)
    }
}


0

Apollo recommends using signed URLs rather than uploading files via mutations. https://www.apollographql.com/blog/backend/file-uploads/file-upload-best-practices/

This is due to CSRF risks and performance impacts, especially as an app scales. For an app which properly handles the CSRF risks, uploading a file via a mutation should be fine.



Leave a Reply

Your email address will not be published. Required fields are marked *