Same here. If I am not mistaken, you need to add the following to graphql.ts
allowedOperations: [
OperationTypeNode.QUERY,
OperationTypeNode.MUTATION,
OperationTypeNode.SUBSCRIPTION,
]
Redwood has lots of ingredients ready for subscription, but it is a pity there was not time yet, to take the last steps. sdl works, service can be created with SubscriptionResolvers, but reaching it is does not seem to be possible.
Cells are not compatible with subscriptions. So one needs to add its own component:
export const SubscribeData = ({ id }: { id: string }) => {
const { data, loading, error } = useSubscription(QUERY, { variables: { id } })
if(loading) return Loading...
if(error) return Something went wrong...
if(!data) return null
return <>{JSON.stringify(data)</>
}
(Why do we need invisible logic of cells, if code can be this short?)
However this one does not reach the service.
Reason probably is a missing link in configuration of Apollo client.
And following graphql-yoga custom-link-recipe (Subscriptions – GraphQL Yoga)
I implemented the following in App.tsx
<RedwoodApolloProvider
useAuth={useAuth}
graphQLClientConfig={{
link: (rwLinks) => {
const sseLink = new SSELink({
uri: globalThis.RWJS_API_GRAPHQL_URL,
withCredentials: true,
})
const httpsLink = split(
({ query, operationName }) => {
const definition = getOperationAST(query, operationName)
return (
definition?.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
sseLink,
rwLinks[3]
)
return ApolloLink.from([rwLinks[0], rwLinks[1], rwLinks[2], httpsLink])
},
}}
>
For completeness my SSELink.tsx adapted from Yoga docs.
import { ApolloLink, FetchResult, Observable, Operation } from '@apollo/client/core'
import { print } from 'graphql'
type SSELinkOptions = EventSourceInit & { uri: string }
export class SSELink extends ApolloLink {
constructor(private options: SSELinkOptions) {
super()
}
request(operation: Operation): Observable<FetchResult> {
const url = new URL('https://localhost/') // temp url... not nice
url.searchParams.append('query', print(operation.query))
if (operation.operationName) {
url.searchParams.append('operationName', operation.operationName)
}
if (operation.variables) {
url.searchParams.append('variables', JSON.stringify(operation.variables))
}
if (operation.extensions) {
url.searchParams.append('extensions', JSON.stringify(operation.extensions))
}
return new Observable((sink) => {
const eventsource = new EventSource(
url.toString().replace('https://localhost/', this.options.uri),
this.options
)
eventsource.onmessage = function (event) {
const data = JSON.parse(event.data)
sink.next(data)
if (eventsource.readyState === 2) {
sink.complete()
}
}
eventsource.onerror = function (error) {
sink.error(error)
}
return () => eventsource.close()
})
}
}
Now this one sends a Get requerst to server. The server logs incoming request /graphql?query=subscription....
but it does not reach the service.
So I am also stuck with not being able to subscribe.
I need subscription to know when an expensive function finished calculating… 