Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork57
jaydenseric/graphql-multipart-request-spec
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
An interoperablemultipart form field structure for GraphQL requests, used by various file upload client/server implementations.
It’s possible to implement:
- Nesting files anywhere within operations (usually in
variables). - Operation batching.
- File deduplication.
- File upload streams in resolvers.
- Aborting file uploads in resolvers.
An “operations object” is anApollo GraphQL POST request (or array of requests if batching). An “operations path” is anobject-path string to locate a file within an operations object.
So operations can be resolved while the files are still uploading, the fields are ordered:
operations: A JSON encoded operations object with files replaced withnull.map: A JSON encoded map of where files occurred in the operations. For each file, the key is the file multipart form field name and the value is an array of operations paths.- File fields: Each file extracted from the operations object with a unique, arbitrary field name.
{query:` mutation($file: Upload!) { singleUpload(file: $file) { id } } `,variables:{file:File// a.txt}}
curl localhost:3001/graphql \ -F operations='{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }' \ -F map='{ "0": ["variables.file"] }' \ -F 0=@a.txt
--------------------------cec8e8123c05ba25Content-Disposition: form-data; name="operations"{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }--------------------------cec8e8123c05ba25Content-Disposition: form-data; name="map"{ "0": ["variables.file"] }--------------------------cec8e8123c05ba25Content-Disposition: form-data; name="0"; filename="a.txt"Content-Type: text/plainAlpha file content.--------------------------cec8e8123c05ba25--{query:` mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } } `,variables:{files:[File,// b.txtFile// c.txt]}}
curl localhost:3001/graphql \ -F operations='{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } }", "variables": { "files": [null, null] } }' \ -F map='{ "0": ["variables.files.0"], "1": ["variables.files.1"] }' \ -F 0=@b.txt \ -F 1=@c.txt
--------------------------ec62457de6331cadContent-Disposition: form-data; name="operations"{ "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } }", "variables": { "files": [null, null] } }--------------------------ec62457de6331cadContent-Disposition: form-data; name="map"{ "0": ["variables.files.0"], "1": ["variables.files.1"] }--------------------------ec62457de6331cadContent-Disposition: form-data; name="0"; filename="b.txt"Content-Type: text/plainBravo file content.--------------------------ec62457de6331cadContent-Disposition: form-data; name="1"; filename="c.txt"Content-Type: text/plainCharlie file content.--------------------------ec62457de6331cad--[{query:` mutation($file: Upload!) { singleUpload(file: $file) { id } } `,variables:{file:File,// a.txt},},{query:` mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } } `,variables:{files:[File,// b.txtFile,// c.txt],},},];
curl localhost:3001/graphql \ -F operations='[{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }, { "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } }", "variables": { "files": [null, null] } }]' \ -F map='{ "0": ["0.variables.file"], "1": ["1.variables.files.0"], "2": ["1.variables.files.1"] }' \ -F 0=@a.txt \ -F 1=@b.txt \ -F 2=@c.txt
--------------------------627436eaefdbc285Content-Disposition: form-data; name="operations"[{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } }, { "query": "mutation($files: [Upload!]!) { multipleUpload(files: $files) { id } }", "variables": { "files": [null, null] } }]--------------------------627436eaefdbc285Content-Disposition: form-data; name="map"{ "0": ["0.variables.file"], "1": ["1.variables.files.0"], "2": ["1.variables.files.1"] }--------------------------627436eaefdbc285Content-Disposition: form-data; name="0"; filename="a.txt"Content-Type: text/plainAlpha file content.--------------------------627436eaefdbc285Content-Disposition: form-data; name="1"; filename="b.txt"Content-Type: text/plainBravo file content.--------------------------627436eaefdbc285Content-Disposition: form-data; name="2"; filename="c.txt"Content-Type: text/plainCharlie file content.--------------------------627436eaefdbc285--GraphQL server authentication and security mechanisms are beyond the scope of this specification, which only covers a multipart form field structure for GraphQL requests.
Note that a GraphQL multipart request has theContent-Typemultipart/form-data; if a browser making such a request determines it meets the criteria for a “simple request” as defined in theFetch specification for theCross-Origin Resource Sharing (CORS) protocol, it won’t cause aCORS preflight request. GraphQL server authentication and security mechanisms must consider this to preventCross-Site Request Forgery (CSRF) attacks.
Pull requests adding either experimental or mature implementations to these lists are welcome!Strikethrough means the project was renamed, deprecated, or no longer supports this spec out of the box (but might via an optional integration).
- jaydenseric/graphql-react (JS:npm)
- jaydenseric/apollo-upload-client (JS:npm)
- jaydenseric/extract-files (JS:npm)
- nearform/graphql-hooks (JS:npm)
- klis87/redux-saga-requests-graphql (JS:npm)
- imolorhe/altair (JS:npm)
- haffdata/buoy (JS:npm)
- FormidableLabs/urql (JS:npm)
apollo-fetch-upload(JS:npm)- GnRlLeclerc/go-graphql-client (Go:GitHub)
- apollographql/apollo-ios (Swift:CocoaPods)
- apollographql/apollo-android (Java:Bintray)
- zino-app/graphql-flutter (Dart:Pub)
- samirelanduk/kirjava (Python:PyPi)
- DoctorJohn/aiogqlc (Python:PyPi)
- graphql-python/gql (Python:PyPi)
- jaydenseric/graphql-upload (JS:npm)
- koresar/graphql-upload-minimal (JS:npm)
- dotansimha/graphql-yoga (JS:npm)
apollographql/apollo-server(JS:npm)jaydenseric/apollo-upload-server(JS:npm)- 99designs/gqlgen (Go:GitHub)
- jpascal/graphql-upload (Go:GitHub)
- jetruby/apollo_upload_server-ruby (Ruby:Gem)
- Ecodev/graphql-upload (PHP:Composer)
- rebing/graphql-laravel (PHP:Composer)
- nuwave/lighthouse (PHP:Composer)
- overblog/graphql-bundle (PHP:Composer)
- infinityloop-dev/graphpinator (PHP:Composer)
- lmcgartland/graphene-file-upload (Python:PyPi)
- strawberry-graphql/strawberry (Python:PyPi)
- graphql-java-kickstart/graphql-java-servlet (Java:Maven)
- ChilliCream/hotchocolate (C#:NuGet)
- async-graphql/async-graphql (Rust:Crates)
About
A spec for GraphQL multipart form requests (file uploads).
Topics
Resources
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Sponsor this project
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.