jaydenseric/apollo-upload-client

Using apollo-upload-client with rest link

DamiToma opened this issue · 6 comments

Hi! I'm working on an app which is using Apollo Client on the frontend and need to upload files to my backend. So I set up my mutation as follows:

import { gql } from "@apollo/client";
import * as Apollo from "@apollo/client";

export type UploadFileInput = {
  taskId: number;
  input: { file: any };
};

export type UploadFileMutation = {
  create: { success: boolean };
};

export const UploadFileDocument = gql`
  mutation UploadFile($taskId: ID!, $file: Upload!) {
    create(taskId: $taskId, file: $file, input: {})
      @rest(type: "File" path: "/task/{args.taskId}/upload-file" method: "POST") {
      success
    }
  }
`;

export function useUploadFileMutation(
  baseOptions?: Apollo.MutationHookOptions<UploadFileMutation, UploadFileInput>
) {
  return Apollo.useMutation<UploadFileMutation, UploadFileInput>(
    UploadFileDocument,
    { ...baseOptions }
  );
}

Then I have a form with an input type file that submits the call as follows:

uploadFile({
  variables: {
    taskId: taskIds[i],
    file: task.document[0],
  },    
});

The problem is when I check the request sent to the backend, the payload is always empty (its value is {}). I think it might be related to the fact I added input: {} in the mutation definition, but I must do that as a body cannot be empty in a POST request (Apollo would throw an error otherwise).

I'm 100% sure that task.document[0] holds the right file information, printing it out to the console shows it's a File object. So I wonder whether the fact that I'm using a REST link might be causing any issues. I gave it several tries, but could not get it to work. Any help on this would be much appreciated, thanks!

Usually when a File value in a payload is incorrectly {}, that means that the upload terminating link isn't setup correctly in Apollo Client, because that's how the regular HTTP link from Apollo serialises a File. Double check that the installation instructions have been followed correctly:

https://github.com/jaydenseric/apollo-upload-client/tree/v17.0.0#installation

I'm not using HttpLink, but RestLink from apollo-link-rest. The client has a chain of links

const client = new ApolloClient({
  cache: RootCache,
  link: from([
    authLink,
    createUploadLink({
      uri: URI
    }),
  ]),
});

Where authLink is defined concatenating the authentication link with the httpLink

const httpLink = new RestLink({
  uri: URI,
});

const withToken = setContext((_, { headers }) => {
  ...
});

const authLink = withToken.concat(httpLink);

Might this be causing issues, even if I'm not using the HTTP link from @apollo/client?

You can only have one "terminating" link that ends the chain and makes the request. As per the installation instructions:

Apollo Client can only have 1 terminating Apollo Link that sends the GraphQL requests; if one such as HttpLink is already setup, remove it.

I'm not experienced with the REST link, but it looks like a terminating link that sends requests.

I'm sorry for the additional question, but I'm not very experienced with Apollo Client. By removing the terminating link, do you mean I should get rid of .concat(httpLink)? I've looked the link you shared up and seems like it can be the only terminating link. The thing is if I remove it how is the client going to send the request then?

The link created by createUploadLink is a terminating link that sends the final request. If there are no files to upload, it sends a GraphQL POST request, if there are files to upload, it sends a GraphQL multipart request. If you are working with a REST API instead of a GraphQL API, then the REST API isn't going to know what to do with a GraphQL multipart request. This apollo-upload-client package is for making requests to a GraphQL API, not a REST API so I think it might not be appropriate for your project.

Bummer, was hoping to have the two work together. Thanks a lot for the help!