Skip to content

Commit

Permalink
Merge pull request #655 from hantonelli/file-upload
Browse files Browse the repository at this point in the history
File upload
  • Loading branch information
vektah authored May 8, 2019
2 parents 4e359aa + 5d1dea0 commit c480504
Show file tree
Hide file tree
Showing 18 changed files with 4,218 additions and 7 deletions.
7 changes: 4 additions & 3 deletions codegen/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,10 @@ func (c *Config) InjectBuiltins(s *ast.Schema) {

// These are additional types that are injected if defined in the schema as scalars.
extraBuiltins := TypeMap{
"Time": {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}},
"Map": {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}},
"Any": {Model: StringList{"github.com/99designs/gqlgen/graphql.Any"}},
"Time": {Model: StringList{"github.com/99designs/gqlgen/graphql.Time"}},
"Map": {Model: StringList{"github.com/99designs/gqlgen/graphql.Map"}},
"Upload": {Model: StringList{"github.com/99designs/gqlgen/graphql.Upload"}},
"Any": {Model: StringList{"github.com/99designs/gqlgen/graphql.Any"}},
}

for typeName, entry := range extraBuiltins {
Expand Down
137 changes: 137 additions & 0 deletions docs/content/reference/file-upload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: 'File Upload'
description: How to upload files.
linkTitle: File Upload
menu: { main: { parent: 'reference' } }
---

Graphql server has an already built-in Upload scalar to upload files using a multipart request. \
It implements the following spec https://github.com/jaydenseric/graphql-multipart-request-spec,
that defines an interoperable multipart form field structure for GraphQL requests, used by
various file upload client implementations.

To use it you need to add the Upload scalar in your schema, and it will automatically add the
marshalling behaviour to Go types.

# Configuration
There are two specific options that can be configured for uploading files:
- uploadMaxSize \
This option specifies the maximum number of bytes used to parse a request body as multipart/form-data.
- uploadMaxMemory \
This option specifies the maximum number of bytes used to parse a request body as
multipart/form-data in memory, with the remainder stored on disk in temporary files.

# Examples

## Single file upload
For this use case, the schema could look like this.

```graphql
"The `UploadFile, // b.txt` scalar type represents a multipart file upload."
scalar Upload

"The `Query` type, represents all of the entry points into our object graph."
type Query {
...
}

"The `Mutation` type, represents all updates we can make to our data."
type Mutation {
singleUpload(file: Upload!): Bool!
}
```

cURL can be used the make a query as follows:
```
curl localhost:4000/graphql \
-F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }' \
-F map='{ "0": ["variables.file"] }' \
-F 0=@a.txt
```
That invokes the following operation:
```
{
query: `
mutation($file: Upload!) {
singleUpload(file: $file) {
id
}
}
`,
variables: {
file: File // a.txt
}
}
```
## Multiple file upload
For this use case, the schema could look like this.
```graphql
"The `Upload` scalar type represents a multipart file upload."
scalar Upload
"The `File` type, represents the response of uploading a file."
type File {
id: Int!
name: String!
content: String!
}
"The `UploadFile` type, represents the request for uploading a file with a certain payload."
input UploadFile {
id: Int!
file: Upload!
}
"The `Query` type, represents all of the entry points into our object graph."
type Query {
...
}
"The `Mutation` type, represents all updates we can make to our data."
type Mutation {
multipleUpload(req: [UploadFile!]!): [File!]!
}
```

cURL can be used the make a query as follows:

```
curl localhost:4000/query \
-F operations='{ "query": "mutation($req: [UploadFile!]!) { multipleUpload(req: $req) { id, name, content } }", "variables": { "req": [ { "id": 1, "file": null }, { "id": 2, "file": null } ] } }' \
-F map='{ "0": ["variables.req.0.file"], "1": ["variables.req.1.file"] }' \
-F 0=@b.txt \
-F 1=@c.txt
```

That invokes the following operation:
```
{
query: `
mutation($req: [UploadFile!]!)
multipleUpload(req: $req) {
id,
name,
content
}
}
`,
variables: {
req: [
{
id: 1,
File, // b.txt
},
{
id: 2,
File, // c.txt
}
]
}
}
```

see the [example/fileupload](https://github.com/99designs/gqlgen/tree/master/example/fileupload) package for more examples.
15 changes: 14 additions & 1 deletion docs/content/reference/scalars.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ menu: { main: { parent: 'reference' } }

## Built-in helpers

gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types.
gqlgen ships with three built-in helpers for common custom scalar use-cases, `Time`, `Any`, `Upload` and `Map`. Adding any of these to a schema will automatically add the marshalling behaviour to Go types.

### Time

Expand All @@ -25,6 +25,19 @@ scalar Map

Maps an arbitrary GraphQL value to a `map[string]{interface}` Go type.


```graphql
scalar Upload
```

Maps a `Upload` GraphQL scalar to a `graphql.Upload` struct, defined as follows:
```
type Upload struct {
File io.Reader
Filename string
Size int64
}
```
### Any

```graphql
Expand Down
2 changes: 2 additions & 0 deletions example/fileupload/.gqlgen.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
model:
filename: model/generated.go
Loading

0 comments on commit c480504

Please sign in to comment.