kamilkisiela / apollo-angular

A fully-featured, production ready caching GraphQL client for Angular and every GraphQL server 🎁
https://apollo-angular.com
MIT License
1.5k stars 311 forks source link

File upload always empty object #1607

Closed SergenN closed 3 years ago

SergenN commented 3 years ago

Describe the bug

I'm trying to upload a file with angular-apollo but the file in the request is always an empty object:

{
   "operationName":"updateUserAvatar",
   "variables":{
      "userId":"103320",
      "photo":{}
   },
   "query":"mutation updateUserAvatar($userId: ID!, $photo: Upload) {\n  updateUserPhoto(input: {id: $userId, photo: $photo}) {\n    errors {\n      field\n      messages\n      __typename\n    }\n    user {\n      id\n      email\n      firstName\n      lastName\n      photo\n      aboutMe\n      __typename\n    }\n    __typename\n  }\n}\n"
}

To Reproduce Steps to reproduce the behavior:

i'm initializing apollo as following:

export class AppModule {
  constructor(
    public apollo: Apollo,
    public httpLink: HttpLink,
    public authService: AuthService,
    public router: Router
  ) {
    this.createApollo();
  }

  createApollo() {
    const basic = setContext((operation, context) => ({
      headers: {
        Accept: 'charset=utf-8'
      }
    }));

    const auth = setContext(async (operation, context) => {
      let token = localStorage.getItem('Token');
      const expiry = localStorage.getItem('TokenExpiry');
      const refresh = localStorage.getItem('Refresh');
      const role = localStorage.getItem('Role');

      const expiryDate = new Date(expiry);
      const now = new Date();

      if (!expiryDate) {
        this.router.navigate(['login']);
        return;
      }

      if (expiryDate < now) {
        if (!refresh) {
          this.router.navigate(['login']);
          return;
        }
        token = (await this.authService.refresh()).access_token;
      }

      if (role) {
        return {
          headers: {
            Authorization: `Bearer ${token}`,
            'X-Role': role
          }
        };
      } else {
        return {
          headers: {
            Authorization: `Bearer ${token}`
          }
        };
      }
    });

    let uri = '/graphql';
    if (environment.production) {
      uri = environment.api + uri;
    }

    const link = ApolloLink.from([basic, auth, createHttpLink({ uri })]);

    this.apollo.create({
      link,
      cache: new InMemoryCache(),
    });
  }

In the component i have the following:

      const file = event.target.files[0];
      await this.apollo.mutate({
        mutation: updateUserAvatar,
        variables: {
          userId: this.currentUser.user.id,
          photo: file
        },
        context: {
          useMultipart: true
        }
      }).toPromise();

My query:

export const updateUserAvatar = gql'
    mutation updateUserAvatar($userId: ID!, $photo: Upload) {
        updateUserPhoto(input: {id: $userId, photo: $photo}) {
            errors {
                field
                messages
            }
        }
    }
';

Expected behavior

I expect the image to be uploaded correctly instead of an empty object.

Environment:

Additional context

fetis commented 3 years ago

apollo-client doesn't support file upload and hence apollo-angular doesn't support it too. you have to do a classical POST request here or try to find the Apollo link component that will do a multipart upload for you. I know only apollo-upload-client which uses fetch internally and hence isn't compatible with Angular

some more information on this issue here https://github.com/apollographql/apollo-client/issues/6010

PhilippS93 commented 3 years ago

Is this on the roadmap for this plugin? Seems to be a very relevant feature and crucial for many people.

fetis commented 3 years ago

@PhilippS93 I agree that it's very often used functionality but it should appear in apollo-client first to be available for us