Skip to content

Commit

Permalink
Use pkgroll, bump Node, remove deprecations, improve, shorter paths, …
Browse files Browse the repository at this point in the history
…and friends (#613)

Co-authored-by: Benjie <[email protected]>
  • Loading branch information
enisdenjo and benjie authored Jan 14, 2025
1 parent 08915c4 commit 3f11aba
Show file tree
Hide file tree
Showing 45 changed files with 2,199 additions and 2,238 deletions.
7 changes: 7 additions & 0 deletions .changeset/clean-seas-work.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'graphql-ws': major
---

Drop support for `ws` v7

`ws` v7 has been deprecated. Please upgrade and use v8.
7 changes: 7 additions & 0 deletions .changeset/friendly-cats-sing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'graphql-ws': major
---

Drop support for deprecated `fastify-websocket`

[`fastify-websocket` has been deprecated since v4.3.0.](https://www.npmjs.com/package/fastify-websocket). Please upgrade and use [`@fastify/websocket`](https://github.com/fastify/fastify-websocket).
44 changes: 44 additions & 0 deletions .changeset/lemon-dolls-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
'graphql-ws': major
---

The `/lib/` part from imports has been removed, for example `graphql-ws/lib/use/ws` becomes `graphql-ws/use/ws`

### Migrating from v5 to v6

Simply remove the `/lib/` part from your graphql-ws imports that use a handler.

#### ws

```diff
- import { useServer } from 'graphql-ws/lib/use/ws';
+ import { useServer } from 'graphql-ws/use/ws';
```

#### uWebSockets.js

```diff
- import { makeBehavior } from 'graphql-ws/lib/use/uWebSockets';
+ import { makeBehavior } from 'graphql-ws/use/uWebSockets';
```

#### @fastify/websocket

```diff
- import { makeHandler } from 'graphql-ws/lib/use/@fastify/websocket';
+ import { makeHandler } from 'graphql-ws/use/@fastify/websocket';
```

#### Bun

```diff
- import { handleProtocols, makeHandler } from 'graphql-ws/lib/use/bun';
+ import { handleProtocols, makeHandler } from 'graphql-ws/use/bun';
```

#### Deno

```diff
- import { makeHandler } from 'https://esm.sh/graphql-ws/lib/use/deno';
+ import { makeHandler } from 'https://esm.sh/graphql-ws/use/deno';
```
5 changes: 5 additions & 0 deletions .changeset/long-glasses-drive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'graphql-ws': major
---

`ErrorMessage` uses and `onError` returns `GraphQLFormattedError` (instead of `GraphQLError`)
17 changes: 17 additions & 0 deletions .changeset/nervous-peaches-hide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'graphql-ws': minor
---

Client is truly zero-dependency, not even a peer dependency on `graphql`

In non-browser environments, you can use only the client and not even depend on `graphql` by importing from `graphql-ws/client`.

```ts
import { createClient } from 'graphql-ws/client';

const client = createClient({
url: 'ws://localhost:4000/graphql'
});
```

Note that, in browser envirments (and of course having your bundler use the [`browser` package.json field](https://docs.npmjs.com/cli/v11/configuring-npm/package-json#browser)), you don't have to import from `graphql-ws/client` - simply importing from `graphql-ws` will only have the `createClient` available.
7 changes: 7 additions & 0 deletions .changeset/proud-emus-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'graphql-ws': major
---

Least supported Node version is v20

Node v10 has been deprecated for years now. There is no reason to support it. Bumping the engine to the current LTS (v20) also allows the code to be leaner and use less polyfills.
7 changes: 7 additions & 0 deletions .changeset/shaggy-geese-learn.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'graphql-ws': major
---

Least supported `graphql` peer dependency is ^15.10.0 and ^16.10.0

Users are advised to use the latest of `graphql` because of various improvements in performance and security.
5 changes: 5 additions & 0 deletions .changeset/stale-tomatoes-deny.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'graphql-ws': major
---

`NextMessage` uses and `onNext` returns `FormattedExecutionResult` (instead of `ExecutionResult`)
107 changes: 107 additions & 0 deletions .changeset/strange-ties-mix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
'graphql-ws': major
---

`onSubscribe`, `onOperation`, `onError`, `onNext` and `onComplete` hooks don't have the full accompanying message anymore, only the ID and the relevant part from the message

There is really no need to pass the full `SubscribeMessage` to the `onSubscribe` hook. The only relevant parts from the message are the `id` and the `payload`, the `type` is useless since the hook inherently has it (`onNext` is `next` type, `onError` is `error` type, etc).

The actual techincal reason for not having the full message is to avoid serialising results and errors twice. Both `onNext` and `onError` allow the user to augment the result and return it to be used instead. `onNext` originally had the `NextMessage` argument which already has the `FormattedExecutionResult`, and `onError` originally had the `ErrorMessage` argument which already has the `GraphQLFormattedError`, and they both also returned `FormattedExecutionResult` and `GraphQLFormattedError` respectivelly - meaning, if the user serialised the results - the serialisation would happen **twice**.

### Migrating from v5 to v6

#### `onSubscribe`

```diff
import { ServerOptions, SubscribePayload } from 'graphql-ws';

const opts: ServerOptions = {
- onSubscribe(ctx, message) {
- const messageId = message.id;
- const messagePayload: SubscribePayload = message.payload;
- },
+ onSubscribe(ctx, id, payload) {
+ const messageId = id;
+ const messagePayload: SubscribePayload = payload;
+ },
};
```

#### `onOperation`

The `SubscribeMessage.payload` is not useful here at all, the `payload` has been parsed to ready-to-use graphql execution args and should be used instead.

```diff
import { ExecutionArgs } from 'graphql';
import { ServerOptions, SubscribePayload } from 'graphql-ws';

const opts: ServerOptions = {
- onOperation(ctx, message) {
- const messageId = message.id;
- const messagePayload: SubscribePayload = message.payload;
- },
+ onOperation(ctx, id, args) {
+ const messageId = id;
+ const executionArgs: ExecutionArgs = args;
+ },
};
```

#### `onError`

The `ErrorMessage.payload` (`GraphQLFormattedError[]`) is not useful here at all, the user has access to `GraphQLError[]` that are true instances of the error containing object references to `originalError`s and other properties. The user can always convert and return `GraphQLFormattedError[]` by using the `.toJSON()` method.

```diff
import { GraphQLError, GraphQLFormattedError } from 'graphql';
import { ServerOptions } from 'graphql-ws';

const opts: ServerOptions = {
- onError(ctx, message, errors) {
- const messageId = message.id;
- const graphqlErrors: readonly GraphQLError[] = errors;
- const messagePayload: readonly GraphQLFormattedError[] = message.payload;
- },
+ onError(ctx, id, errors) {
+ const messageId = id;
+ const graphqlErrors: readonly GraphQLError[] = errors;
+ const messagePayload: readonly GraphQLFormattedError[] = errors.map((e) => e.toJSON());
+ },
};
```

#### `onNext`

The `NextMessage.payload` (`FormattedExecutionResult`) is not useful here at all, the user has access to `ExecutionResult` that contains actual object references to error instances. The user can always convert and return `FormattedExecutionResult` by serialising the errors with `GraphQLError.toJSON()` method.

```diff
import { ExecutionResult, FormattedExecutionResult } from 'graphql';
import { ServerOptions } from 'graphql-ws';

const opts: ServerOptions = {
- onNext(ctx, message, result) {
- const messageId = message.id;
- const graphqlResult: ExecutionResult = result;
- const messagePayload: FormattedExecutionResult = message.payload;
- },
+ onNext(ctx, id, result) {
+ const messageId = id;
+ const graphqlResult: ExecutionResult = result;
+ const messagePayload: FormattedExecutionResult = { ...result, errors: result.errors?.map((e) => e.toJSON()) };
+ },
};
```

#### `onComplete`

```diff
import { ServerOptions } from 'graphql-ws';

const opts: ServerOptions = {
- onComplete(ctx, message) {
- const messageId = message.id;
- },
+ onComplete(ctx, id) {
+ const messageId = id;
+ },
};
```
49 changes: 49 additions & 0 deletions .changeset/tiny-worms-fry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
'graphql-ws': major
---

Errors thrown from subscription iterables will be caught and reported through the `ErrorMessage`

Compared to the behaviour before, which terminated the whole WebSocket connection - those errors are now gracefully reported and terminate only the specific subscription that threw the error.

There's been [an editorial change in the GraphQL Spec suggesting this being the correct approach](https://github.com/graphql/graphql-spec/pull/1099).

Also, if you'd like to get involved and ideally drop your opinion about whether iterable errors should be reported as errors or `ExecutionResult`s with `errors` field set, [please read more here](https://github.com/graphql/graphql-spec/pull/1127).

### Migrating from v5 to v6

If you had used the suggested "ws server usage with custom subscribe method that gracefully handles thrown errors" recipe, you can simply remove it since this behaviour is now baked in.

```diff
import { subscribe } from 'graphql';
import { useServer } from 'graphql-ws/use/ws';
import { WebSocketServer } from 'ws'; // yarn add ws

const wsServer = new WebSocketServer({
port: 4000,
path: '/graphql',
});

useServer(
{
schema,
- async subscribe(...args) {
- const result = await subscribe(...args);
- if ('next' in result) {
- // is an async iterable, augment the next method to handle thrown errors
- const originalNext = result.next;
- result.next = async () => {
- try {
- return await originalNext();
- } catch (err) {
- // gracefully handle the error thrown from the next method
- return { value: { errors: [err] } };
- }
- };
- }
- return result;
- },
},
wsServer,
);
```
24 changes: 24 additions & 0 deletions .changeset/twelve-rabbits-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'graphql-ws': major
---

Remove deprecated `isMessage`, use `validateMessage` instead

### Migrating from v5 to v6

Replace all ocurrances of `isMessage` with `validateMessage`. Note that `validateMessage` throws if the message is not valid, compared with `isMessage` that simply returned true/false.

```diff
- import { isMessage } from 'graphql-ws';
+ import { validateMessage } from 'graphql-ws';

function isGraphQLWSMessage(val) {
- return isMessage(val);
+ try {
+ validateMessage(val);
+ return true;
+ } catch {
+ return false;
+ }
}
```
19 changes: 19 additions & 0 deletions .changeset/wet-teachers-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'graphql-ws': major
---

Removed deprecated `isFatalConnectionProblem`, use `shouldRetry` instead

### Migrating from v5 to v6

Replace all ocurrances of `isFatalConnectionProblem` with `shouldRetry`. Note that the result is inverted, where you returned `false` in `isFatalConnectionProblem` you should return `true` in `shouldRetry`.

```diff
import { createClient } from 'graphql-ws';

const client = createClient({
url: 'ws://localhost:4000/graphql',
- isFatalConnectionProblem: () => false,
+ shouldRetry: () => true,
});
```
23 changes: 0 additions & 23 deletions .eslintrc.js

This file was deleted.

31 changes: 16 additions & 15 deletions .github/workflows/ci.yml → .github/workflows/check.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: CI
name: Check

on:
push:
Expand All @@ -7,15 +7,8 @@ on:
pull_request:

jobs:
check:
strategy:
fail-fast: false
matrix:
check:
- format
- lint
- type
name: Check ${{ matrix.check }}
format:
name: Format
runs-on: ubuntu-latest
steps:
- name: Checkout
Expand All @@ -25,17 +18,25 @@ jobs:
with:
node-version-file: .node-version
- name: Check
run: yarn check:${{ matrix.check }}
run: yarn check:format

test:
name: Test
types:
name: Types with graphql@${{matrix.graphql}}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
graphql: [15, 16]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup
uses: the-guild-org/shared-config/setup@v1
with:
node-version-file: .node-version
- name: Test
run: yarn test
- name: Install
run: yarn add --dev graphql@${{matrix.graphql}}
- name: Info
run: yarn info graphql
- name: Check
run: yarn check:types
Loading

0 comments on commit 3f11aba

Please sign in to comment.