Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: upload.arrayBuffer is not a function #8497

Closed
4 tasks done
VKlapan opened this issue Apr 6, 2023 · 27 comments · Fixed by #8721
Closed
4 tasks done

Error: upload.arrayBuffer is not a function #8497

VKlapan opened this issue Apr 6, 2023 · 27 comments · Fixed by #8721
Labels
state:released Released as stable version state:released-alpha Released as alpha version state:released-beta Released as beta version type:question Support or code-level question

Comments

@VKlapan
Copy link

VKlapan commented Apr 6, 2023

New Issue Checklist

Issue Description

When I try to upload a file (via Parse Dashboard GraphQL API Console) I receive an error:
"upload.arrayBuffer is not a function".
Do not understand - what is wrong?

Steps to reproduce

Try to check file uploading via Parse Dashboard GraphQL API Console.
Query:
mutation createFile($file: Upload!) { createFile(input: {upload:$file}) { fileInfo { name url } } }
Variables - base64 string:
{ "file":"V29ya2luZyBhdCBQYXJzZSBpcyBncmVhdCE=" }

Actual Outcome

Response:
{ "errors": [ { "message": "upload.arrayBuffer is not a function", "locations": [ { "line": 2, "column": 3 } ], "path": [ "createFile" ] } ], "data": { "createFile": null } }

Expected Outcome

Environment

Server

  • Parse Server version: 6.0.0
  • Operating system: Linux Mint 20.2 Cinnamon and docker
  • Local or remote host (AWS, Azure, Google Cloud, Heroku, Digital Ocean, etc): local

Database

  • System (MongoDB or Postgres): MongoDB
  • Database version: mongo:4
  • Local or remote host (MongoDB Atlas, mLab, AWS, Azure, Google Cloud, etc): local

Client

  • SDK (iOS, Android, JavaScript, PHP, Unity, etc): JavaScript
  • SDK version: "@types/parse": "^2.18.13"

Logs

server config:

`
Successfully compiled 1 file with Babel (297ms).
SERVER CONFIG
Configuration loaded from /parse-server/config/server.js
warn: DeprecationWarning: The Parse Server option 'allowClientClassCreation' default will change to 'false' in a future version.
warn: DeprecationWarning: The Parse Server option 'allowExpiredAuthDataToken' default will change to 'false' in a future version.
RUN Parse Server Cloud
allowClientClassCreation: true
allowCustomObjectId: false
allowExpiredAuthDataToken: true
allowHeaders: ["X-Apollo-Tracing"]
appId: parse-template
appName: Parse Template
cacheMaxSize: 10000
cacheTTL: 5000
cloud: /parse-server/cloud-dist
collectionPrefix:
customPages: {"invalidLink":"http://localhost/invalid-link","invalidVerificationLink":"http://localhost/invalid-verification-link","passwordResetSuccess":"http://localhost/password-reset-success","choosePassword":"http://localhost/choose-password"}
databaseURI: mongodb://db/parse
defaultLimit: 100
directAccess: true
emailAdapter: {"options":{"service":"SMTP","fromAddress":"dev@.com","user":"dev@","password":"","host":"mail..com","port":587,"isSSL":false,"templates":{"resetPassword":{"path":"/parse-server/templates/reset-password","subject":"Password reset"},"verifyEmail":{"path":"/parse-server/templates/verify-email","subject":"Email verification"}}}}
emailVerifyTokenReuseIfValid: false
emailVerifyTokenValidityDuration: 7200
enableAnonymousUsers: true
enableExpressErrorHandler: false
enforcePrivateUsers: true
expireInactiveSessions: true
filesAdapter: {"_encryptionKey":null,"_filesDir":"/parse-server/files"}
fileUpload: {"enableForPublic":true,"enableForAnonymousUser":true,"enableForAuthenticatedUser":true}
graphQLPath: /graphql
graphQLSchema: /parse-server/schema.graphql
host: 0.0.0.0
idempotencyOptions: {"ttl":300,"paths":[]}
liveQuery: {"classNames":[]}
logLevel: warn
logLevels: {"triggerAfter":"info","triggerBeforeError":"error","triggerBeforeSuccess":"info"}
logsFolder: ./logs
maintenanceKeyIps: ["127.0.0.1","::1"]
masterKey: REDACTED
masterKeyIps: ["172.30.0.0/16"]
maxUploadSize: 4000mb
mountGraphQL: true
mountPath: /
mountPlayground: false
objectIdSize: 10
pages: {"enableRouter":false,"enableLocalization":false,"localizationFallbackLocale":"en","placeholders":{},"forceRedirect":false,"pagesPath":"./public","pagesEndpoint":"apps","customUrls":{},"customRoutes":[]}
passwordPolicy: {"doNotAllowUsername":true,"resetTokenValidityDuration":10800}
playgroundPath: /playground
port: 1337
preserveFileName: false
preventLoginWithUnverifiedEmail: false
protectedFields: {"_User":{"
":["email"],"role:admin":[]}}
publicServerURL: http://localhost:1337
rateLimit: []
requestKeywordDenylist: [{"key":"_bsontype","value":"Code"},{"key":"constructor"},{"key":"proto"}]
restAPIKey: asd78sd897df09as87dfh09asd
revokeSessionOnPasswordReset: true
scheduledPush: false
security: {"enableCheck":false,"enableCheckLog":false}
sessionLength: 31536000
startLiveQueryServer: true
trustProxy: []
verbose: false
verifyUserEmails: true
jsonLogs: false
level: undefined
serverURL: http://localhost:1337/
state: initialized

`

@parse-github-assistant
Copy link

parse-github-assistant bot commented Apr 6, 2023

Thanks for opening this issue!

  • 🚀 You can help us to fix this issue faster by opening a pull request with a failing test. See our Contribution Guide for how to make a pull request, or read our New Contributor's Guide if this is your first time contributing.

@Moumouls
Copy link
Member

Moumouls commented Apr 8, 2023

Hi @VKlapan,

Parse GraphQL server follow the multi part graphql upload spec. You can't simply call a mutation into the Dashboard GraphQL console. Parse GraphQL server does not support base 64 file upload ( it's also a really bad practice since it do not leverage streaming, multi part http systems)

You can see some client side examples: https://github.com/jaydenseric/graphql-multipart-request-spec
If you need to upload an image using the dashboard i can suggest you to just use the "Browser" section.

Or you can give a try to https://altairgraphql.dev/ that support multi part file upload.

@mtrezza mtrezza added the type:question Support or code-level question label Apr 9, 2023
@mtrezza
Copy link
Member

mtrezza commented Apr 9, 2023

I'm closing this as it does not seem to be a Parse Server issue.

@mtrezza mtrezza closed this as completed Apr 9, 2023
@VKlapan
Copy link
Author

VKlapan commented Apr 10, 2023

Hi @VKlapan,

Parse GraphQL server follow the multi part graphql upload spec. You can't simply call a mutation into the Dashboard GraphQL console. Parse GraphQL server does not support base 64 file upload ( it's also a really bad practice since it do not leverage streaming, multi part http systems)

You can see some client side examples: https://github.com/jaydenseric/graphql-multipart-request-spec If you need to upload an image using the dashboard i can suggest you to just use the "Browser" section.

Or you can give a try to https://altairgraphql.dev/ that support multi part file upload.

Thanks.
I'll try to use your tips.

@VKlapan
Copy link
Author

VKlapan commented Apr 10, 2023

So...
I've tried to use information from examples: https://github.com/jaydenseric/graphql-multipart-request-spec
but with zero result (

For example, I have a question:
Is this example correct and it has to work?
https://github.com/jaydenseric/graphql-multipart-request-spec#curl-request

But I have error: {"error":"Unexpected token - in JSON at position 0"}
Why?

@VKlapan
Copy link
Author

VKlapan commented Apr 18, 2023

a few more details:

I have this error on my parse server 6.0.0 :

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

Does anybody know how to fix it ?

@Moumouls
Copy link
Member

Hi @VKlapan we can't help here until you provide more details to reproduce the error, can you share a failing test or a reproduction repository.

I use the alpha release of Parse Server in production with file upload, my team has a lot of upload tests we don't have any issue.

@VKlapan
Copy link
Author

VKlapan commented Apr 19, 2023

Hi @Moumouls
I just try to learn Parse Server now so I don't have either any tests or reproduction repository. I have the simplest code to use base mutation createFile - createFile(input: CreateFileInput!): CreateFilePayload :

on web app

const [file, setFile] = useState<File | undefined>(undefined);

  const MUTATION = gql'
    mutation createFile($file: Upload!) {
      createFile(input: {upload: $file}) {
        fileInfo {
          name
          url
        }
      }
    }
  ';

  const [mutate] = useMutation(MUTATION);

  const onChangeHandler = (event: ChangeEvent<HTMLInputElement>) => {
    if (event.target && event.target.files) {
      const inputedFile = event.target.files[0];
      setFile(inputedFile);
    }
  };

  const onClickHandler = () => {
    console.log('onClick \n', file);
    mutate({variables: {file}});
  };

Payload on DevTools/Neywork:
https://monosnap.com/direct/PjTfG1Uh4kwucPQhnFDVQc3k0Nj2Tm

response:
{"error":"Unexpected token - in JSON at position 0"}

server log:

[47] parse-server running on http://localhost:1337/ [47] GraphQL running on http://localhost:1337/graphql Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at new NodeError (node:internal/errors:400:5) at ServerResponse.setHeader (node:_http_outgoing:663:11) at ServerResponse.header (/parse-server/node_modules/express/lib/response.js:794:10) at ServerResponse.send (/parse-server/node_modules/express/lib/response.js:174:12) at ServerResponse.json (/parse-server/node_modules/express/lib/response.js:278:15) at handleParseErrors (/parse-server/lib/middlewares.js:398:9) at Layer.handle_error (/parse-server/node_modules/express/lib/router/layer.js:71:5) at trim_prefix (/parse-server/node_modules/express/lib/router/index.js:326:13) at /parse-server/node_modules/express/lib/router/index.js:286:9 at Function.process_params (/parse-server/node_modules/express/lib/router/index.js:346:12) at next (/parse-server/node_modules/express/lib/router/index.js:280:10) at Layer.handle_error (/parse-server/node_modules/express/lib/router/layer.js:67:12) at trim_prefix (/parse-server/node_modules/express/lib/router/index.js:326:13) at /parse-server/node_modules/express/lib/router/index.js:286:9 at Function.process_params (/parse-server/node_modules/express/lib/router/index.js:346:12) at next (/parse-server/node_modules/express/lib/router/index.js:280:10)

@Moumouls
Copy link
Member

Hi @VKlapan , you have may be forgot to use a compatible upload client ?

Like: https://www.apollographql.com/docs/react/data/file-uploads/

@VKlapan
Copy link
Author

VKlapan commented Apr 19, 2023

@Moumouls
Hm...
I think that I do it, don't I?:
package.json:

"dependencies": {
"@ant-design/icons": "^4.0.2",
"@apollo/client": "^3.7.12",
"@apollo/react-hooks": "^4.0.0",
"@umijs/hooks": "^1.8.0",
"antd": "^4.0.0",
"apollo-upload-client": "^17.0.0",
"graphql": "^16.6.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-infinite-scroller": "^1.2.4",
"react-router-dom": "^6.10.0",
"react-scripts": "5.0.1",
"styled-components": "^5.0.1"
},

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});
client.defaultOptions = defaultOptions;

const Provider: React.FC<PropsWithChildren> = ({children}) => (
  <ApolloProvider client={client}>{children}</ApolloProvider>
);

export default Provider;

@Moumouls
Copy link
Member

Did you setup correctly the Apollo upload link ?

@VKlapan
Copy link
Author

VKlapan commented Apr 19, 2023

@Moumouls

Did you setup correctly the Apollo upload link ?

I'm not sure((((
Can you give me some hints?

UPD:
Is it correct?

import {createUploadLink} from 'apollo-upload-client';

const httpLink = createUploadLink({
  uri: GRAPHQL_URI,
});

@Moumouls
Copy link
Member

Moumouls commented Apr 19, 2023

import { createUploadLink } from 'apollo-upload-client'
import {
	InMemoryCache,
	ApolloClient,
} from '@apollo/client'

const options = const options = {
			uri: graphQLUrl,
		}
const client = new ApolloClient({
				cache: new InMemoryCache(),
				link: createUploadLink(options),
			})

You client should be initialized like this @VKlapan

@VKlapan
Copy link
Author

VKlapan commented Apr 19, 2023

I can be wrong, but I think that I have the same.
(my full code as one piece):

import React, {PropsWithChildren} from 'react';
import {
  ApolloProvider,
  DefaultOptions,
  InMemoryCache,
  ApolloClient,
  createHttpLink,
} from '@apollo/client';
import {APPLICATION_ID, GRAPHQL_URI, API_KEY} from '../consts/env';
import {setContext} from '@apollo/client/link/context';

import {createUploadLink} from 'apollo-upload-client';

const httpLink = createUploadLink({
  uri: GRAPHQL_URI,
});

const authLink = setContext((_, {headers}) => {
  const token = localStorage.getItem('token');
  if (token) {
    return {
      headers: {
        'X-Parse-Application-Id': APPLICATION_ID,
        'X-Parse-REST-API-Key': API_KEY,
        'X-Parse-Session-Token': token,
      },
    };
  }
  return {
    headers: {
      'X-Parse-Application-Id': APPLICATION_ID,
      'X-Parse-REST-API-Key': API_KEY,
    },
  };
});

