File uploads

There are a few ways to accomplish file uploads in Apollo Server. Our blog post on file uploads goes in depth on the approaches we’ve taken, outlining the pros and cons of each.

Apollo recommends handling the file upload itself out-of-band of your GraphQL server for the sake of simplicity and security. This “signed URL” approach allows your client to retrieve a URL from your GraphQL server (via S3 or other storage service) and upload the file to the URL rather than to your GraphQL server. The blog post mentioned above goes into detail on how to set this up.

Another common approach involves adding file upload support directly to Apollo Server via the third-party graphql-upload library. This package provides support for the multipart/form-data content-type. Note: this approach is vulnerable to a CSRF mutation attack unless Apollo Server is explicitly configured with csrfPrevention: true. In the examples below, we demonstrate configuring CSRF prevention in order to prevent this vulnerability by always requiring a “preflight” OPTIONS request before executing an operation. When configuring your file upload client, you will need to send a non-empty Apollo-Require-Preflight header or Apollo Server will block the request. For example, if you use the apollo-upload-client package with Apollo Client Web, pass headers: {'Apollo-Require-Preflight': 'true'} to createUploadLink.

New in Apollo Server 3: Apollo Server 3 does not contain a built-in integration with graphql-upload like in Apollo Server 2. Instead, the instructions below show how to integrate it yourself. You cannot do this with the “batteries-included” apollo-server library; you must use a web framework integration such as apollo-server-express instead. This page shows how to integrate graphql-upload with Express and Fastify. To implement similar functionality with another Node.js HTTP framework (e.g., Koa), see the graphql-upload documentation for more information. Some integrations might need to use graphql-upload‘s processRequest directly.

index.ts

1

const

express

=

require

(

'express'

)

;

2

const

{

ApolloServer

,

gql

}

=

require

(

'apollo-server-express'

)

;

3

const

{

4

GraphQLUpload

,

5

graphqlUploadExpress

,

6

}

=

require

(

'graphql-upload'

)

;

7

const

{

finished

}

=

require

(

'stream/promises'

)

;

8

const

{

ApolloServerPluginLandingPageLocalDefault

}

=

require

(

'apollo-server-core'

)

;

9

10

const

typeDefs

=

gql

`

11

12

13

14

scalar

Upload

15

16

type

File

{

17

filename

:

String

!

18

mimetype

:

String

!

19

encoding

:

String

!

20

}

21

22

type

Query

{

23

24

25

26

otherFields

:

Boolean

!

27

}

28

29

type

Mutation

{

30

31

singleUpload

(

file

:

Upload

!

)

:

File

!

32

}

33

`

;

34

35

const

resolvers

=

{

36

37

38

Upload

:

GraphQLUpload

,

39

40

Mutation

:

{

41

singleUpload

:

async

(

parent

,

{

file

}

)

=>

{

42

const

{

createReadStream

,

filename

,

mimetype

,

encoding

}

=

await

file

;

43

44

45

46

const

stream

=

createReadStream

(

)

;

47

48

49

50

const

out

=

require

(

'fs'

)

.

createWriteStream

(

'local-file-output.txt'

)

;

51

stream

.

pipe

(

out

)

;

52

await

finished

(

out

)

;

53

54

return

{

filename

,

mimetype

,

encoding

}

;

55

}

,

56

}

,

57

}

;

58

59

async

function

startServer

(

)

{

60

const

server

=

new

ApolloServer

(

{

61

typeDefs

,

62

resolvers

,

63

64

csrfPrevention

:

true

,

65

cache

:

'bounded'

,

66

plugins

:

[

ApolloServerPluginLandingPageLocalDefault

(

{

embed

:

true

}

)

]

,

67

}

)

;

68

await

server

.

start

(

)

;

69

70

const

app

=

express

(

)

;

71

72

73

app

.

use

(

graphqlUploadExpress

(

)

)

;

74

75

server

.

applyMiddleware

(

{

app

}

)

;

76

77

await

new

Promise

<

void

>

(

(

r

)

=>

app

.

listen

(

{

port

:

4000

}

,

r

)

)

;

78

79

console

.

log

(

`

🚀 Server ready at http://localhost:4000

${

server

.

graphqlPath

}

`

)

;

80

}

81

82

startServer

(

)

;

index.js

1

const

express

=

require

(

'express'

)

;

2

const

{

ApolloServer

,

gql

}

=

require

(

'apollo-server-express'

)

;

3

const

{

4

GraphQLUpload

,

5

graphqlUploadExpress

,

6

}

=

require

(

'graphql-upload'

)

;

7

const

{

finished

}

=

require

(

'stream/promises'

)

;

8

const

{

ApolloServerPluginLandingPageLocalDefault

}

=

require

(

'apollo-server-core'

)

;

9

10

const

typeDefs

=

gql

