2

I have a nodejs express app that serves as a proxy between a client application and a graphql api. The graphql api implements the graphql-multipart-request-spec. I am using the graphql-request package to query it. The proxy application uses nestjs and multer for the upload.

According to graphql-request docs you can upload files like this


const UploadUserAvatar = gql`
  mutation uploadUserAvatar($userId: Int!, $file: Upload!) {
    updateUser(id: $userId, input: { avatar: $file })
  }
`

request('/api/graphql', UploadUserAvatar, {
  userId: 1,
  file: createReadStream('./avatar.img'),
})

But that implies that the uploaded file has been saved to the file system. Is it possible to do the same thing using the buffer of the file?

I tried to turn the buffer to a ReadableStream like this:

const readable = new Readable()
readable._read = () => {} // _read is required but you can noop it
readable.push(file.buffer)
readable.push(null)

await request('/api/graphql', UploadUserAvatar, {
  userId: 1,
  file: readable,
})

But the graphql api returns the following error:

Unexpected end of multipart data

Timo Jokinen
  • 707
  • 7
  • 26
  • ... async/await ? – xadm Jan 02 '21 at 01:47
  • I am familiar with promises but i don‘t see how that is supposed to solve my issue – Timo Jokinen Jan 02 '21 at 11:24
  • it's required to not close connection and return response ... https://stackoverflow.com/q/60006391/6124657 ... how `file` is 'extracted'/request is processed [from multipart]? – xadm Jan 02 '21 at 15:49
  • I am using await but I omitted it for the sake of the example. The file is extracted with the multer library and it works if i save it on the disc but not when using the buffer – Timo Jokinen Jan 02 '21 at 15:55
  • IMHO you should show entire context ... how about https://stackoverflow.com/a/56087675/6124657 ... do you need buffer at all? why not just forward FormData? – xadm Jan 02 '21 at 16:59

1 Answers1

1

I got into similar issue and I solved it by using form-data package.

The soultion that worked for me:

import FormData from "form-data";

function uploadUserAvatar(userId: string, readable) {
    const requestBody = new FormData();
    
    requestBody.append('operations', JSON.stringify({
        query: UploadUserAvatar,
        variables: { userId }
    }));
    
    requestBody.append('map', JSON.stringify({
        0: ['variables.file']
    }));

    requestBody.append('0', readable);
    
    return new Promise((resolve, reject) => {
        requestBody.submit('http://endpoint/api/graphql', (err, res) => {
            if (err) {
                reject(err);
            } else {
                let body = '';
                res.on('readable', () => {
                    body += res.read();
                });
                
                res.on('end', () => {
                    resolve(JSON.parse(body));
                });
            }
        });
    });
}

I hope it will solve your problem too.

Sagie
  • 996
  • 3
  • 12
  • 25