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

feat(persisted): Replace persistedFetchExchange with generic persistedExchange #3057

Merged
merged 10 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/pretty-cows-dance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/core': patch
---

Add logic for `request.extensions.persistedQuery` to `@urql/core` to omit sending `query` as needed.
5 changes: 5 additions & 0 deletions .changeset/strange-apples-trade.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/exchange-persisted-fetch': major
---

Remove `persistedFetchExchange` and instead implement `persistedExchange`. This exchange must be placed in front of a terminating exchange (such as the default `fetchExchange` or a `subscriptionExchange` that supports persisted queries), and only modifies incoming operations to contain `extensions.persistedQuery`, which is sent on via the API. If the API expects Automatic Persisted Queries, requests are retried by this exchange internally.
5 changes: 5 additions & 0 deletions .changeset/three-poets-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@urql/exchange-persisted-fetch': major
---

Rename `@urql/exchange-persisted-fetch` to `@urql/exchange-persisted`
106 changes: 37 additions & 69 deletions docs/advanced/persistence-and-uploads.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ order: 1
# Persisted Queries and Uploads

`urql` supports both [Automatic Persisted
Queries](https://www.apollographql.com/docs/apollo-server/performance/apq/) and [File
Uploads](https://www.apollographql.com/docs/apollo-server/data/file-uploads/).
Both of these features are implemented by enhancing or swapping out the default
[`fetchExchange`](../api/core.md#fetchexchange).
Queries](https://www.apollographql.com/docs/apollo-server/performance/apq/), Persisted Queries, and
[File Uploads](https://www.apollographql.com/docs/apollo-server/data/file-uploads/).

While File Uploads should work without any modifications, an additional exchange must be installed
and added for Persisted Queries to work.

## Automatic Persisted Queries

Expand All @@ -29,33 +30,33 @@ Additionally, we could also decide to send these hashed queries as GET requests
requests. If we only send the persisted queries with hashes as GET requests then they become a lot
easier for a CDN to cache, as by default most caches would not cache POST requests automatically.

In `urql`, we may use the `@urql/exchange-persisted-fetch` package's `persistedFetchExchange` to
implement Automatic Persisted Queries. This exchange works alongside other fetch exchanges and only
handles `query` operations.
In `urql`, we may use the `@urql/exchange-persisted` package's `persistedExchange` to
implement Automatic Persisted Queries. This exchange works alongside the default `fetchExchange`
and other exchanges by adding the `extensions.persistedQuery` parameters to a GraphQL request.

### Installation & Setup

First install `@urql/exchange-persisted-fetch` alongside `urql`:
First install `@urql/exchange-persisted` alongside `urql`:

```sh
yarn add @urql/exchange-persisted-fetch
yarn add @urql/exchange-persisted
# or
npm install --save @urql/exchange-persisted-fetch
npm install --save @urql/exchange-persisted
```

You'll then need to add the `persistedFetchExchange` method, that this package exposes,
You'll then need to add the `persistedExchange` function, that this package exposes,
to your `exchanges`.

```js
import { createClient, dedupExchange, fetchExchange, cacheExchange } from 'urql';
import { persistedFetchExchange } from '@urql/exchange-persisted-fetch';
import { persistedExchange } from '@urql/exchange-persisted-fetch';

const client = createClient({
url: 'http://localhost:1234/graphql',
exchanges: [
dedupExchange,
cacheExchange,
persistedFetchExchange({
persistedExchange({
preferGetForPersistedQueries: true,
}),
fetchExchange,
Expand All @@ -65,24 +66,24 @@ const client = createClient({

As we can see, typically it's recommended to set `preferGetForPersistedQueries` to `true` to force
all persisted queries to use GET requests instead of POST so that CDNs can do their job.
We also added the `persistedFetchExchange` in front of the usual `fetchExchange`, since it only
handles queries but not mutations.
We also added the `persistedExchange` in front of the usual `fetchExchange`, since it has to
update operations before they reach an exchange that talks to an API.

The `preferGetForPersistedQueries` is similar to the [`Client`'s
`preferGetMethod`](../api/core.md#client) but only switches persisted queries to use GET requests
instead. This is preferable since sometimes the GraphQL query can grow too large for a simple GET
query to handle, while the `persistedFetchExchange`'s SHA256 hashes will remain predictably small.
query to handle, while the `persistedExchange`'s SHA256 hashes will remain predictably small.

### Customizing Hashing

The `persistedFetchExchange` also accepts a `generateHash` option. This may be used to swap out the
The `persistedExchange` also accepts a `generateHash` option. This may be used to swap out the
exchange's default method of generating SHA256 hashes. By default, the exchange will use the
built-in [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) on the
browser, which has been implemented to support IE11 as well. In Node.js it'll use the [Node
Crypto Module](https://nodejs.org/api/crypto.html) instead.
built-in [Web Crypto API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) when it's
available, and in Node.js it'll use the [Node Crypto Module](https://nodejs.org/api/crypto.html)
instead.

If you're using [the `graphql-persisted-document-loader` for
Webpack](https://github.com/leoasis/graphql-persisted-document-loader) for instance, then you will
Webpack](https://github.com/leoasis/graphql-persisted-document-loader), for instance, then you will
already have a loader generating SHA256 hashes for you at compile time. In that case we could swap
out the `generateHash` function with a much simpler one that uses the `generateHash` function's
second argument, a GraphQL `DocumentNode` object.
Expand All @@ -94,7 +95,7 @@ persistedFetchExchange({
```

If you're using **React Native** then you may not have access to the Web Crypto API, which means
that you have to provide your own SHA256 function to the `persistedFetchExchange`. Luckily we can do
that you have to provide your own SHA256 function to the `persistedExchange`. Luckily, we can do
so easily by using the first argument `generateHash` receives, a GraphQL query as a string.

```js
Expand All @@ -108,58 +109,25 @@ persistedFetchExchange({
```

Additionally, if the API only expects persisted queries and not arbitrary ones and all queries are
pre-registered against the API then the `persistedFetchExchange` may be put into a **non-automatic**
pre-registered against the API then the `persistedExchange` may be put into a **non-automatic**
persisted queries mode by giving it the `enforcePersistedQueries: true` option. This disables any
retry logic and assumes that persisted queries will be handled like regular GraphQL requests.

[Read more about `@urql/persisted-fetch-exchange` in our API
docs.](../api/persisted-fetch-exchange.md)

## File Uploads

GraphQL server frameworks like [Apollo Server support an unofficial spec for file
uploads.](https://www.apollographql.com/docs/apollo-server/data/file-uploads/) This allows us to
define mutations on our API that accept an `Upload` input, which on the client would be a variable
that we can set to a [File](https://developer.mozilla.org/en-US/docs/Web/API/File), which we'd
typically retrieve via a [file input for
instance](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications).

In `urql`, we may use the `@urql/exchange-multipart-fetch` package's `multipartFetchExchange` to
support file uploads, which is a drop-in replacement for the default
[`fetchExchange`](../api/core.md#fetchexchange). It may also be used [alongside the
`persistedFetchExchange`](#automatic-persisted-queries).
Many GraphQL server frameworks and APIs support the ["GraphQL Multipart Request
Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) to allow files to be uploaded.
Often, this is defined in schemas using a `File` or `Upload` input.
This allows us to pass a `File` or `Blob` directly to our GraphQL requests as variables, and the
spec requires us to perform this request as a multipart upload.

It works by using the [`extract-files` package](https://www.npmjs.com/package/extract-files). When
the `multipartFetchExchange` sees at least one `File` in the variables it receives for a mutation,
then it will send a `multipart/form-data` POST request instead of a standard `application/json`
one. This is basically the same kind of request that we'd expect to send for regular HTML forms.

### Installation & Setup

First install `@urql/exchange-multipart-fetch` alongside `urql`:

```sh
yarn add @urql/exchange-multipart-fetch
# or
npm install --save @urql/exchange-multipart-fetch
```

The `multipartFetchExchange` is a drop-in replacement for the `fetchExchange`, which should be
replaced in the list of `exchanges`:

```js
import { createClient, dedupExchange, cacheExchange } from 'urql';
import { multipartFetchExchange } from '@urql/exchange-multipart-fetch';

const client = createClient({
url: 'http://localhost:3000/graphql',
exchanges: [dedupExchange, cacheExchange, multipartFetchExchange],
});
```
Files are often handled in the browser via the [File API](https://developer.mozilla.org/en-US/docs/Web/API/File),
which we may typically get to via a [file input](https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications)
for example.

If you're using the `persistedFetchExchange` then put the `persistedFetchExchange` in front of the
`multipartFetchExchange`, since only the latter is a full replacement for the `fetchExchange`, and
the former only handled query operations.
In `urql`, these are supported natively, so as long as your JS environment supports either `File` or
`Blob`s, you can pass these directly to any `urql` API via your `variables`, and the default
`fetchExchange` will swich to using a multipart request instead.

[Read more about `@urql/multipart-fetch-exchange` in our API
docs.](../api/multipart-fetch-exchange.md)
Previously, this worked by installing the [`@urql/multipart-fetch-exchange` package](../api/multipart-fetch-exchange.md),
however, this package has been deprecated and file uploads are now built into `@urql/core`.
1 change: 0 additions & 1 deletion docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ more about the core package on the "Core" page.](../basics/core.md)
- [`@urql/exchange-retry` API docs](./retry-exchange.md)
- [`@urql/exchange-execute` API docs](./execute-exchange.md)
- [`@urql/exchange-multipart-fetch` API docs](./multipart-fetch-exchange.md)
- [`@urql/exchange-persisted-fetch` API docs](./persisted-fetch-exchange.md)
- [`@urql/exchange-request-policy` API docs](./request-policy-exchange.md)
- [`@urql/exchange-auth` API docs](./auth-exchange.md)
- [`@urql/exchange-refocus` API docs](./refocus-exchange.md)
3 changes: 1 addition & 2 deletions docs/api/multipart-fetch-exchange.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ It follows the unofficial [GraphQL Multipart Request
Spec](https://github.com/jaydenseric/graphql-multipart-request-spec) which is supported by the
[Apollo Sever package](https://www.apollographql.com/docs/apollo-server/data/file-uploads/).

This exchange uses the same fetch logic as the [`fetchExchange`](./core.md#fetchexchange) and the
[`persistedFetchExchange`](./persisted-fetch-exchange.md) by reusing logic from `@urql/core/internal`.
This exchange uses the same fetch logic as the [`fetchExchange`](./core.md#fetchexchange) and by reusing logic from `@urql/core/internal`.
The `multipartFetchExchange` is a drop-in replacement for the default
[`fetchExchange`](./core.md#fetchexchange) and will act exactly like the `fetchExchange` unless the
`variables` that it receives for mutations contain any `File`s as detected by the `extract-files` package.
Expand Down
68 changes: 0 additions & 68 deletions docs/api/persisted-fetch-exchange.md

This file was deleted.

Loading