const defaultOptions: DefaultOptions = {
  watchQuery: {
    fetchPolicy: 'network-only',
  },
  query: {
    fetchPolicy: 'network-only',
  },
  mutate: {
    fetchPolicy: 'network-only',
  },
};

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});
client.defaultOptions = defaultOptions;

console.log('CLIENT', authLink);

const Provider: React.FC<PropsWithChildren> = ({children}) => (
  <ApolloProvider client={client}>{children}</ApolloProvider>
);

export default Provider;

@Moumouls help me to find my bugs (((

@VKlapan
Copy link
Author

VKlapan commented Apr 20, 2023

UPD
one more attempt...

I use Altair GraphQL Client, which has embedded File upload mechanism
and I try to use createFile mutation, which is embedded to Parse Server

and I receive the same Error:

{"error":"Unexpected token - in JSON at position 0"}

Can anybody explain to me - why? The base features don't work !
Who can prove the opposite?

What is very important - I use Parse Server 6.0.0

@Moumouls
Copy link
Member

Okay @VKlapan it's weird can you provide a reproduction repo with the failing behavior, for example just a simple react app with the apollo upload client configured and a guide to reproduce the error. I think it will be the only solution to understand the issue since parse server test suite is working correctly on this feature and also in my company we don't have any issue with this one ( we run on latest parse server version + some additional features from my fork)

@mtrezza mtrezza reopened this Apr 20, 2023
@mtrezza
Copy link
Member

mtrezza commented Apr 20, 2023

Reopened as the issue seems to require investigation

@VKlapan
Copy link
Author

VKlapan commented Apr 21, 2023

Okay @VKlapan it's weird can you provide a reproduction repo with the failing behavior, for example just a simple react app with the apollo upload client configured and a guide to reproduce the error. I think it will be the only solution to understand the issue since parse server test suite is working correctly on this feature and also in my company we don't have any issue with this one ( we run on latest parse server version + some additional features from my fork)

Ok @Moumouls I'll create a repo.
But I have to ask (to be sure that we understand each other correctly) - do you run parse server 6.0.0 in your company? or wich version?

@VKlapan

This comment was marked as off-topic.

@mtrezza

This comment was marked as off-topic.

@VKlapan

This comment was marked as off-topic.

@FransGH
Copy link
Contributor

FransGH commented Aug 28, 2023

I'm currently looking into the same issue on Parse Server 5.3.3. On development, I was able to fix both the Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client and "error":"Unexpected token - in JSON at position 0" by changing the mount order GraphQL and ParseServer in Express.

On staging/production, the server is not started via a custom script but from the command line. Above error can be resolved by patching ParseServer.js and mounting 'this.app' after GraphQL.

    if (options.mountGraphQL === true || options.mountPlayground === true) {
      let graphQLCustomTypeDefs = undefined;

      if (typeof options.graphQLSchema === 'string') {
        graphQLCustomTypeDefs = parse(fs.readFileSync(options.graphQLSchema, 'utf8'));
      } else if (typeof options.graphQLSchema === 'object' || typeof options.graphQLSchema === 'function') {
        graphQLCustomTypeDefs = options.graphQLSchema;
      }

      const parseGraphQLServer = new _ParseGraphQLServer.ParseGraphQLServer(this, {
        graphQLPath: options.graphQLPath,
        playgroundPath: options.playgroundPath,
        graphQLCustomTypeDefs
      });

      if (options.mountGraphQL) {
        parseGraphQLServer.applyGraphQL(app);
      }

      if (options.mountPlayground) {
        parseGraphQLServer.applyPlayground(app);
      }
    }

    app.use(options.mountPath, this.app);

I'm still testing if this has any side effects but if not, I'll create a PR.

@Moumouls
Copy link
Member

Hi @VKlapan @FransGH , I sent a PR for an update to grapqhl yoga V4, I saw the issue recently and I found the source during the upgrade.

I'll send a PR dedicated to this issue tomorrow since the fix is quite easy.

@Moumouls
Copy link
Member

It's related to the deepcopy of mutation args, converting the file object to a standard object. I don't know why it happens now ( maybe a package update side effect)

@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 7.0.0-alpha.14

@parseplatformorg parseplatformorg added the state:released-alpha Released as alpha version label Feb 14, 2024
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 7.0.0-beta.1

@parseplatformorg parseplatformorg added the state:released-beta Released as beta version label Mar 19, 2024
@parseplatformorg
Copy link
Contributor

🎉 This change has been released in version 7.0.0

@parseplatformorg parseplatformorg added the state:released Released as stable version label Mar 19, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
state:released Released as stable version state:released-alpha Released as alpha version state:released-beta Released as beta version type:question Support or code-level question
Projects
None yet
5 participants