-
-
Notifications
You must be signed in to change notification settings - Fork 810
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
Stitching secure subscriptions using makeRemoteExecutableSchema #864
Comments
@nikhilkawtakwar did you make it work? If so, how? |
@arcticbarra not actually. I connected directly to one of remote graphql server as I wanted subscription to work for that specific server. |
Hi @nikhilkawtakwar, @arcticbarra, I found a way to pass The gist is:
const server = new GraphQLServer({
schema,
context: ({ connection }) => {
if (connection && connection.context) {
return connection.context;
}
}
});
const wsLink = (operation, forward) => {
const context = operation.getContext();
const connectionParams = context.graphqlContext || {};
const client = new SubscriptionClient(subUri, {
connectionParams,
reconnect: true,
}, ws);
return client.request(operation);
}; |
@josephktcheung |
@mlewando I've taken your hint and created another gist: https://gist.github.com/josephktcheung/cd1b65b321736a520ae9d822ae5a951b This time I use |
That's exactly what I meant :) btw. I think that you're missing closing of btw2. If such approach is the correct one:
|
regarding your btw2, it can either be an article in https://www.apollographql.com/docs/graphql-subscriptions/ (which points to Also, what do you mean by tool? |
Something that would encapsulate creation/closing of the clients and the custom wsLink implementation. Eg. Additional class in |
@josephktcheung Thanks for the gist. But for secured subscription, the connection is always hanging. |
Any updates? |
For anyone that's still struggling this is how I ended up solving it. I was using AbsintheSocket but should be pretty similar for a normal socket connection. https://gist.github.com/arcticbarra/b3d6557f86472c48ae62f82f85e0dc8e |
Hi, |
My take using token/service name as identifiers to reuse the link: https://gist.github.com/tomasAlabes/a8f160d8aeb807976819ea413bcd9c5b |
This is a working example of remote schema with subscription by webscoket and query and mutation by http. It can be secured by custom headers(params) and shown in this example. Flow Client request Note
const wsLink = new ApolloLink(operation => {
// This is your context!
const context = operation.getContext().graphqlContext
// Create a new websocket link per request
return new WebSocketLink({
uri: "<YOUR_URI>",
options: {
reconnect: true,
connectionParams: { // give custom params to your websocket backend (e.g. to handle auth)
headers: {
authorization: jwt.sign(context.user, process.env.SUPER_SECRET),
foo: 'bar'
}
},
},
webSocketImpl: ws,
}).request(operation)
// Instead of using `forward()` of Apollo link, we directly use websocketLink's request method
})
const httpLink = setContext((_graphqlRequest, { graphqlContext }) => {
return {
headers: {
authorization: jwt.sign(graphqlContext.user, process.env.SUPER_SECRET),
},
}
}).concat(new HttpLink({
uri,
fetch,
}))
const link = split(
operation => {
const definition = getMainDefinition(operation.query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink, // <-- Executed if above function returns true
httpLink, // <-- Executed if above function returns false
)
const schema = await introspectSchema(link)
const executableSchema = makeRemoteExecutableSchema({
schema,
link,
})
const server = new ApolloServer({
schema: mergeSchemas({ schemas: [ executableSchema, /* ...anotherschemas */] }),
context: ({ req, connection }) => {
let authorization;
if (req) { // when query or mutation is requested by http
authorization = req.headers.authorization
} else if (connection) { // when subscription is requested by websocket
authorization = connection.context.authorization
}
const token = authorization.replace('Bearer ', '')
return {
user: getUserFromToken(token),
}
},
}) |
Folding into #1316 |
Reopening this issue to track progress on an example demonstrating existing functionality and documenting any gaps. |
Is this really a documentation issue? |
See: #864 (comment) Replace mergeSchemas with stitchSchemas and makeRemoteExecutableSchema with wrapSchema and link with executor via linkToExecutor. I am under the assumption that the flow works just everyone would be better off with a canonical example within the repository. |
I just tried this code and it doesn't seem to be working. This is my current code: import fetch from 'cross-fetch'
import { introspectSchema, makeRemoteExecutableSchema } from 'apollo-server'
import { HttpLink } from 'apollo-link-http'
import { WebSocketLink } from 'apollo-link-ws'
import { ApolloLink, split } from 'apollo-link'
import { setContext } from 'apollo-link-context'
import { getMainDefinition } from 'apollo-utilities'
import ws from 'ws'
export const getRemoteSchema = async ({ uri, subscriptionsUri }) => {
const wsLink = new ApolloLink((operation) => {
// This is your context!
const context = operation.getContext().graphqlContext
// Create a new websocket link per request
return new WebSocketLink({
uri: subscriptionsUri,
options: {
reconnect: true,
connectionParams: { // give custom params to your websocket backend (e.g. to handle auth)
headers: { authorization: context?.authorization || null },
},
},
webSocketImpl: ws,
}).request(operation)
// Instead of using `forward()` of Apollo link, we directly use websocketLink's request method
})
const httpLink = setContext((_graphqlRequest, { graphqlContext }) => {
return {
headers: { authorization: graphqlContext?.authorization || null },
}
}).concat(new HttpLink({
uri,
fetch,
}))
const link = split(
(operation) => {
const definition = getMainDefinition(operation.query)
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription'
},
wsLink, // <-- Executed if above function returns true
httpLink // <-- Executed if above function returns false
)
const schema = await introspectSchema(link)
const executableSchema = makeRemoteExecutableSchema({ schema, link })
return executableSchema
} I'm getting
This seems to be coming from the call to
And when I remove the |
Well, I don't know what was wrong with my environment, but the above code works now ¯\_(ツ)_/¯ |
Closing. It would be nice to have an examples directory with complex examples like this and caching, etc, but for now i'll close to keep the issues tracker clean. |
We have implemented schema stitching where GraphQL server fetches schema from two remote servers and stitches them together. Everything was working fine when we were only working with Query and Mutations, but now we have use-case where we even need to stitch Subscriptions and remote schema has auth implemented over it.
We are having hard time figuring out on how to pass authorization token received in connectionParams from client to remote server via gateway.
This is how we are introspecting schema:
API Gateway code:
Is there any way for achieving this using graphql-tools? If not, can we create a feature request for same?
The text was updated successfully, but these errors were encountered: