-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Support defer and stream GraphQL Directives in RedwoodRealti…
…me (#9235) Stream and defer are directives that allow you to improve latency for clients by sending data the most important data as soon as it's ready. As applications grow, the GraphQL operation documents can get bigger. The server will only send the response back once all the data requested in the query is ready. However, not all requested data is of equal importance, and the client may not need all of the data at once. See: https://the-guild.dev/graphql/yoga-server/docs/features/defer-stream This PR: * adds useDeferStream plugin to useRedwoodRealtime * adds option to enable plugin * fixes useRedwoodError on Execute where the result only includes result.data but not other result info like incremental or path information that a Repeater ReadableStream needs * adds Repeater to write safe async resolvers: https://the-guild.dev/graphql/yoga-server/docs/features/defer-stream#writing-safe-stream-resolvers ### TODO: - [x] document - [x] add examples to realtime - [x] change countdown subscription to use Repeater - [x] check if directives are supported in React with Apollo Client - note: defer supported by Apollo Client, stream is not - [x] check the client gql use of queries works with code gen - [ ] test error/exception handling in a Repeater push ### Examples ```graphql export const schema = gql` type Query { alphabet: [String!]! @skipAuth """ A field that resolves fast. """ fastField: String! @skipAuth """ A field that resolves slowly. Maybe you want to @defer this field ;) """ slowField(waitFor: Int! = 5000): String @skipAuth } ` ``` and services: ```ts import { Repeater } from '@redwoodjs/realtime' import { logger } from 'src/lib/logger' export const alphabet = async () => { return new Repeater<string>(async (push, stop) => { const values = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] const publish = () => { const value = values.shift() if (value) { logger.debug({ value }, 'publishing') push(value) } if (values.length === 0) { stop() } } const interval = setInterval(publish, 1000) stop.then(() => { logger.debug('cancel') clearInterval(interval) }) publish() }) } const wait = (time: number) => new Promise((resolve) => setTimeout(resolve, time)) export const fastField = async () => { await wait(100) return 'I am speedyyy' } export const slowField = async (_, { waitFor = 5000 }) => { await wait(waitFor) console.log('slowField') return 'I am slowey' } ``` https://github.com/redwoodjs/redwood/assets/1051633/020812ed-81fd-41fb-9066-2493820da1ba ### What does incremental stream look like? When making the request with the `@stream` directive: ```bash curl -g -X POST \ -H "accept:multipart/mixed" \ -H "content-type: application/json" \ -d '{"query":"query StreamAlphabet { alphabet @stream }"}' \ http://localhost:8911/graphql ``` Here you see the initial response has `[]` for alphabet data. Then on each push to the Repeater, an `incremental` update the the list of letters is sent. The stream ends when `hasNext` is `false`: ```bash * Connected to localhost (127.0.0.1) port 8911 (#0) > POST /graphql HTTP/1.1 > Host: localhost:8911 > User-Agent: curl/8.1.2 > accept:multipart/mixed > content-type: application/json > Content-Length: 53 > < HTTP/1.1 200 OK < connection: keep-alive < content-type: multipart/mixed; boundary="-" < transfer-encoding: chunked < Date: Mon, 25 Sep 2023 18:47:57 GMT < --- Content-Type: application/json; charset=utf-8 Content-Length: 39 {"data":{"alphabet":[]},"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["a"],"path":["alphabet",0]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["b"],"path":["alphabet",1]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["c"],"path":["alphabet",2]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["d"],"path":["alphabet",3]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["e"],"path":["alphabet",4]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["f"],"path":["alphabet",5]}],"hasNext":true} --- Content-Type: application/json; charset=utf-8 Content-Length: 70 {"incremental":[{"items":["g"],"path":["alphabet",6]}],"hasNext":true} --- ... --- Content-Type: application/json; charset=utf-8 Content-Length: 17 {"hasNext":false} ----- ``` --------- Co-authored-by: Josh GM Walker <[email protected]>
- Loading branch information
Showing
15 changed files
with
966 additions
and
8 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
14 changes: 14 additions & 0 deletions
14
...rc/commands/experimental/templates/defer/fastAndSlowFields/fastAndSlowFields.sdl.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
export const schema = gql` | ||
type Query { | ||
""" | ||
A field that resolves fast. | ||
""" | ||
fastField: String! @skipAuth | ||
|
||
""" | ||
A field that resolves slowly. | ||
Maybe you want to @defer this field ;) | ||
""" | ||
slowField(waitFor: Int! = 5000): String @skipAuth | ||
} | ||
` |
14 changes: 14 additions & 0 deletions
14
...src/commands/experimental/templates/defer/fastAndSlowFields/fastAndSlowFields.ts.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { logger } from 'src/lib/logger' | ||
|
||
const wait = (time: number) => | ||
new Promise((resolve) => setTimeout(resolve, time)) | ||
|
||
export const fastField = async () => { | ||
return 'I am fast' | ||
} | ||
|
||
export const slowField = async (_, { waitFor = 5000 }) => { | ||
logger.debug('waiting on slowField') | ||
await wait(waitFor) | ||
return 'I am slow' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
packages/cli/src/commands/experimental/templates/stream/alphabet/alphabet.sdl.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const schema = gql` | ||
type Query { | ||
""" | ||
A field that spells out the letters of the alphabet | ||
Maybe you want to @stream this field ;) | ||
""" | ||
alphabet: [String!]! @skipAuth | ||
} | ||
` |
31 changes: 31 additions & 0 deletions
31
packages/cli/src/commands/experimental/templates/stream/alphabet/alphabet.ts.template
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { Repeater } from '@redwoodjs/realtime' | ||
|
||
import { logger } from 'src/lib/logger' | ||
|
||
export const alphabet = async () => { | ||
return new Repeater<string>(async (push, stop) => { | ||
const letters = 'abcdefghijklmnopqrstuvwxyz'.split('') | ||
|
||
const publish = () => { | ||
const letter = letters.shift() | ||
|
||
if (letter) { | ||
logger.debug({ letter }, 'publishing letter...') | ||
push(letter) | ||
} | ||
|
||
if (letters.length === 0) { | ||
stop() | ||
} | ||
} | ||
|
||
const interval = setInterval(publish, 1000) | ||
|
||
stop.then(() => { | ||
logger.debug('cancel') | ||
clearInterval(interval) | ||
}) | ||
|
||
publish() | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ export { | |
RedisLiveQueryStore, | ||
liveQueryStore, | ||
pubSub, | ||
Repeater, | ||
} from './graphql' | ||
|
||
export type { | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters