jaydenseric / graphql-upload

Middleware and an Upload scalar to add support for GraphQL multipart requests (file uploads via queries and mutations) to various Node.js GraphQL servers.
https://npm.im/graphql-upload
MIT License
1.42k stars 131 forks source link

Graphql File Upload NestJS issue #383

Closed thedarkknight197 closed 5 months ago

thedarkknight197 commented 5 months ago

When i send a mutation with a file to my graphql endpoint i receive from backend this error:

[Nest] 23620  - 30/01/2024, 18:05:10   ERROR [ExceptionsHandler] Missing multipart field ‘operations’ (https://github.com/jaydenseric/graphql-multipart-request-spec).
BadRequestError: Missing multipart field ‘operations’ (https://github.com/jaydenseric/graphql-multipart-request-spec).
****

The object that arrive to backend is:

{
  "operationName": null,
  "variables": {
    "content": "",
    "chatroomId": 2,
    "attachment": {
      "promise": {},
      "file": {
        "filename": "upload",
        "mimetype": "application/octet-stream",
        "encoding": "7bit"
      }
    }
  },
  "query": "mutation($chatroomId: Float!, $content: String, $attachment: Upload) {\n  __typename\n  sendMessage(chatroomId: $chatroomId, content: $content, attachment: $attachment) {\n    __typename\n    attachment {\n      __typename\n      id\n      path\n    }\n    chatRoomId\n    content\n    createdAt\n    id\n    userId\n  }\n}"
}

I am using flutter as client following this documentation: https://github.com/zino-hofmann/graphql-flutter/blob/main/packages/graphql/README.md#graphql-upload I am sending file in this way:

IconButton(
  onPressed: () {
    String text = viewModel.content.text;
    if (text.isNotEmpty ||
        viewModel.attachment.isFileSet()) {
      final myFile = viewModel.attachment.file != null
          ? MultipartFile.fromBytes(
              "",
              viewModel.attachment.file!.readAsBytesSync(),
              filename: "upload",
            )
          : null;
      try {
        final result = runMutation({
          "content": text,
          "chatroomId": chatRoomId,
          if (viewModel.attachment.isFileSet())
            "attachment": myFile,
        });
        debugPrint(result.toString());
      } catch (e) {
        debugPrint(e.toString());
      }
      viewModel.clear();
    }
  },
  icon: const Icon(Icons.send_sharp),
),

It's my first time in graphql so i am very much confused about it.

I don't understand why but i need to add 2 times the Graphql middleware, the first time the body arrive empty and the second one the body have the data that i sent. This happen only when i send media.

I need the middleware only working on "/graphql" routes, because i have also rest media upload, and if is loaded globally my other crud operations stop to work.

This is my app.module configuration:

export class AppModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(
        graphqlUploadExpress({
          maxFileSize: 10000000000,
          maxFiles: 1,
        }),
      )
      .forRoutes('/graphql');

    consumer
      .apply(
        graphqlUploadExpress({
          maxFileSize: 10000000000,
          maxFiles: 1,
        }),
      )
      .forRoutes('/graphql');
  }
}

I am using graphql-upload": "^14.0.0", because latest also adding "allowJs": true, "maxNodeModuleJsDepth": 10, "module": "Node16", inside tsconfig.json as mentioned here: https://github.com/jaydenseric/graphql-upload?tab=readme-ov-file#requirements doesn't compile

jaydenseric commented 5 months ago

The current version is 16.0.2; I don't support past versions. Also, I can only support the graphql-upload software in my repo with bug fixes or new features; it's too hard for me to help people debug their project code with all sorts of other software like NestJS that I didn't make or maintain. The description of your issue doesn't suggest you are reporting a graphql-upload bug or feature request.

Conceptually, you can't run the same graphqlUploadExpress middleware multiple times on the same request. If you are doing that, don't. Or you will have problems.

It's none of my business, but I recommend people avoid abstract and complicated frameworks like NestJS. You just end up with frustrating limitations trying to configure it, a massive birds-nest of dependencies, and nasty issues where they don't care about supporting things like modern ESM for many years.

thedarkknight197 commented 5 months ago

I fixed much things, now uploading a file using a graphql gui client the request arrive correctly.

But the issue on flutter still present.

I debbugged the code, and in graphQlUploadExpress the request arrive with the file attached, but the request non arrive to my endpoint.

Have you any advice where i can ask to solve this issues? I don't understand if is the middleware that is not sending correctly the data or any other issue.

Thank you a lot

thedarkknight197 commented 5 months ago

@jaydenseric This is the full body from graphql client (working)

{
  "query": "mutation ($file: Upload) {\n  sendMessage(attachment: $file, chatroomId: 2, content: \"string\") {\n    attachment {\n      id\n      path\n    }\n    chatRoomId\n    content\n    createdAt\n    id\n    userId\n  }\n}\n",
  "variables": {
    "file": {
      "promise": {},
      "file": {
        "filename": "Cattura.PNG",
        "mimetype": "image/png",
        "encoding": "7bit"
      }
    }
  },
  "operationName": null
}

This is coming from my flutter mobile app, did you see some errors here?

{
  "operationName": null,
  "variables": {
    "file": {
      "promise": {},
      "file": {
        "filename": "1fc9f6fc-9fe6-4d10-85f5-da7005d31b8d2135490974604539669.webp",
        "mimetype": "application/octet-stream",
        "encoding": "7bit"
      }
    }
  },
  "query": "mutation sendMessage($file: Upload, $message: String) {\n  __typename\n  sendMessage(chatroomId: 2, content: $message, attachment: $file) {\n    __typename\n    content\n    createdAt\n    id\n    attachment {\n      __typename\n      id\n      path\n    }\n  }\n}"
}