How to do graphql subscriptions with Absinthe (Elixir) and Urql? - websocket

My idea was to build a client app using react and urql and a graphql api using elixir and absinthe but at the moment it looks as if these don't really play that well together.
Is there a way to actually use the Absinthe subscriptions with any other client than Apollo? I've tried to use urql but I fail with the ws connection getting the following error:
WebSocket connection to 'ws://localhost:4000/socket/websocket' failed:
Error during WebSocket handshake: Sent non-empty
'Sec-WebSocket-Protocol' header but no response was received
Only thing I've found so far which seems to be related to this issue is this library absinthe/socket-apollo-link (https://github.com/absinthe-graphql/absinthe-socket/tree/master/packages/socket-apollo-link) but it's obviously only for Apollo.
In my failed attempt I did the following:
import React from 'react'
import { Provider, Client, dedupExchange, fetchExchange, subscriptionExchange } from 'urql'
import { cacheExchange } from '#urql/exchange-graphcache'
import { SubscriptionClient } from 'subscriptions-transport-ws'
const DataProvider = ({ children }) => {
const cache = cacheExchange({})
const subscriptionClient = new SubscriptionClient('ws://localhost:4000/socket/websocket', {})
const client = new Client({
url: 'http://localhost:4000/api',
exchanges: [
dedupExchange,
cache,
fetchExchange,
subscriptionExchange({
forwardSubscription: operations => subscriptionClient.request(operations),
}),
],
})
return <Provider value={client}>{children}</Provider>
}
export default DataProvider
This 'subscriptions-transport-ws' I found from a tutorial and that mysterious '/websocket' at the end of the ws url was mentioned in some gh issue but it seems to work (before adding that to the end of the url there was no ws connection at all).

This doesn't directly answer the question, but in case someone ends up with the same confusion and as a response to your comment here:
I found from a tutorial and that mysterious '/websocket' at the end of the ws url was mentioned in some gh issue but it seems to work (before adding that to the end of the url there was no ws connection at all).
This is due to both longpoll and websocket being viable transports. You likely had your socket defined in your Endpoint with websocket: true which gave you that route. Running mix phx.routes | grep socket would have been a helpful command.

This is obviously a late reply, but I'll try my best anyway. It seems that Absinthe has an alternative client library that's different from subscriptions-transport-ws, notably it's here: https://github.com/absinthe-graphql/absinthe-socket/blob/master/packages/socket-apollo-link/src/createAbsintheSocketLink.js#L8
If you integrate with this library instead then presumably it'll work. So that means you likely have to integrate with PhoenixSocket instead, as shown here: https://github.com/absinthe-graphql/absinthe-socket/tree/master/packages/socket-apollo-link#examples

Related

When Setting Up ApolloGraphQL Persisted Queries, How To Support /GraphQL?

When I follow the ApolloGraphQL docs here:
https://www.apollographql.com/docs/apollo-server/performance/apq/#setup
It says that the Apollo Server automatically handles AQP requests with no changes. Yet, when my client calls:
http://localhost:4000/graphql
I get a 404 file not found.
Shouldn't that automatically be working on an apollo graphql server?
I didn't realize for localhost testing I need to set full uri.
const link = ApolloLink.from([
createPersistedQueryLink({ useGETForHashedQueries: true }),
createHttpLink({ uri: "http://localhost:4000/graphql" })
]);

Socket.io support for WSO2 API Manager Websocket?

