keystone icon indicating copy to clipboard operation
keystone copied to clipboard

Image uploading through the backend db api fails

Open molotow11 opened this issue 1 year ago • 5 comments

Hello, Guys!

I've tried to upload an image via backend api, but not luck. So, I have local image, getting it as stream, then creating Upload instance. I've used the following code:

const upload = prepareToUpload(__dirname + '/tmp.jpg');
const image = await localDb.Image.createOne({
 data: {
   image: {
     upload: upload,
    }
 }
});

const prepareToUpload = (filePath: string) => {
  const filename = path.basename(filePath);

  const stream = fs.createReadStream(filePath);
  const createReadStream = () => stream;

  const mimetype = 'image/jpeg';
  const encoding = 'utf-8';

  const image: Upload = {
    createReadStream,
    filename,
    mimetype,
    encoding,
  };

  const upload = new Upload();
  upload.resolve(image);

  return upload;
};

Expected to be Image uploaded but got this error: Error creating Image GraphQLError: Variable "$data" got invalid value { resolve: [function], reject: [function], promise: {}, file: { createReadStream: [function createReadStream], filename: "tmp.jpg", mimetype: "image/jpeg", encoding: "utf-8" } } at "data.image.upload"; Upload value invalid.

node 16.20.0

    "@keystone-6/auth": "^7.0.0",
    "@keystone-6/core": "^5.2.0",
    "@keystone-6/fields-document": "^8.0.0",
    "dotenv": "^16.0.3",
    "graphql-upload": "^16.0.2",
    "nodejs-mysql": "^0.1.3",
    "readable": "^1.1.3",
    "request": "^2.88.2"

molotow11 avatar Jun 16 '23 10:06 molotow11

Hi,

This error most probably means that the Upload instance you created is invalid and graphql-upload fails it's check.

I am not an expert in graphql-upload, but have a look at code that works for me (https://github.com/keystonejs/keystone/issues/7986#issuecomment-1459606289). Comparing those, I think you need to promisify your image before upload.resolve. Just guessing, did not try it.

marekryb avatar Jun 19 '23 00:06 marekryb

Hello, @marekryb Thank you, yes I've checked its type and it was Upload. Anyway, tried your way for convert it into Upload again, but no luck.

But, it is strange issue on my and that I was unable to import Upload standard way, so I've used it like the following.

 const uploadImport = await import('graphql-upload/Upload.mjs');
 const upload = new uploadImport.default;

molotow11 avatar Jun 19 '23 06:06 molotow11

@molotow11 I checked your code and it works fine (except for types).

Now that you mentioned that you import Upload.mjs, I think I know what is happening. keystone is using graphql-upload 15.0.2 https://github.com/keystonejs/keystone/blob/a412558e5e44abcba4e675da9a0d3e486231148d/packages/core/package.json#L252 and you are probably using 16.0.2.

This can create all kind of weird things, including mismatch of class prototypes. Try installing exact version that keystone is using.

Also now looking at your error message, you can see that your Upload object has empty promise (promise {}), but this is what is used later in graphql-upload

    if (value instanceof Upload) return value.promise;

marekryb avatar Jun 19 '23 07:06 marekryb

@marekryb Thanks so much, yes it works fine with "graphql-upload": "^15.0.2", But, is it stable solution?

molotow11 avatar Jun 19 '23 11:06 molotow11

It depends how you define stable.

Just whenever you update keystone, you need to keep track of graphql-upload version changes.

marekryb avatar Jun 22 '23 06:06 marekryb