`

11

12

13

14

scalar

Upload

15

16

type

File

{

17

filename

:

String

!

18

mimetype

:

String

!

19

encoding

:

String

!

20

}

21

22

type

Query

{

23

24

25

26

otherFields

:

Boolean

!

27

}

28

29

type

Mutation

{

30

31

singleUpload

(

file

:

Upload

!

)

:

File

!

32

}

33

`

;

34

35

const

resolvers

=

{

36

37

38

Upload

:

GraphQLUpload

,

39

40

Mutation

:

{

41

singleUpload

:

async

(

parent

,

{

file

}

)

=>

{

42

const

{

createReadStream

,

filename

,

mimetype

,

encoding

}

=

await

file

;

43

44

45

46

const

stream

=

createReadStream

(

)

;

47

48

49

50

const

out

=

require

(

'fs'

)

.

createWriteStream

(

'local-file-output.txt'

)

;

51

stream

.

pipe

(

out

)

;

52

await

finished

(

out

)

;

53

54

return

{

filename

,

mimetype

,

encoding

}

;

55

}

,

56

}

,

57

}

;

58

59

async

function

startServer

(

)

{

60

const

server

=

new

ApolloServer

(

{

61

typeDefs

,

62

resolvers

,

63

64

csrfPrevention

:

true

,

65

cache

:

'bounded'

,

66

plugins

:

[

ApolloServerPluginLandingPageLocalDefault

(

{

embed

:

true

}

)

]

,

67

}

)

;

68

await

server

.

start

(

)

;

69

70

const

app

=

express

(

)

;

71

72

73

app

.

use

(

graphqlUploadExpress

(

)

)

;

74

75

server

.

applyMiddleware

(

{

app

}

)

;

76

77

await

new

Promise

(

(

r

)

=>

app

.

listen

(

{

port

:

4000

}

,

r

)

)

;

78

79

console

.

log

(

`

🚀 Server ready at http://localhost:4000

${

server

.

graphqlPath

}

`

)

;

80

}

81

82

startServer

(

)

;

1

const

{

ApolloServer

,

gql

}

=

require

(

'apollo-server-fastify'

)

;

2

const

{

3

ApolloServerPluginLandingPageLocalDefault

,

4

}

=

require

(

'apollo-server-core'

)

;

5

const

{

GraphQLUpload

,

processRequest

}

=

require

(

'graphql-upload'

)

;

6

const

{

finished

}

=

require

(

'stream/promises'

)

;

7

8

const

typeDefs

=

gql

`

9

10

11

12

scalar

Upload

13

14

type

File

{

15

filename

:

String

!

16

mimetype

:

String

!

17

encoding

:

String

!

18

}

19

20

type

Query

{

21

22

23

24

otherFields

:

Boolean

!

25

}

26

27

type

Mutation

{

28

29

singleUpload

(

file

:

Upload

!

)

:

File

!

30

}

31

`

;

32

33

const

resolvers

=

{

34

35

36

Upload

:

GraphQLUpload

,

37

38

Mutation

:

{

39

singleUpload

:

async

(

parent

,

{

file

}

)

=>

{

40

const

{

createReadStream

,

filename

,

mimetype

,

encoding

}

=

await

file

;

41

42

43

44

const

stream

=

createReadStream

(

)

;

45

46

47

48

const

out

=

require

(

'fs'

)

.

createWriteStream

(

'local-file-output.txt'

)

;

49

stream

.

pipe

(

out

)

;

50

await

finished

(

out

)

;

51

52

return

{

filename

,

mimetype

,

encoding

}

;

53

}

,

54

}

,

55

}

;

56

57

const

app

=

require

(

'fastify'

)

(

{

58

logger

:

true

59

}

)

;

60

61

const

start

=

async

(

)

=>

{

62

try

{

63

64

app

.

addContentTypeParser

(

'multipart'

,

(

request

,

payload

,

done

)

=>

{

65

request

.

isMultipart

=

true

;

66

done

(

)

;

67

}

)

;

68

69

70

app

.

addHook

(

'preValidation'

,

async

function

(

request

,

reply

)

{

71

if

(

!

request

.

isMultipart

)

{

72

return

;

73

}

74

75

request

.

body

=

await

processRequest

(

request

.

raw

,

reply

.

raw

)

;

76

}

)

;

77

78

const

server

=

new

ApolloServer

(

{

79

typeDefs

,

80

resolvers

,

81

82

csrfPrevention

:

true

,

83

cache

:

'bounded'

,

84

plugins

:

[

85

ApolloServerPluginLandingPageLocalDefault

(

{

embed

:

true

}

)

,

86

]

,

87

}

)

;

88

89

90

await

server

.

start

(

)

;

91

92

app

.

register

(

server

.

createHandler

(

)

)

;

93

await

app

.

listen

(

3000

)

;

94

}

catch

(

err

)

{

95

app

.

log

.

error

(

err

)

;

96

process

.

exit

(

1

)

;

97

}

98

}

;

99

100

start

(

)

;

Xổ số miền Bắc