Does inbuilt WebSocket API in WSO2 APIM manager supports socket.io/nodejs based endpoint?
I have socket.io based websocket server (in NodeJS) And socket.io javascript client, in between we are using WSO2 API manager for authenticating websocket connections. But WSO2 fails to connect backend server and gives nothing in response.
I used access_token query parameter for passing Bearer token (from JavaScript client) and it seems working as no error. But WSO2 does not forward requests to endpoint.
Same works fine with native WebSocket server-client.
You can add the following logs to <AM_HOME>/repository/conf/log4j.properties to debug the issue further.
For APIM 3.x.x you can change it according to the log4j2 and add to log4j2.properties
log4j.logger.org.wso2.carbon.inbound.endpoint.protocol.websocket.InboundWebsocketSourceHandler=DEBUG log4j.logger.org.wso2.carbon.inbound.endpoint.protocol.websocket.InboundWebsocketResponseSender=DEBUG
log4j.logger.org.wso2.carbon.websocket.transport.WebSocketClientHandler=DEBUG
log4j.logger.org.wso2.carbon.websocket.transport.WebsocketTransportSender=DEBUG
Previous answer was for socket.io JS client, here is the same hack for socket.io python client.
Modify client.py file from /home/user/.local/lib/python3.6/site-packages/engineio
line no. 515
change this,
return ('{scheme}://{netloc}/{path}/?{query}'
'{sep}transport={transport}&EIO=3').format(...
to this,
return ('{scheme}://{netloc}/{path}?{query}'
'{sep}transport={transport}&EIO=3').format(...
Now, instead of directly modifying actual flow we can use conditional parameter.
if self.noslash == 'true':
return ('{scheme}://{netloc}/{path}?{query}'
'{sep}transport={transport}&EIO=3').format(...
else return original statement.
noslash parameter can be fetched from connect() function in both engineio and socketio library of python.
def connect(self, url, headers={}, transports=None,
engineio_path='engine.io', noslash=None):
self.noslash = noslash
Here is the sample connection string for python-socketio
sio.connect('http://localhost:9099', headers={'Authorization':'Bearer 90e8sf10-3s1w-495f-b20d-5a009f63193v'}, transports=['websocket'], socketio_path='/livefeed/v1', noslash='true')
finally I get rid of this issue. Yes, WSO2 APIM websocket supports socket.io js library that will be the first answer to my own query. More on that, here are some findings.
The actual reason for issue was request URL pattern used by socket.io library and WSO2 APIM. Socket.io JS library make the final URL with / at the end of resource (reference), which is not accepted by query separation logic used by WSO2.
So in simple, this is acceptable
ws://localhost:9099/livefeed/v1?EIO=4&transport=websocket
but not this,
ws://localhost:9099/livefeed/v1/?EIO=4&transport=websocket
Solution:
After few discussions with WSO2 team, it was clear that it is impossible to implement changes at their end. So little hack in socket.io.js file worked for me.
line number - 2535
changed this,
_this.opts.path = _this.opts.path.replace(/\/$/, "") + "/";
to this,
if(typeof opts.noslash!== 'undefined' && opts.noslash== "true"){
_this.opts.path = _this.opts.path.replace(/\/$/, "");
}else{
_this.opts.path = _this.opts.path.replace(/\/$/, "") + "/";
}
and created socket using extra argument.
var socket = io.connect("http://localhost:9099?access_token=90e8sf10-3s1w-495f-b20d-5a009f63193v", { transports: ['websocket', 'polling'], path: '/livefeed/v1', noslash: 'true' });
I know this is not a actual solution, but it worked for me.

Caching the axios httpService in NestJS

Within our application we use the Axios HttpService to do some request to a third-party api.
Because the amount of data returned bij de api is so huge, we would like to cache the responses.
In the docs is wasn't able to find some examples of how to do this.
I'm currently doing this as follows:
#Module({
imports: [
HttpModule,
CacheModule.register({
ttl: 15,
store: redisStore,
host: 'localhost',
port: 6379,
})
]
})
export class AppModule {}
I register the CacheModule globally.
Then import it in the module where i need it.
In the service where i use the third-party api, i create an interceptor where i go and cache the reponses. Very crude and just for testing.
constructor(private readonly httpService: HttpService,
private readonly cache: CacheStore) {
httpService.axiosRef.interceptors.response.use((response) => {
cache.set(response.request.url, response.data);
return response;
}, error => Promise.reject(error));
}
First of all this doesn't run, because the CACHE_MANAGER can't be imported into the CacheModule, for some reason.
Second this is a more a Node.js way of creating such interceptors and not the NestJS way.
But is this a way to move forward or is there a more effecient way and if yes, what way is that?
The CacheModule is not the right tool here, as it means to cache ingoing requests (the requests your service receive, so it doesn't proceed them again and send back a cache result).
What you're trying to do is caching the outgoing requests (the one your service makes to 3rd-party services). For mysterious reasons, I couldn't find it documented in NestJS documentation either, but here's how you can go:
As you're using Axios, you can implement caching using the axios-cache-adapter npm package.
npm install --save axios-cache-adapter
Then you need to create an adapter (preferably in the constructor of your service, see notes below):
const cache = setupCache({
maxAge: 3600 * 1000, // 60 minutes
});
and provide this adapter as part of the AxiosRequestConfig alongside your request:
const config = {
adapter: this.cache.adapter,
};
this.httpService.get( url, config );
You should be good with some caching now!
IMPORTANT NOTES:
caching should be used, and will likely work, only on GET requests
use the setupCache function only once (in the constructor of your service for instance); if you create one cache object for every request, this cache will be empty everytime, defeating the purpose of it

should I use express-graphql or appolo-server-graphql

I'm working on an express backend that use graphql ajd I don't know if I need to use the express-graphql or appolo-server-graphql lib.
Thank you for your help!
I suggest using apollo-server-express over express-graphql. They are very similar, but apollo-server-express has more bells and whistles all while having a simpler and clearer API IMO.
The biggest improvement in apollo-server-express, for me, is the playground: https://github.com/prisma/graphql-playground
The playground is better than express-graphql's graphiql for several reasons, but one big one is that it allows you to put HTTP headers in the request, which is more appropriate for handling session.
www.graphqlbin.com will allow you to use the playground on any endpoint which does not have cors. If you have cors, then you will need to run playground directly from your server.
Here is a sample of code to get you started:
const { ApolloServer } = require('apollo-server-express')
const graphqlServer = new ApolloServer({
schema,
introspection: true,
playground: true,
})
graphqlServer.applyMiddleware({
app
})

How to change websocket url in graphql-playground (subscriptions)

I wanted to change the graphql websocket end point inside graphql, anyone know how to do this?
by default it pings
wss://localhost/graphql
I need to change it to pusher url
thanks :-)
If you are running a standalone instance of GraphQL Playground, the URL is passed directly to the component as a prop:
<Playground
endpoint="http://localhost/graphql"
subscriptionEndpoint="wss://localhost/graphql"
/>
If you're using apollo-server, the endpoint URL should be derived from the subscriptionsPath, but it can also be set directly in the config:
const server = new ApolloServer({
typeDefs,
resolvers,
playground: {
subscriptionEndpoint: 'wss://localhost/graphql',
},
});
EDIT:
It doesn't appear there's a way to configure the desktop client with a specific subscription URL, unless you're using it with a local repo that contains a .graphqlconfig. In that case, you can provide additional information about your environment, including the subscription url, in the config file as outlined here.

Resources