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

docs: add file uploads #4626

Merged
merged 30 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
a93a9ea
Add first draft
Meschreiber Feb 9, 2024
b13c969
Update initial example for a single upload
Meschreiber Feb 9, 2024
29bb6b0
Add examples
Meschreiber Feb 12, 2024
504bc89
Add meta description
Meschreiber Feb 13, 2024
b183d9c
Add to list of enterprise features
Meschreiber Feb 13, 2024
067c2f6
Standardize caps
Meschreiber Feb 13, 2024
e45a515
Merge branch 'dev' into docs/file-uploads
Geal Feb 13, 2024
4948ea0
Copyedit
Meschreiber Feb 13, 2024
e4563b1
Apply suggestions from code review
Meschreiber Feb 15, 2024
fa7323e
Merge branch 'dev' into docs/file-uploads
Meschreiber Feb 16, 2024
ec12fa3
Remove from enterprise list (managed in monodocs now)
Meschreiber Feb 16, 2024
b2f8ddd
Update configuration information
Meschreiber Feb 16, 2024
51ec684
Merge branch 'dev' into docs/file-uploads
Meschreiber Feb 20, 2024
74bf5be
Callout private preview limitations
Meschreiber Feb 20, 2024
8da3c75
Merge branch 'dev' into docs/file-uploads
Meschreiber Feb 21, 2024
246c7dd
Add minVersion
Meschreiber Feb 23, 2024
42ab06f
Merge branch 'dev' into docs/file-uploads
Meschreiber Feb 23, 2024
9310020
Merge branch 'dev' into docs/file-uploads
BrynCooke Mar 5, 2024
c46a350
Bump minVersion
Meschreiber Mar 8, 2024
e6fbced
Update unsupported query modes
Meschreiber Apr 8, 2024
3a38b12
Add unsupported and supported usage examples
Meschreiber Apr 9, 2024
cbda633
Merge branch 'dev' into docs/file-uploads
abernix Apr 26, 2024
6ac203b
Merge branch 'dev' into docs/file-uploads
abernix May 8, 2024
c579d75
Merge branch 'dev' into docs/file-uploads
abernix May 13, 2024
dea67d4
Remove File uploads from nav
Meschreiber May 13, 2024
31a3768
Update frontmatter
Meschreiber May 13, 2024
d87080c
Condense callouts
Meschreiber May 13, 2024
172c8a4
Merge branch 'dev' into docs/file-uploads
Meschreiber May 13, 2024
9fa5247
Merge branch 'dev' into docs/file-uploads
abernix May 14, 2024
c927dcb
Merge branch 'dev' into docs/file-uploads
abernix May 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion docs/source/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@
"experimental"
]
],
"File uploads": [
"/executing-operations/file-uploads",
[
"enterprise",
"preview"
]
],
"GraphQL Subscriptions": {
"Subscriptions setup": [
"/executing-operations/subscription-support",
Expand All @@ -101,7 +108,7 @@
"enterprise"
]
]
}
}
},
"Telemetry and Monitoring": {
"Overview": "/configuration/telemetry/overview",
Expand Down
283 changes: 283 additions & 0 deletions docs/source/executing-operations/file-uploads.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
---
title: File uploads
description: Receive file uploads with the Apollo Router
minVersion: X.x
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
---

<PreviewFeature />

<EnterpriseFeature />

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).
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved

### 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.
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved

Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
#### 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

Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
<table class="field-table">
<thead>
<tr>
<th>Attribute/ <br/> Default value</th>
<th>Description</th>
<th>Valid Values</th>
</tr>
</thead>
<tbody>
<tr>
<td>

##### `enabled`

Default: `false`

</td>
<td>Flag to enable reception of client file uploads</td>
<td>boolean</td>

</tr>
<tr>
<td>

##### `protocols.multipart.enabled`
Default: `false`

</td>
<td>Flag to enable reception of multipart file uploads</td>
<td>boolean</td>
</tr>
<tr>
<td>

##### `protocols.multipart.mode`
Default: None
BrynCooke marked this conversation as resolved.
Show resolved Hide resolved

</td>
<td>Supported file upload mode</td>
<td>

`streaming`
(required value)

</td>
</tr>
<tr>
<td>

##### `protocols.multipart.limits.max_file_size`
Default: `1MB`

</td>
<td>The maximum file size to accept</td>
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
<td>integer of MB</td>
</tr>
<tr>
<td>

##### `protocols.multipart.limits.max_files`
Default: `4`

</td>
<td>The maximum number of files to accept</td>
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
<td>integer</td>
</tr>
</tbody>
</table>


### 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

<Note>

The order of the files sent must match the order they're declared in the map sent in the second part of the request.

</Note>

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--
```

<Note>

`gc0p4Jq0M2Yt08jU534c0p` is an example of the boundary parameter required by [multipart requests](https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html).

</Note>

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:

<table class="field-table metrics">
<thead>
<tr>
<th>Name</th>
<th>Description</th>
</tr>
</thead>

<tbody>
<tr>
<td>

##### `apollo.router.operations.file_uploads`

</td>
<td>

Counter for the number of file uploads.

</td>
</tr>

<tr>
<td>

##### `apollo.router.operations.file_uploads.file_size`

</td>
<td>

Histogram for the size of uploaded files.

</td>
</tr>

<tr>
<td>

##### `apollo.router.operations.file_uploads.files`

</td>
<td>

Histogram for the number of uploaded files.

</td>
</tr>

</tbody>
</table>

## Error codes for file uploads

A file upload request may receive the following error responses:

<table class="field-table metrics">
<tr>
<th>Error Code</th>
<th>Description</th>
</tr>
<tr>
<td>

##### `UPLOADS_LIMITS_MAX_FILES_EXCEEDED`

</td>
<td>The number of files in the payload exceeded the configured limit.</td>
</tr>
<tr>
<td>

##### `UPLOADS_LIMITS_MAX_FILE_SIZE_EXCEEDED`

</td>
<td>A file exceeded the maximum configured file size.</td>
Meschreiber marked this conversation as resolved.
Show resolved Hide resolved
</tr>
<tr>
<td>

##### `UPLOADS_FILE_MISSING`

</td>
<td>The operation specified a file that was missing from the request.</td>
</tr>
<tr>
<td>

##### `UPLOADS_OPERATION_CANNOT_STREAM`

</td>
<td>The query was invalid as it cannot be streamed to the client.</td>
</tr>
</table>


## Known limitations

### Unsupported query modes

When file uploads are enabled in the router, any operations using the [`@defer`](/graphos/operations/defer/) are unsupported.
Loading