[Question] Meteor/Apollo Subscriptions #199
-
Hi, I'm running a Meteor/Apollo/React stack and I was trying to get Graphql Subscriptions working and I came across this package. However, there isn't an example of implementation using Meteor/Apollo. Would anyone be able to advise on how to proceed with this? Currently on my server I have: const schema = makeExecutableSchema({
typeDefs,
resolvers,
})
const context = async ({req}) => ({
user: await getUser(req.headers.authorization)
})
const server = new ApolloServer({
schema,
context
})
server.applyMiddleware({
app: WebApp.connectHandlers,
path: "/graphql",
})
WebApp.connectHandlers.use("/graphql", (req, res) => {
if (req.method === "GET") {
res.end()
}
}) And on my client I have: const httpLink = new HttpLink({
uri: "http://XXX.XXX.XX.XX:3000/graphql"
})
const authLink = new ApolloLink((operation, forward) => {
const token = Accounts._storedLoginToken()
operation.setContext(() => ({
headers: {
authorization: token
}
}))
return forward(operation)
})
const client = new ApolloClient({
link: from([authLink, httpLink]),
cache: new InMemoryCache()
})
const ApolloApp = () => (
<BrowserRouter>
<ApolloProvider client={client}>
<App client={client} />
</ApolloProvider>
</BrowserRouter>
)
Meteor.startup(() => {
render(<ApolloApp />, document.getElementById("app"))
}) How should I go about configuring this to allow for subscriptions? Meteor does have the swydo:ddp-apollo package but I've not been able to set that up correctly and one of the drawbacks is that Graphiql will no longer be available. I can't depend on Apollo Dev Tools right now either because it doesn't open up Graphiql either regardless of server configuration (already filed a bug for this, it just shows up as blank). If there's some way I can use this package to get subscriptions working, that would be great. Appreciate any help, thank you. |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 12 replies
-
Hey there! Sadly I have no experience with Apollo whatsoever, and very little with Meteor (years ago I've used it). Briefly looking at the mentioned Regarding the server, Meteor exports the |
Beta Was this translation helpful? Give feedback.
-
Hi, sorry for the late response as I had to work on other aspects of the app I'm building. I will be working on this shortly and will look into your suggestions and will update here once I have made any progress. Thanks a lot for the explanation and links. |
Beta Was this translation helpful? Give feedback.
-
Here's the final complete source for my client and server. For more insights, check out this thread: Client: import React from "react"
import { render } from "react-dom"
import { BrowserRouter } from "react-router-dom"
import { ApolloClient, ApolloProvider, HttpLink, split, InMemoryCache } from "@apollo/client"
import { ApolloLink, Observable} from '@apollo/client/core'
import { offsetLimitPagination, getMainDefinition } from "@apollo/client/utilities"
import { ApolloLink as ApolloLink2, from } from "apollo-link"
import { print, GraphQLError } from "graphql"
import { createClient } from "graphql-ws"
import App from "../../ui/App"
class WebSocketLink extends ApolloLink {
constructor(options) {
super()
this.client = createClient(options)
}
request(operation) {
return new Observable((sink) => {
return this.client.subscribe(
{ ...operation, query: print(operation.query) },
{
next: sink.next.bind(sink),
complete: sink.complete.bind(sink),
error: (err) => {
if (err instanceof Error) return sink.error(err)
if (err instanceof CloseEvent) {
return sink.error(
// reason will be available on clean closes
new Error(
`Socket closed with event ${err.code} ${err.reason || ''}`
)
)
}
return sink.error(new GraphQLError({ message: message }))
}
}
)
})
}
}
const httpLink = new HttpLink({ uri: "http://XXX.XXX.XX.XX:4000/graphql" })
const authLink = new ApolloLink2((operation, forward) => {
const token = Accounts._storedLoginToken()
operation.setContext(() => ({
headers: {
authorization: token
}
}))
return forward(operation)
})
const wsLink = new WebSocketLink({
url: "ws://XXX.XXX.XX.XX:4000/subscriptions",
connectionParams: () => ({ authorization: Accounts._storedLoginToken() })
})
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
// You can just use a normal InMemoryCache with no options, I just left
// my implementation in
const cache = new InMemoryCache({
typePolicies: {
Query: {
fields: {
getFeed: offsetLimitPagination()
}
}
}
})
const client = new ApolloClient({
link: from([authLink, splitLink]),
cache
})
const ApolloApp = () => (
<BrowserRouter>
<ApolloProvider client={client}>
<App client={client} />
</ApolloProvider>
</BrowserRouter>
)
Meteor.startup(() => {
render(<ApolloApp />, document.getElementById("app"))
})
/*
The following line is to disconnect Meteor's DDP/WebSocket connection
as it is not needed in production. Can be commented out in development
to allow for Hot Code Push convenience.
*/
//Meteor.disconnect() Server: import express from "express"
import { ApolloServer } from "apollo-server-express"
import ws from "ws"
import { useServer } from "graphql-ws/lib/use/ws"
const typeDefs = [/* Schemas */]
const resolvers = merge(/* Resolvers */)
const schema = makeExecutableSchema({ typeDefs, resolvers })
const app = express()
const context = async ({req}) => ({
user: await getUser(req.headers.authorization)
})
const wsContext = async ({ connectionParams }) => ({
user: await getUser(connectionParams.authorization)
})
const apolloServer = new ApolloServer({ schema, context })
apolloServer.applyMiddleware({ app })
const server = app.listen(4000, () => {
const wsServer = new ws.Server({
server,
path: '/subscriptions'
})
useServer({ schema, context: wsContext }, wsServer)
}) |
Beta Was this translation helpful? Give feedback.
Here's the final complete source for my client and server. For more insights, check out this thread:
Client: