From a93a9ea49858346e52c3677b1633f101048c406d Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 9 Feb 2024 11:56:10 -0700 Subject: [PATCH 01/18] Add first draft --- docs/source/config.json | 9 +- .../executing-operations/file-uploads.mdx | 283 ++++++++++++++++++ 2 files changed, 291 insertions(+), 1 deletion(-) create mode 100644 docs/source/executing-operations/file-uploads.mdx diff --git a/docs/source/config.json b/docs/source/config.json index 1498552029..2d5721d4ff 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -81,6 +81,13 @@ "experimental" ] ], + "File uploads": [ + "/executing-operations/file-uploads", + [ + "enterprise", + "preview" + ] + ], "GraphQL Subscriptions": { "Subscriptions setup": [ "/executing-operations/subscription-support", @@ -101,7 +108,7 @@ "enterprise" ] ] - } + } }, "Telemetry and Monitoring": { "Overview": "/configuration/telemetry/overview", diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx new file mode 100644 index 0000000000..f1984c9e0f --- /dev/null +++ b/docs/source/executing-operations/file-uploads.mdx @@ -0,0 +1,283 @@ +--- +title: File uploads +description: Receive file uploads with the Apollo Router +minVersion: X.x +--- + + + + + +Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec). + +## About file uploads using multipart requests + +The [GraphQL Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) enables you to send files using arguments in a GraphQL mutation. + +{/* To-do: specifics + examples */} + +## File upload configuration and usage + +To enable file uploads in clients, you must both [configure support in the Apollo Router](#configure-file-upload-support-in-the-router) and [ensure client usage conforms to requirements](#client-usage-requirements). + +### Configure file upload support in the router + +By default, receiving client file uploads is _not_ enabled in the Apollo Router. +To enable file upload support, set the following fields in your `router.yaml` configuration file: + +```yaml title="router.yaml" showLineNumbers="false" +preview_file_uploads: + enabled: true + protocols: + multipart: + enabled: true + mode: streaming + limits: + max_file_size: 5mb # default 1mb + max_files: 2 # default 4 +``` + +#### Mode + +The only supported `mode` is `streaming`. This mode means that the router doesn't retain uploaded files in memory during a request. +If a request cannot be fulfilled in a streaming fashion, the router returns the [`UPLOADS_OPERATION_CANNOT_STREAM`](#uploads_operation_cannot_stream) error. + +#### Limits + +Configure the maximum file size and number of files to accept to prevent denial-of-service attacks. If requests exceed limits, the router rejects them. + +#### Configuration reference + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Attribute/
Default value
DescriptionValid Values
+ +##### `enabled` + +Default: `false` + +Flag to enable reception of client file uploadsboolean
+ +##### `protocols.multipart.enabled` +Default: `false` + +Flag to enable reception of multipart file uploadsboolean
+ +##### `protocols.multipart.mode` +Default: None + +Supported file upload mode + +`streaming` +(required value) + +
+ +##### `protocols.multipart.limits.max_file_size` +Default: `1MB` + +The maximum file size to acceptinteger of MB
+ +##### `protocols.multipart.limits.max_files` +Default: `4` + +The maximum number of files to acceptinteger
+ + +### Client usage requirements + +When calling a mutation with file uploads, the client must send the following HTTP parts in the following order: + +1. The raw GraphQL query +1. A map of file(s) to variable name(s) +1. The files to be uploaded, one HTTP request part per file + + + +The order of the files sent must match the order they're declared in the map sent in the second part of the request. + + + +The following is an example of such a multipart HTTP request: + +```http disableCopy showLineNumbers=false +--------------------------gc0p4Jq0M2Yt08jU534c0p +Content-Disposition: form-data; name="operations" + +{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } } +--------------------------gc0p4Jq0M2Yt08jU534c0p +Content-Disposition: form-data; name="map" + +{ "file1": ["variables.file"], "file2": ["variables.file"] } +--------------------------gc0p4Jq0M2Yt08jU534c0p +Content-Disposition: form-data; name="file1"; filename="a.txt" +Content-Type: text/plain + +Alpha file content. +--------------------------gc0p4Jq0M2Yt08jU534c0p +Content-Disposition: form-data; name="file2"; filename="b.txt" +Content-Type: text/plain + +Beta file content. + +--------------------------gc0p4Jq0M2Yt08jU534c0p-- +``` + + + +`gc0p4Jq0M2Yt08jU534c0p` is an example of the boundary parameter required by [multipart requests](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). + + + +Refer to the docs for your client library for further instructions. + +- [Apollo Client (web)](/react/data/file-uploads/) +- [Apollo iOS](/ios/file-uploads/) +- [Apollo Kotlin](/kotlin/advanced/upload/) + +Custom clients can be implemented following the [spec documentation](https://github.com/jaydenseric/graphql-multipart-request-spec). + +## Security + +Without additional security, HTTP multipart requests can be exploited as part of [cross-site request forgery](https://owasp.org/www-community/attacks/csrf) (CSRF) attacks. + +The Apollo Router already has a mechanism to prevent these type of attacks, which is enabled by default. You should verify that your router has not disabled this mechanism before using file uploads. See [Cross-Site Request Forgery Prevention](../configuration/csrf) for details. + +## Metrics for file uploads + +Metrics in the Apollo Router for file uploads: + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDescription
+ +##### `apollo.router.operations.file_uploads` + + + +Counter for the number of file uploads. + +
+ +##### `apollo.router.operations.file_uploads.file_size` + + + +Histogram for the size of uploaded files. + +
+ +##### `apollo.router.operations.file_uploads.files` + + + +Histogram for the number of uploaded files. + +
+ +## Error codes for file uploads + +A file upload request may receive the following error responses: + + + + + + + + + + + + + + + + + + + + + + +
Error CodeDescription
+ +##### `UPLOADS_LIMITS_MAX_FILES_EXCEEDED` + +The number of files in the payload exceeded the configured limit.
+ +##### `UPLOADS_LIMITS_MAX_FILE_SIZE_EXCEEDED` + +A file exceeded the maximum configured file size.
+ +##### `UPLOADS_FILE_MISSING` + +The operation specified a file that was missing from the request.
+ +##### `UPLOADS_OPERATION_CANNOT_STREAM` + +The query was invalid as it cannot be streamed to the client.
+ + +## Known limitations + +### Unsupported query modes + +When file uploads are enabled in the router, any operations using the [`@defer`](/graphos/operations/defer/) are unsupported. \ No newline at end of file From b13c969d0749b3b32da258289201512bdf011673 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 9 Feb 2024 12:07:03 -0700 Subject: [PATCH 02/18] Update initial example for a single upload --- docs/source/config.json | 2 +- docs/source/executing-operations/file-uploads.mdx | 13 ++++--------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/docs/source/config.json b/docs/source/config.json index 2d5721d4ff..1aea7e4ed8 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -108,7 +108,7 @@ "enterprise" ] ] - } + } }, "Telemetry and Monitoring": { "Overview": "/configuration/telemetry/overview", diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index f1984c9e0f..f4430bd7fe 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -122,7 +122,7 @@ Default: `4` When calling a mutation with file uploads, the client must send the following HTTP parts in the following order: -1. The raw GraphQL query +1. The raw GraphQL operation 1. A map of file(s) to variable name(s) 1. The files to be uploaded, one HTTP request part per file @@ -142,24 +142,19 @@ Content-Disposition: form-data; name="operations" --------------------------gc0p4Jq0M2Yt08jU534c0p Content-Disposition: form-data; name="map" -{ "file1": ["variables.file"], "file2": ["variables.file"] } +{ "file1": ["variables.file"] } --------------------------gc0p4Jq0M2Yt08jU534c0p Content-Disposition: form-data; name="file1"; filename="a.txt" Content-Type: text/plain Alpha file content. ---------------------------gc0p4Jq0M2Yt08jU534c0p -Content-Disposition: form-data; name="file2"; filename="b.txt" -Content-Type: text/plain - -Beta file content. - --------------------------gc0p4Jq0M2Yt08jU534c0p-- ``` -`gc0p4Jq0M2Yt08jU534c0p` is an example of the boundary parameter required by [multipart requests](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). +- `gc0p4Jq0M2Yt08jU534c0p` is an example of the boundary parameter required by [multipart requests](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). +- `variables.file` in the raw GraphQL operation must be set to `null` according to the [specification](https://github.com/jaydenseric/graphql-multipart-request-spec). From 29bb6b017495da5de76fa701a0d7522e0203abd5 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 12 Feb 2024 16:58:12 -0700 Subject: [PATCH 03/18] Add examples --- .../executing-operations/file-uploads.mdx | 131 ++++++++++++++---- 1 file changed, 104 insertions(+), 27 deletions(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index f4430bd7fe..78a54c39cf 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -1,6 +1,7 @@ --- title: File uploads -description: Receive file uploads with the Apollo Router +subtitle: Receive file uploads with the Apollo Router +description: '' minVersion: X.x --- @@ -12,9 +13,60 @@ Learn how to configure the Apollo Router to receive file uploads in client reque ## About file uploads using multipart requests -The [GraphQL Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) enables you to send files using arguments in a GraphQL mutation. +A [multipart HTTP request](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html) lets you include various data formats—such as text, file data, and JSON objects—in a single HTTP request. The [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec) uses multipart requests to upload files using arguments in a GraphQL mutation. -{/* To-do: specifics + examples */} +### Example usage + +Imagine you're building a platform where users can create posts with an image and title. +Your subgraph schema may include something like this: + +```graphql showLineNumbers=false disableCopy +type Post { + id: ID! + title: String! + image: Upload! +} + +type Mutation { + createPost(title: String!, image: Upload!): Post! +} +``` + + + +Some GraphQL server implementations provide built-in support for handling file uploads, including an `Upload` scalar type. +For others, including the latest version of [Apollo Server](/apollo-server/), you must use external packages, such as [`graphql-upload`](https://www.npmjs.com/package/graphql-upload). +Refer to your subgraph library or package documentation for further information, including writing resolvers for uploaded files. + + + +When a client calls the `createPost` mutation, it can use variables to include the actual image file to upload: + +```graphql showLineNumbers=false disableCopy +{ + query: ` + mutation CreatePost($title: String!, $image: Upload!) { + createPost(title: $title, image: $image) { + id + title + image + } + } + `, + variables: { + title: "My first post", + image: File // image.png + } +} +``` + +A request using the GraphQL multipart request spec would include the following as separate parts in a multipart request: + +- the above operation definition +- the image file to upload +- a map between variables and files to upload + +The exact requirements are documented in [Client usage requirements](#client-usage-requirements). ## File upload configuration and usage @@ -22,7 +74,7 @@ To enable file uploads in clients, you must both [configure support in the Apoll ### Configure file upload support in the router -By default, receiving client file uploads is _not_ enabled in the Apollo Router. +By default, receiving client file uploads isn't enabled in the Apollo Router. To enable file upload support, set the following fields in your `router.yaml` configuration file: ```yaml title="router.yaml" showLineNumbers="false" @@ -39,12 +91,29 @@ preview_file_uploads: #### Mode -The only supported `mode` is `streaming`. This mode means that the router doesn't retain uploaded files in memory during a request. +The only supported `mode` is `streaming`. +That means the router doesn't retain uploaded files in memory during a request. +Streaming file uploads can be more memory-efficient, especially for large files, since it avoids loading the entire file into memory. + +To ensure your operation is streamable, avoid nesting in file uploads. +For example, the following operation would not be streamable: + +```graphql title="❌" disableCopy=true showLineNumbers=false +mutation UploadNestedFiles($files: [Upload!]!) { + uploadNestedFiles(files: $files) { + success + } +} + +``` + If a request cannot be fulfilled in a streaming fashion, the router returns the [`UPLOADS_OPERATION_CANNOT_STREAM`](#uploads_operation_cannot_stream) error. #### Limits -Configure the maximum file size and number of files to accept to prevent denial-of-service attacks. If requests exceed limits, the router rejects them. +The router includes limits to prevent denial-of-service attacks. +You can configure both the maximum file size and number of files to accept. +If a request exceeds limits, the router rejects them. #### Configuration reference @@ -98,11 +167,15 @@ Default: None ##### `protocols.multipart.limits.max_file_size` -Default: `1MB` +Default: `1mb` The maximum file size to accept -integer of MB + + +Accepts values in a human-readable format; for example, `5kb` and `99mb` are acceptable values. + + @@ -126,37 +199,41 @@ When calling a mutation with file uploads, the client must send the following HT 1. A map of file(s) to variable name(s) 1. The files to be uploaded, one HTTP request part per file - - -The order of the files sent must match the order they're declared in the map sent in the second part of the request. +#### Example request payload - +The following is an example of a multipart HTTP request payload that builds off the [example scenario](#example-usage): -The following is an example of such a multipart HTTP request: - -```http disableCopy showLineNumbers=false +```http disableCopy showLineNumbers=false title="Request payload" --------------------------gc0p4Jq0M2Yt08jU534c0p Content-Disposition: form-data; name="operations" -{ "query": "mutation ($file: Upload!) { singleUpload(file: $file) { id } }", "variables": { "file": null } } +{ "query": "mutation CreatePost($title: String!, $image: Upload!) { createPost(title: $title, image: $image) { id } }", "variables": { "title": "My first post", "image": null } } --------------------------gc0p4Jq0M2Yt08jU534c0p Content-Disposition: form-data; name="map" -{ "file1": ["variables.file"] } +{ "0": ["variables.image"] } --------------------------gc0p4Jq0M2Yt08jU534c0p -Content-Disposition: form-data; name="file1"; filename="a.txt" -Content-Type: text/plain +Content-Disposition: form-data; name="0"; filename="image.png" +Content-Type: image/png -Alpha file content. +[Binary image content here] --------------------------gc0p4Jq0M2Yt08jU534c0p-- ``` - +See below for an explanation of each part of the request payload: -- `gc0p4Jq0M2Yt08jU534c0p` is an example of the boundary parameter required by [multipart requests](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). -- `variables.file` in the raw GraphQL operation must be set to `null` according to the [specification](https://github.com/jaydenseric/graphql-multipart-request-spec). +- **`Content-Disposition: form-data; name="operations"`** + - The first part of the request must include the operation definition. This example specifies a mutation named `CreatePost` that accepts variables for a `title` and `image`. + - The `variables` object includes the title for the post and sets the `image` variable to `null` as the [multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec) requires for any variables that represent files to be uploaded. +- **`Content-Disposition: form-data; name="map"`** + - The second part of the request must include the mapping between the files to upload and the variables in the GraphQL operation. + - In this case, it maps the file in the request part with `name="0"` to `variables.image`. The map can use any key names you like—for example, `file1` instead of `0`—as long as the keys match the `name`s of the following request parts. +- **`Content-Disposition: form-data; name="0"; filename="image.png"`** + - The following part(s) contain the actual file(s) to be uploaded, with one file per part. The order of the files must match the order they're declared in the map in the second part of the request. + - In this case, there is only one file to upload, which has the name `image.png` and the appropriate content type (`image/png`) + - These parts also include actual file content—in this case, an image binary. - +Each part of the request payload is separated by a boundary string (`gc0p4Jq0M2Yt08jU534c0p`) per the [multipart request format](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html). Refer to the docs for your client library for further instructions. @@ -170,7 +247,7 @@ Custom clients can be implemented following the [spec documentation](https://git Without additional security, HTTP multipart requests can be exploited as part of [cross-site request forgery](https://owasp.org/www-community/attacks/csrf) (CSRF) attacks. -The Apollo Router already has a mechanism to prevent these type of attacks, which is enabled by default. You should verify that your router has not disabled this mechanism before using file uploads. See [Cross-Site Request Forgery Prevention](../configuration/csrf) for details. +The Apollo Router already has a mechanism to prevent these types of attacks, which is enabled by default. You should verify that your router hasn't disabled this mechanism before using file uploads. See [Cross-Site Request Forgery Prevention](../configuration/csrf) for details. ## Metrics for file uploads @@ -242,7 +319,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_LIMITS_MAX_FILES_EXCEEDED` -The number of files in the payload exceeded the configured limit. +The number of files in the request exceeded the configured limit. @@ -266,7 +343,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_OPERATION_CANNOT_STREAM` -The query was invalid as it cannot be streamed to the client. +The request was invalid as it couldn't be streamed to the client. From 504bc8902f7c1445ca71d9343a9b9cd6ca56c8c1 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 12 Feb 2024 17:16:45 -0700 Subject: [PATCH 04/18] Add meta description --- docs/source/executing-operations/file-uploads.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 78a54c39cf..d240dcb1c1 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -1,7 +1,7 @@ --- title: File uploads subtitle: Receive file uploads with the Apollo Router -description: '' +description: Configure the Apollo Router to receive file uploads using the GraphQL multipart request spec. minVersion: X.x --- From b183d9cf1f0a241c173b510784ad0f5f20ed99d2 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 12 Feb 2024 17:21:51 -0700 Subject: [PATCH 05/18] Add to list of enterprise features --- docs/source/enterprise-features.mdx | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/enterprise-features.mdx b/docs/source/enterprise-features.mdx index a29e199b75..8b48911861 100644 --- a/docs/source/enterprise-features.mdx +++ b/docs/source/enterprise-features.mdx @@ -14,6 +14,7 @@ Try out these Enterprise features for free with an [Enterprise trial](/graphos/o ## List of features - **Real-time updates** via [GraphQL subscriptions](./executing-operations/subscription-support/) +- Support for [**file uploads** via multipart requests](./executing-operations/file-uploads) - **Authentication of inbound requests** via [JSON Web Token (JWT)](./configuration/authn-jwt/) - [**Authorization** of specific fields and types](./configuration/authorization) through the [`@requiresScopes`](./configuration/authorization#requiresscopes), [`@authenticated`](./configuration/authorization#authenticated), and [`@policy`](./configuration/authorization#policy) directives - Redis-backed [**distributed caching** of query plans and persisted queries](./configuration/distributed-caching/) and [**subgraph entity caching**](./configuration/entity-caching/) From 067c2f65bfb8200413ece80b11a332955b0476c0 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 12 Feb 2024 17:34:13 -0700 Subject: [PATCH 06/18] Standardize caps --- docs/source/executing-operations/file-uploads.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index d240dcb1c1..91d44a07cd 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -9,7 +9,7 @@ minVersion: X.x -Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL Multipart Request Spec](https://github.com/jaydenseric/graphql-multipart-request-spec). +Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec). ## About file uploads using multipart requests From 4948ea0ad778067db3033067101a1a8e19d74557 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 13 Feb 2024 08:35:25 -0700 Subject: [PATCH 07/18] Copyedit --- docs/source/executing-operations/file-uploads.mdx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 91d44a07cd..922109ced9 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -111,7 +111,7 @@ If a request cannot be fulfilled in a streaming fashion, the router returns the #### Limits -The router includes limits to prevent denial-of-service attacks. +The router includes default limits for file uploads to prevent denial-of-service attacks. You can configure both the maximum file size and number of files to accept. If a request exceeds limits, the router rejects them. @@ -319,7 +319,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_LIMITS_MAX_FILES_EXCEEDED` -The number of files in the request exceeded the configured limit. +The number of files in the request exceeded the configured limit @@ -335,7 +335,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_FILE_MISSING` -The operation specified a file that was missing from the request. +The operation specified a file that was missing from the request @@ -343,7 +343,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_OPERATION_CANNOT_STREAM` -The request was invalid as it couldn't be streamed to the client. +The request was invalid as it couldn't be streamed to the client From e4563b1119b96941e0cdf472be519ac205d4c2ac Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Thu, 15 Feb 2024 15:40:28 -0700 Subject: [PATCH 08/18] Apply suggestions from code review Co-authored-by: Edward Huang --- .../executing-operations/file-uploads.mdx | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 922109ced9..e4ed8faca1 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -1,6 +1,6 @@ --- title: File uploads -subtitle: Receive file uploads with the Apollo Router +subtitle: Receive files uploaded by clients with the Apollo Router description: Configure the Apollo Router to receive file uploads using the GraphQL multipart request spec. minVersion: X.x --- @@ -9,15 +9,15 @@ minVersion: X.x -Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec). +Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec). ## About file uploads using multipart requests -A [multipart HTTP request](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html) lets you include various data formats—such as text, file data, and JSON objects—in a single HTTP request. The [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec) uses multipart requests to upload files using arguments in a GraphQL mutation. +A [multipart HTTP request](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html) lets you efficiently send multiple files of various data formats—such as text, binary, and JSON objects—in a single HTTP request. The [GraphQL multipart request spec](https://github.com/jaydenseric/graphql-multipart-request-spec) uses multipart requests to upload files using arguments in a GraphQL mutation. ### Example usage -Imagine you're building a platform where users can create posts with an image and title. +Imagine you're building a platform where users can create posts with a title and an image file. Your subgraph schema may include something like this: ```graphql showLineNumbers=false disableCopy @@ -70,7 +70,7 @@ The exact requirements are documented in [Client usage requirements](#client-usa ## File upload configuration and usage -To enable file uploads in clients, you must both [configure support in the Apollo Router](#configure-file-upload-support-in-the-router) and [ensure client usage conforms to requirements](#client-usage-requirements). +To enable file uploads from clients, you must both [configure support in the Apollo Router](#configure-file-upload-support-in-the-router) and [ensure client usage conforms to requirements](#client-usage-requirements). ### Configure file upload support in the router @@ -113,7 +113,7 @@ If a request cannot be fulfilled in a streaming fashion, the router returns the The router includes default limits for file uploads to prevent denial-of-service attacks. You can configure both the maximum file size and number of files to accept. -If a request exceeds limits, the router rejects them. +If a request exceeds a limit, the router rejects the request. #### Configuration reference @@ -170,7 +170,9 @@ Default: None Default: `1mb` -The maximum file size to accept +The maximum file size to accept. +If this limit is exceeded, the router rejects the entire request. + Accepts values in a human-readable format; for example, `5kb` and `99mb` are acceptable values. @@ -184,7 +186,9 @@ Accepts values in a human-readable format; for example, `5kb` and `99mb` are acc Default: `4` -The maximum number of files to accept +The maximum number of files to accept. +If this limit is exceeded, the router rejects the entire request. + integer @@ -327,7 +331,7 @@ A file upload request may receive the following error responses: ##### `UPLOADS_LIMITS_MAX_FILE_SIZE_EXCEEDED` -A file exceeded the maximum configured file size. +A file exceeded the maximum configured file size From ec12fa3c56d2d4d5bf573003828518c78ec05e44 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 16 Feb 2024 14:29:06 -0700 Subject: [PATCH 09/18] Remove from enterprise list (managed in monodocs now) --- docs/source/enterprise-features.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/docs/source/enterprise-features.mdx b/docs/source/enterprise-features.mdx index 8b48911861..a29e199b75 100644 --- a/docs/source/enterprise-features.mdx +++ b/docs/source/enterprise-features.mdx @@ -14,7 +14,6 @@ Try out these Enterprise features for free with an [Enterprise trial](/graphos/o ## List of features - **Real-time updates** via [GraphQL subscriptions](./executing-operations/subscription-support/) -- Support for [**file uploads** via multipart requests](./executing-operations/file-uploads) - **Authentication of inbound requests** via [JSON Web Token (JWT)](./configuration/authn-jwt/) - [**Authorization** of specific fields and types](./configuration/authorization) through the [`@requiresScopes`](./configuration/authorization#requiresscopes), [`@authenticated`](./configuration/authorization#authenticated), and [`@policy`](./configuration/authorization#policy) directives - Redis-backed [**distributed caching** of query plans and persisted queries](./configuration/distributed-caching/) and [**subgraph entity caching**](./configuration/entity-caching/) From b2f8dddae8b2631b6b17ab9f66c3f65ef0ef7bf1 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 16 Feb 2024 14:46:45 -0700 Subject: [PATCH 10/18] Update configuration information --- .../executing-operations/file-uploads.mdx | 76 +++++++++++++------ 1 file changed, 54 insertions(+), 22 deletions(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index e4ed8faca1..08346d7f61 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -95,16 +95,24 @@ The only supported `mode` is `streaming`. That means the router doesn't retain uploaded files in memory during a request. Streaming file uploads can be more memory-efficient, especially for large files, since it avoids loading the entire file into memory. -To ensure your operation is streamable, avoid nesting in file uploads. -For example, the following operation would not be streamable: +To ensure your operation is streamable, avoid nesting file uploads. +For example, the following `nestedUpload` operation attempting to upload `$file3` would not be streamable: ```graphql title="❌" disableCopy=true showLineNumbers=false -mutation UploadNestedFiles($files: [Upload!]!) { - uploadNestedFiles(files: $files) { - success +mutation uploadFiles($file1: Upload, $file3: Upload, $file3: Upload ) { + upload(data: $file1) { + __typename + } + upload(data: $file2) { + __typename + } + + nestedUpload { + upload(data: $file3) { + __typename + } } } - ``` If a request cannot be fulfilled in a streaming fashion, the router returns the [`UPLOADS_OPERATION_CANNOT_STREAM`](#uploads_operation_cannot_stream) error. @@ -117,11 +125,13 @@ If a request exceeds a limit, the router rejects the request. #### Configuration reference +The following are attributes of the root [`preview_file_uploads`](#configure-file-upload-support-in-the-router) configuration. + - - + + @@ -131,10 +141,15 @@ If a request exceeds a limit, the router rejects the request. ##### `enabled` -Default: `false` +Flag to enable reception of client file uploads + + + + - @@ -142,24 +157,33 @@ Default: `false` + - + - @@ -167,15 +191,19 @@ Default: None - + @@ -183,11 +211,15 @@ Accepts values in a human-readable format; for example, `5kb` and `99mb` are acc - + From 74bf5be04560efa20ae527d1cc5df90395074527 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Tue, 20 Feb 2024 13:32:02 -0700 Subject: [PATCH 11/18] Callout private preview limitations --- docs/source/executing-operations/file-uploads.mdx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 08346d7f61..beb9ac0322 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -5,7 +5,11 @@ description: Configure the Apollo Router to receive file uploads using the Graph minVersion: X.x --- - + + +This feature is currently in invite-only [preview](/resources/product-launch-stages/#product-launch-stages). Don't hesitate to get in touch if you'd like to request access or have any questions or feedback. + + @@ -386,6 +390,8 @@ A file upload request may receive the following error responses: ## Known limitations +While in private preview, Apollo recommends using file uploads only in development and testing environments, not in production. + ### Unsupported query modes When file uploads are enabled in the router, any operations using the [`@defer`](/graphos/operations/defer/) are unsupported. \ No newline at end of file From 246c7dd286ee99dc46c87dfc8efa7dffe18dc96d Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 23 Feb 2024 10:16:08 -0700 Subject: [PATCH 12/18] Add minVersion --- docs/source/executing-operations/file-uploads.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index beb9ac0322..55da78672c 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -2,7 +2,7 @@ title: File uploads subtitle: Receive files uploaded by clients with the Apollo Router description: Configure the Apollo Router to receive file uploads using the GraphQL multipart request spec. -minVersion: X.x +minVersion: 1.41.0 --- From c46a350f97c6a2da53ea6212e591f4d14b3708eb Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Fri, 8 Mar 2024 09:01:31 -0700 Subject: [PATCH 13/18] Bump minVersion --- docs/source/executing-operations/file-uploads.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 55da78672c..05cf715760 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -2,7 +2,7 @@ title: File uploads subtitle: Receive files uploaded by clients with the Apollo Router description: Configure the Apollo Router to receive file uploads using the GraphQL multipart request spec. -minVersion: 1.41.0 +minVersion: 1.41.1 --- From e6fbced38160be63a4cdff2b178cc2edc434f36d Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 8 Apr 2024 10:34:55 -0700 Subject: [PATCH 14/18] Update unsupported query modes --- docs/source/executing-operations/file-uploads.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 05cf715760..138697cdff 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -394,4 +394,4 @@ While in private preview, Apollo recommends using file uploads only in developme ### Unsupported query modes -When file uploads are enabled in the router, any operations using the [`@defer`](/graphos/operations/defer/) are unsupported. \ No newline at end of file +The router rejects operations that use file upload variables on or inside fields using [`@defer`](/graphos/operations/defer/). \ No newline at end of file From 3a38b1292fcb826e10db7c2771a7d967e1ca933d Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 8 Apr 2024 17:52:56 -0700 Subject: [PATCH 15/18] Add unsupported and supported usage examples --- .../executing-operations/file-uploads.mdx | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 138697cdff..76d967be2b 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -394,4 +394,28 @@ While in private preview, Apollo recommends using file uploads only in developme ### Unsupported query modes -The router rejects operations that use file upload variables on or inside fields using [`@defer`](/graphos/operations/defer/). \ No newline at end of file +The router rejects operations that use file upload variables on or inside fields using [`@defer`](/graphos/operations/defer/). + + + +```graphql title="❌ Unsupported usage" disableCopy=true showLineNumbers=false +query ($file: Upload) { + someField { + ... @defer { + anotherField(file: $file) + } + } +} +``` + +```graphql title="✅ Supported usage" disableCopy=true showLineNumbers=false +query ($file: Upload) { + someField(file: $file) { + ... @defer { + anotherField + } + } +} +``` + + \ No newline at end of file From dea67d4dc4236568b0fa84344fbcaaefaa13a68e Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 13 May 2024 09:43:49 -0600 Subject: [PATCH 16/18] Remove File uploads from nav --- docs/source/config.json | 7 ------- 1 file changed, 7 deletions(-) diff --git a/docs/source/config.json b/docs/source/config.json index d091005e41..468b9fe5e0 100644 --- a/docs/source/config.json +++ b/docs/source/config.json @@ -82,13 +82,6 @@ "enterprise" ] ], - "File uploads": [ - "/executing-operations/file-uploads", - [ - "enterprise", - "preview" - ] - ], "GraphQL Subscriptions": { "Subscriptions setup": [ "/executing-operations/subscription-support", From 31a37687f0a9ec005051f4457363fd50240c8f0f Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 13 May 2024 09:47:18 -0600 Subject: [PATCH 17/18] Update frontmatter --- docs/source/executing-operations/file-uploads.mdx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 76d967be2b..7a2867a509 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -3,11 +3,12 @@ title: File uploads subtitle: Receive files uploaded by clients with the Apollo Router description: Configure the Apollo Router to receive file uploads using the GraphQL multipart request spec. minVersion: 1.41.1 +noIndex: true --- -This feature is currently in invite-only [preview](/resources/product-launch-stages/#product-launch-stages). Don't hesitate to get in touch if you'd like to request access or have any questions or feedback. +This feature is currently in invite-only [preview](/resources/product-launch-stages/#product-launch-stages). Get in touch with your Apollo contact to request access. From d87080c858305621d35c7b95f60f81b493813804 Mon Sep 17 00:00:00 2001 From: Maria Elisabeth Schreiber Date: Mon, 13 May 2024 09:50:49 -0600 Subject: [PATCH 18/18] Condense callouts --- docs/source/executing-operations/file-uploads.mdx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/source/executing-operations/file-uploads.mdx b/docs/source/executing-operations/file-uploads.mdx index 7a2867a509..675206fa01 100644 --- a/docs/source/executing-operations/file-uploads.mdx +++ b/docs/source/executing-operations/file-uploads.mdx @@ -8,12 +8,10 @@ noIndex: true -This feature is currently in invite-only [preview](/resources/product-launch-stages/#product-launch-stages). Get in touch with your Apollo contact to request access. +This feature is in invite-only [preview](/resources/product-launch-stages/#product-launch-stages) for organization with an Enterprise plan. Get in touch with your Apollo contact to request access. - - Learn how to configure the Apollo Router to receive file uploads in client requests using the [GraphQL multipart request specification](https://github.com/jaydenseric/graphql-multipart-request-spec). ## About file uploads using multipart requests @@ -311,7 +309,7 @@ Metrics in the Apollo Router for file uploads: @@ -324,7 +322,7 @@ Counter for the number of file uploads. @@ -337,7 +335,7 @@ Histogram for the size of uploaded files.
Attribute/
Default value
DescriptionAttribute/
Description
Default value Valid Values
+ +`false` Flag to enable reception of client file uploads boolean
##### `protocols.multipart.enabled` -Default: `false` + +Flag to enable reception of multipart file uploads + + + +`false` Flag to enable reception of multipart file uploads boolean
##### `protocols.multipart.mode` -Default: None + +Supported file upload mode + + + +`streaming` Supported file upload mode `streaming` -(required value)
##### `protocols.multipart.limits.max_file_size` -Default: `1mb` -The maximum file size to accept. +The maximum file size to accept. If this limit is exceeded, the router rejects the entire request. + -Accepts values in a human-readable format; for example, `5kb` and `99mb` are acceptable values. +`1mb` + + + +values in a [human-readable format](https://crates.io/crates/bytesize), for example, `5kb` and `99mb`
##### `protocols.multipart.limits.max_files` -Default: `4` -The maximum number of files to accept. +The maximum number of files to accept. If this limit is exceeded, the router rejects the entire request. + + + +`4` + integer
-Counter for the number of file uploads. +Counter for the number of file uploads
-Histogram for the size of uploaded files. +Histogram for the size of uploaded files
-Histogram for the number of uploaded files. +Histogram for the number of uploaded files