Skip to content

ReactNative/Expo - file upload does not work #496

@zdnk

Description

@zdnk

Description

Setting Upload scalar to ExtractableFile and passing ReactNativeFile as variable fails because graphql-request does not send the file, but just the stringified ReactNativeFile

export class APIClient {
  public tokenProvider?: TokenProvider | null = null;
  private _url: string;
  private _client: GraphQLClient;

  constructor(url: string) {
    this._url = url;
    this._client = new GraphQLClient(url, {
      requestMiddleware: async (request) => {
        if (!this.tokenProvider) {
          return request;
        }

        request.headers = {
          ...request.headers,
          Authorization: `Bearer ${await this.tokenProvider.get()}`,
        };
        return request;
      },
    });
  }

  public request<T = any, V = Variables>(
    document: RequestDocument,
    variables?: V,
    headers?: Headers
  ): Promise<T> {
    return this._client.request(document, variables, headers);
  }

  public sendImage(
    consultationId: string,
    image: ExtractableFile,
  ): Promise<SendImageMutation> {
    return this.request(SendImageDocument, { consultationId, image })
  }
}
mutation sendImage($consultationId: String!, $image: Upload!) {
    send_message(id: $consultationId, file: $image) {
        id
    }
}
      const file = new ReactNativeFile({
        uri: asset.uri,
        name: asset.fileName ? asset.fileName : undefined,
      });
      client.sendImage(props.consultationId, file);
import { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: 'https://api.tlappka.dev/graphql',
  documents: ['libs/api/**/*.tsx', 'libs/api/**/*.graphql', 'libs/api/**/*.gql'],
  generates: {
    'libs/api/generated/types.ts': {
      plugins: [
        'typescript',
        'typescript-operations',
        'typescript-react-query',
        { add: { content: `import { ExtractableFile } from 'extract-files';` } },
      ],
      config: {
        scalars: {
          Upload: 'ExtractableFile',
          DateTime: 'Date',
        },
        fetcher: {
          func: '../fetcher#fetcher',
          isReactHook: false,
        },
      },
    },
    'libs/api/generated/sdk.ts': {
      plugins: ['typescript-graphql-request'],
      config: {
        importOperationTypesFrom: 'Operations',
        importDocumentNodeExternallyFrom: './types',
        documentMode: 'external',
      },
    },
  },
};

export default config;
import request from 'graphql-request';
import { client } from './client';

export function fetcher<TData, TVariables extends { [key: string]: any }>(
  query: string,
  variables?: TVariables,
  requestHeaders?: RequestInit['headers']
) {
  return async (): Promise<TData> => {
    var token: string | null = null;

    if (client.tokenProvider) {
      try {
        token = await client.tokenProvider.get();
      } catch (error) {
        console.error('token error', error);
        throw error;
      }
    }

    const headers = {
      ...(token ? { Authorization: `Bearer ${token}` } : {}),
      ...(requestHeaders ? requestHeaders : {}),
    };

    try {
      return await request(client.url, query, variables, headers);
    } catch (error) {
      console.error('api error', error);
      throw error;
    }
  };
}

Error:

Error: Variable "$image" got invalid value { uri: "file:///var/mobile/Containers/Data/Application/71D42506-0B00-4AD1-8788-58E96D5F5DD7/Library/Caches/ImagePicker/8DC46B0A-B360-47DD-96BE-44750D1DA1F1.jpg", name: "70220832162__08A4CF04-F008-45E3-8182-9F8D257BB778.jpg" }; Upload value invalid.: {"response":{"errors":[{"message":"Variable "$image" got invalid value { uri: "file:///var/mobile/Containers/Data/Application/71D42506-0B00-4AD1-8788-58E96D5F5DD7/Library/Caches/ImagePicker/8DC46B0A-B360-47DD-96BE-44750D1DA1F1.jpg", name: "70220832162__08A4CF04-F008-45E3-8182-9F8D257BB778.jpg" }; Upload value invalid.","locations":[{"line":2,"column":50}],"extensions":{"code":"BAD_USER_INPUT","exception":{"stacktrace":["GraphQLError: Variable "$image" got invalid value { uri: "file:///var/mobile/Containers/Data/Application/71D42506-0B00-4AD1-8788-58E96D5F5DD7/Library/Caches/ImagePicker/8DC46B0A-B360-47DD-96BE-44750D1DA1F1.jpg", name: "70220832162__08A4CF04-F008-45E3-8182-9F8D257BB778.jpg" }; Upload value invalid."," at /workspace/node_modules/graphql/execution/values.js:147:11"," at coerceInputValueImpl (/workspace/node_modules/graphql/utilities/coerceInputValue.js:154:9)"," at coerceInputValueImpl (/workspace/node_modules/graphql/utilities/coerceInputValue.js:49:14)"," at coerceInputValue (/workspace/node_modules/graphql/utilities/coerceInputValue.js:32:10)"," at coerceVariableValues (/workspace/node_modules/graphql/execution/values.js:132:69)"," at getVariableValues (/workspace/node_modules/graphql/execution/values.js:45:21)"," at buildExecutionContext (/workspace/node_modules/graphql/execution/execute.js:280:63)"," at execute (/workspace/node_modules/graphql/execution/execute.js:116:22)"," at execute (/workspace/node_modules/apollo-server-core/dist/requestPipeline.js:205:48)"," at processGraphQLRequest (/workspace/node_modules/apollo-server-core/dist/requestPipeline.js:148:34)"]}}}],"status":400,"headers":{"map":{"access-control-allow-origin":"*","cache-control":"private","cf-cache-status":"DYNAMIC","cf-ray":"7b295a21485b27b8-PRG","content-length":"1644","content-type":"application/json; charset=utf-8","date":"Tue, 04 Apr 2023 11:53:38 GMT","etag":"W/"66c-lPDQm8YdxMlT56EPPMEvHKrw+nU"","server":"cloudflare","x-do-app-origin":"c53198ca-756c-4760-b27d-f1b81b801d7f","x-do-orig-status":"400","x-powered-by":"Express"}}},"request":{"query":"\n mutation sendImage($consultationId: String!, $image: Upload!) {\n send_message(id: $consultationId, file: $image) {\n id\n }\n}\n ","variables":{"consultationId":"429ecf5a-e825-4fb1-951a-1a49972e31fd","image":{"uri":"file:///var/mobile/Containers/Data/Application/71D42506-0B00-4AD1-8788-58E96D5F5DD7/Library/Caches/ImagePicker/8DC46B0A-B360-47DD-96BE-44750D1DA1F1.jpg","name":"70220832162__08A4CF04-F008-45E3-8182-9F8D257BB778.jpg"}}}}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions