Where do I add this websocket code in the new nuxt.js setup since it does not have server? - vue.js

I am using the new version of Nuxt which does not come with a server folder
In the old version, you had a server folder and an index.js which contained the app
I want to add the websocket WS library with the new version of NuxtJS
The library requires an instance of server which you create by calling http.createServer(app) meaning an instance of the running express app. You would use this server instance now to listen to 3000 in the index.js file. Also create a sessionHandler which you obtain by calling session({}) with the express-session library
const WebSocket = require('ws')
function websocket({ server, sessionHandler, logger }) {
// https://github.com/websockets/ws/blob/master/examples/express-session-parse/index.js, if you pass a server instance here 'upgrade' handler will crash
const wss = new WebSocket.Server({ noServer: true })
function noop() {}
function heartbeat() {
this.isAlive = true
}
wss.on('connection', (ws, request, client) => {
ws.isAlive = true
ws.on('pong', heartbeat)
ws.on('message', (msg) => {
logger.info(`Received message ${msg} from user ${client}`)
ws.send(true)
})
})
server.on('upgrade', (request, socket, head) => {
sessionHandler(request, {}, () => {
logger.info(`${JSON.stringify(request.session)} WEBSOCKET SESSION PARSED`)
wss.handleUpgrade(request, socket, head, (ws) => {
wss.emit('connection', ws, request)
})
})
})
// TODO use a setTimeout here instead of a setInterval
setInterval(function ping() {
// wss.clients => Set
wss.clients.forEach(function each(ws) {
if (ws.isAlive === false) return ws.terminate()
ws.isAlive = false
ws.ping(noop)
})
}, 30000)
return wss
}
module.exports = websocket
Does anyone know how I can make this work on the new Nuxt setup without the server folder

You can create a custom module and use nuxt hooks to get a server instance on listen event.
Create modules/ws.js:
const WebSocket = require('ws')
const wss = new WebSocket.Server({ noServer: true })
wss.on('connection', ws => {
ws.on('message', message => {
console.log('received: %s', message);
})
ws.send('Hello')
})
export default function () {
this.nuxt.hook('listen', server => {
server.on('upgrade', (request, socket, head) => {
wss.handleUpgrade(request, socket, head, ws => {
wss.emit('connection', ws);
})
})
})
}
And register the module in nuxt.config.js:
export default {
modules: [
'~/modules/ws'
]
}
In your case you could create a module directory instead of a single file to store multiple related files.
Create modules/ws/index.js
const websocket = require('./websocket') // this is your file
export default function () {
this.nuxt.hook('listen', server => {
websocket({
server,
sessionHandler (request, _, cb) { // example
cb()
},
logger: console // example
})
})
}
Then copy your file to modules/ws/websocket.js. You can use module.exports which is CommonJS format or change it into ES Module format, Nuxt(Webpack) can handle that.
In your code I notice that ws.send(true) cause an error TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received type boolean (true) which basically mean you cannot send boolean.

Related

How to access object on Vue front end from axios proxy

I have a locally hosted mongodb database with mongoose, express, axios, and a Vue front end. Right now I'm trying to access a single object from an exported array, but I'm missing the mark and getting "undefined" as the result.
vue.config.js:
module.exports = {
devServer: {
proxy: 'http://localhost:3000',
}
}
here's the front end Vue script meant to use the objects:
import axios from 'axios';
export default {
name: 'Game',
data () {
return {
pages: [],
currentPage: {},
pageTitle: "",
pageText: "",
options: [],
}
},
created () {
this.getPages();
},
methods: {
async getPages() {
try {
let res = await axios.get('/api/pages');
this.pages = res.data;
console.log(this.pages);
this.currentPage = this.pages[0];
console.log(this.currentPage);
return true;
} catch (error) {
console.log(error);
}
},
my "get" endpoint in pages.js:
router.get('/', async (req, res) => {
try {
let pages = await Page.find();
res.send({pages: pages}); //send result of search for pages as list of pages called "pages"
} catch (error) {
console.log(error);
res.sendStatus(500); //500 = server could not fulfill request
}
});
the route in server.js:
const pages = require('./routes/pages');
app.use('/api/pages', pages);
app.listen(3000, () => console.log('Server listening on port 3000!'));
module.exports = app;
and here's the console output, with the "pages" object from vue's data property and the "currentPage" that's supposed to be at pages[0] (printed to console in earlier example):
I can access the api at 'localhost:3000/api/pages' just fine, but how do I break into that array and access the first page object? I want to get an object from the list axios fetches from mongoose, then hold that object in a variable so I can access it's properties. The whole "pages > [[Target]] > pages > [ ]" is part of the problem I'm sure, but I don't know what to tell the code to open it.
Whoops! I realized my mistake. In pages.js I should have sent "res.send(pages);" After a whole couple days too XD

Serving public and private ports using Nestjs

I'm building a that aims to serve a mobile application. Besides serving the client, it will have several back-office functionalities.
We are using swagger and we do want to be able to access the swagger docs of our back-office endpoints. However, we do not want to expose all of our endpoints publicly.
Assuming that having all endpoints public is a bad option one solutions we are thinking of is letting our server serve two ports, and then only exposing one port to the public. We have created a small sample repo that that serves a client module and a back-office module on two different ports.
The main.ts looks like the following:
import { NestFactory } from '#nestjs/core';
import { ClientModule } from './modules/client/client.module';
import * as express from 'express';
import * as http from 'http';
import {ExpressAdapter} from '#nestjs/platform-express';
import { BackOfficeModule } from './modules/backoffice/backoffice.module';
import { SwaggerModule, DocumentBuilder } from '#nestjs/swagger';
async function bootstrap() {
const clientServer = express();
const clientApp = await NestFactory.create(
ClientModule,
new ExpressAdapter(clientServer),
);
const clientOptions = new DocumentBuilder()
.setTitle('ClientServer')
.setDescription('The client server API description')
.setVersion('1.0')
.addTag('client')
.build();
const clientDocument = SwaggerModule.createDocument(clientApp, clientOptions);
SwaggerModule.setup('api', clientApp, clientDocument);
await clientApp.init();
const backOfficeServer = express();
const backOfficeApp = await NestFactory.create(
BackOfficeModule,
new ExpressAdapter(backOfficeServer),
);
const backOfficeOptions = new DocumentBuilder()
.setTitle('BackOffice')
.setDescription('The back office API description')
.setVersion('1.0')
.addTag('backOffice')
.build();
const backOfficeDocument = SwaggerModule.createDocument(backOfficeApp, backOfficeOptions);
SwaggerModule.setup('api', backOfficeApp, backOfficeDocument);
await backOfficeApp.init();
http.createServer(clientServer).listen(3000); // The public port (Load balancer will route traffic to this port)
http.createServer(backOfficeServer).listen(4000); // The private port (Will be accessed through a bastian host or similar)
}
bootstrap();
Another option would be to create a bigger separation of the codebase and infrastructure, however as this is a very early stage we feel that is unnecessary.
Our question to the Nest community is thus, has anyone done this? If so, what is are your experience? What are the drawbacks to separating our backend code like this?
Disclaimer: this solution is for express+REST combination.
Routing
Even thought nestjs can't separate controller's based on port, it can separate them based on host. Using that, you can add a reverse proxy in front of your application, that modifies the host header based on the port. Or, you can do that in an express middleware, to make things even more simpe. This is what I did:
async function bootstrap() {
const publicPort = 3000
const privatePort = 4000
const server = express()
server.use((req, res, next) => {
// act as a proper reverse proxy and set X-Forwarded-Host header if it hasn't been set
req.headers['x-forwarded-host'] ??= req.headers.host
switch (req.socket.localPort) {
case publicPort:
req.headers.host = 'public'
break
case privatePort:
req.headers.host = 'private'
break
default:
// this shouldn't be possible
res.sendStatus(500)
return
}
next()
})
const app = await NestFactory.create(AppModule, new ExpressAdapter(server))
http.createServer(server).listen(publicPort)
http.createServer(server).listen(privatePort)
}
Controllers:
#Controller({ path: 'cats', host: 'public' })
export class CatsController {...}
#Controller({ path: 'internal' host: 'private' })
export class InternalController {...}
Alternatively, you can simplify by creating your own PublicController and PrivateController decorators:
// decorator for public controllers, also sets guard
export const PublicController = (path?: string): ClassDecorator => {
return applyDecorators(Controller({ path, host: 'public' }), UseGuards(JwtAuthGuard))
}
// decorator for private controllers
export const PrivateController = (path?: string): ClassDecorator => {
return applyDecorators(Controller({ path, host: 'private' }))
}
#PublicController('cats')
export class CatsController {...}
#PrivateController('internal')
export class InternalController {...}
Swagger
For swagger, SwaggerModule.createDocument has an option "include", which accepts a list of modules to include in the swagger docs. With a bit of effort we can also turn the swagger serving part into an express Router, so both the private and public swagger can be served on the same path, for the different ports:
async function bootstrap() {
const publicPort = 3000
const privatePort = 4000
const server = express()
server.use((req, res, next) => {
// act as a proper reverse proxy and set X-Forwarded-Host header if it hasn't been set
req.headers['x-forwarded-host'] ??= req.headers.host
switch (req.socket.localPort) {
case publicPort:
req.headers.host = 'public'
break
case privatePort:
req.headers.host = 'private'
break
default:
// this shouldn't be possible
res.sendStatus(500)
return
}
next()
})
const app = await NestFactory.create(AppModule, new ExpressAdapter(server))
// setup swagger
let publicSwaggerRouter = await createSwaggerRouter(app, [CatsModule])
let privateSwaggerRouter: await createSwaggerRouter(app, [InternalModule])
server.use('/api', (req: Request, res: Response, next: NextFunction) => {
switch (req.headers.host) {
case 'public':
publicSwaggerRouter(req, res, next)
return
case 'private':
privateSwaggerRouter(req, res, next)
return
default:
// this shouldn't be possible
res.sendStatus(500)
return
}
})
http.createServer(server).listen(publicPort)
http.createServer(server).listen(privatePort)
}
async function createSwaggerRouter(app: INestApplication, modules: Function[]): Promise<Router> {
const swaggerConfig = new DocumentBuilder().setTitle('MyApp').setVersion('1.0').build()
const document = SwaggerModule.createDocument(app, swaggerConfig, { include: modules })
const swaggerUi = loadPackage('swagger-ui-express', 'SwaggerModule', () => require('swagger-ui-express'))
const swaggerHtml = swaggerUi.generateHTML(document)
const router = Router()
.use(swaggerUi.serveFiles(document))
.get('/', (req: Request, res: Response, next: NextFunction) => {
res.send(swaggerHtml)
})
return router
}
That's ok, but if you want to run two servers on 1 host, I would recommend to create two files like main-client.ts and main-back-office.ts and run them in different processes, because in that case failures of one server would not affect work of another.
Also if you are not run this in Docker I would suggest tools like forever, pm2, supervisor or my own very small library workers-cluster
If you run it in Docker and don't want big refactoring, I would recommend to create
single Dockerfile with running different CMD or ENTRYPOINT commands
The NestJS docs cover how to let one server serve multiple ports:
https://docs.nestjs.com/faq/multiple-servers#multiple-simultaneous-servers
The following recipe shows how to instantiate a Nest application that listens on multiple ports (for example, on a non-HTTPS port and an HTTPS port) simultaneously.
const httpsOptions = {
key: fs.readFileSync('./secrets/private-key.pem'),
cert: fs.readFileSync('./secrets/public-certificate.pem'),
};
const server = express();
const app = await NestFactory.create(
ApplicationModule,
new ExpressAdapter(server),
);
await app.init();
http.createServer(server).listen(3000);
https.createServer(httpsOptions, server).listen(443);

Receive WebSockets data from vuex and Vue-native-websocket plugin

I am currently using the Quasar V1 framework which includes Vue and Vuex.
Today I was looking at this plugin:
https://www.npmjs.com/package/vue-native-websocket/v/2.0.6
I am unsure on how to setup this plugin and make it work and would require a little bit of help to make sure I am doing this right as it will be the first time I use WebSockets with Vue.
I have first installed vue-native-websocket via npm and created a boot file called src\boot\websocket.js
via this command:
npm install vue-native-websocket --save
websocket.js
import VueNativeSock from 'vue-native-websocket';
export default async ({ Vue }) => {
Vue.use(VueNativeSock, 'wss://echo.websocket.org', {
reconnection: true,
reconnectionAttempts: 5,
reconnectionDelay: 3000
});
};
In Quasar v1, I have then created a module called "websockets" in:
src\store\websockets
This module has:
actions.js
getters.js
index.js
mutations.js
state.js
I need to use the websocket with format: 'json' enabled
My question is:
Let's say I have a page where I would like my websocket connection to be created and receive the live data, shall I do this?
Code for the module:
websockets/mutations.js:
export function SOCKET_ONOPEN (state, event) {
let vm = this;
vm.prototype.$socket = event.currentTarget;
state.socket.isConnected = true;
}
export function SOCKET_ONCLOSE (state, event) {
state.socket.isConnected = false;
}
export function SOCKET_ONERROR (state, event) {
console.error(state, event);
}
// default handler called for all methods
export function SOCKET_ONMESSAGE (state, message) {
state.socket.message = message;
}
// mutations for reconnect methods
export function SOCKET_RECONNECT (state, count) {
console.info(state, count);
}
export function SOCKET_RECONNECT_ERROR (state) {
state.socket.reconnectError = true;
}
Code for the module:
websockets/state.js
export default {
socket: {
isConnected: false,
message: '',
reconnectError: false
}
};
But the issue now is in my vue page.
Let's say I would like to show only the data from the websocket that has a specific event, how do I call this from the vue page itself please? I am very confused on this part of the plugin.
What is very important for me to understand if how to separate the receive and send data.
ie: I may want to receive the list of many users
or I may want to receive a list of all the news
or I may add a new user to the database.
I keep hearing about channels and events and subscriptions......
From what I understand, you have to first subscribe to a channel(ie: wss://mywebsite.com/news), then listen for events, in this case I believe the events is simply the data flow from this channel).
If I am correct with the above, how to subscribe to a channel and listen for events with this plugin please, any idea?
If you had a very quick example, it would be great, thank you.
I have developed a chat application using Vue-native-websocket plugin. Here i am showing how you can register the pulgin in the vuex store and how to call it from your vue component.
Step 1: Define these methods in your index.js file
const connectWS = () => {
vm.$connect()
}
const disconnectWS = () => {
vm.$disconnect()
}
const sendMessageWS = (data) => {
if (!Vue.prototype.$socket) {
return
}
Vue.prototype.$socket.send(JSON.stringify(data))
}
Step 2: Write the socket state and mutations
SOCKET_ONOPEN (state, event) {
if (!state.socket.isConnected) {
Vue.prototype.$socket = event.currentTarget
state.socket.isConnected = true
let phone = state.config.selectedChatTicket.phone
sendMessageWS({type: WSMessageTypes.HANDSHAKE, data: {id: window.ACCOUNT_INFO.accId, phone: phone, agentId: USER_NAME}})
}
},
SOCKET_ONCLOSE (state, event) {
console.log('SOCKET_ONCLOSE', state, event)
state.socket.isConnected = false
Vue.prototype.$socket = null
},
// NOTE: Here you are getting the message from the socket connection
SOCKET_ONMESSAGE (state, message) {
state.data.chatCollection = updateChatCollection(state.data.chatCollection,message)
},
STEP 3 : Write Action, you can call it from your vue component
NOTE:: socket actions to connect and disconnect
WSConnect ({commit, state}) {
connectWS()
},
WSDisconnect ({commit, state}) {
disconnectWS()
},
STEP 4: Register the plugin in the end as it requires the store object
Vue.use(VueNativeSock, `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://www.example.com/socketserver`,
{ store: store, format: 'json', connectManually: true })
STEP 5: call your action from your vue component
buttonClick (rowData) {
const tickCount = this.ticketClickCounter
if (tickCount === 0) {
this.$store.dispatch('WSConnect')
} else {
this.$store.dispatch('WSDisconnect')
setTimeout(() => {
this.$store.dispatch('WSConnect')
}, 1000)
}
this.ticketClickCounter = tickCount + 1
},
Now you are connected to the socket
STEP 6: write a action method in your vuex file
sendChatMessageAction ({commit, state}, data) {
// NOTE: Here, you are sending the message through the socket connection
sendMessageWS({
type: WSMessageTypes.MESSAGE,
data: {
param1: abc,
param2: xyz,
param3: 123,
param4: $$$
}
})
},
STEP 7: you can define a input text box and on-enter evenlisterner you can call the action method
onEnter (event) {
if (event.target.value !== '') {
let newValue = {
param1: Date.now(),
param2: xyz,
param3: 123,
}
this.$store.dispatch('sendChatMessageAction', newValue) // Action
}
},

Storing REST response to indexedDB with Cycle.js

I'm in the middle of learninig Cycle.JS and ran into a challenge. I have a component that will get a result from an HTTP call and I'd like to persist this response in indexDB. However, I feel that the request for persistence is the responsibility of another component.
The questions I have are:
Is this a use case for a custom driver that persists HTTP responses to indexDB?
How does another component access the response stream for a request it did not make?
When I try to select the category from the HTTP source, nothing gets logged to the console. I'm using xstream, so the streams should be hot and I expect debug to output. What's going on here?
Below is my component that makes the HTTP call:
import { Feed } from './feed'
export function RssList ({HTTP, props}, feedAdapter = x => x) {
const request$ = props.url$
.map(url => ({
url: url,
method: 'GET',
category: 'rss'
}))
const response$ = HTTP
.select('rss')
.flatten()
.map(feedAdapter)
const vDom$ = response$
.map(Feed)
.startWith('')
return {
DOM: vDom$,
HTTP: request$
}
}
Here is my attempt at accessing the response at the app level:
export function main (sources) {
const urlSource = url$(sources)
const rssSink = rss$(sources, urlSource.value)
const vDom$ = xs.combine(urlSource.DOM, rssSink.DOM)
.map(([urlInput, rssList]) =>
<div>
{urlInput}
{rssList}
</div>
)
sources.HTTP.select('rss').flatten().debug() // nothing happens here
return {
DOM: vDom$,
HTTP: rssSink.HTTP
}
}
Selecting a category in the main (the parent) component is the correct approach, and is supported.
The only reason why sources.HTTP.select('rss').flatten().debug() doesn't log anything is because that's not how debug works. It doesn't "subscribe" to the stream and create side effects. debug is essentially like a map operator that uses an identity function (always takes x as input and outputs x), but with a logging operation as a side effect. So you either need to replace .debug() with .addListener({next: x => console.log(x)}) or use the stream that .debug() outputs and hook it with the operator pipeline that goes to sinks. In other words, debug is an in-between logging side effect, not a destination logging side effect.
Question #1: Custom HTTP->IDB Driver: It depends on the nature of the project, for a simple example I used a general CycleJS IDB Driver. See example below or codesandbox.io example.
Question #2: Components Sharing Streams: Since components and main share the same source/sink API you can link the output (sink) of one component to the input (source) of another. See example below or codesandbox.io example.
Question #3: debug and Logging: As the authoritative (literally) André Staltz pointed out debug needs to be inserted into a completed stream cycle, I.E., an already subscribed/listened stream.
In your example you can put debug in your RssList component:
const response$ = HTTP
.select('rss')
.flatten()
.map(feedAdapter)
.debug()
OR add a listener to your main example:
sources.HTTP.select('rss').flatten().debug()
.addListener({next: x => console.log(x)})
OR, what I like to do, is include a log driver:
run(main, {
DOM: makeDOMDriver('#app'),
HTTP: makeHTTPDriver(),
log: log$ => log$.addListener({next: log => console.log(log)}),
})
Then I'll just duplicate a stream and send it to the log sink:
const url$ = props.url
const http$ = url$.map(url => ({url: url, method: 'GET', category: 'rss'}))
const log$ = url$
return {
DOM: vdom$,
HTTP: http$,
log: log$,
}
Here's some example code for sending HTTP response to IndexedDB storage, using two components that share the data and a general IndexedDB driver:
function main(sources) {
const header$ = xs.of(div('RSS Feed:'))
const rssSink = RssList(sources) // input HTTP select and props
// output VDOM and data for IDB storage
const vDom$ = xs.combine(header$, rssSink.DOM) // build VDOM
.map(([header, rssList]) => div([header, rssList])
)
const idbSink = IdbSink(sources, rssSink.IDB) // output store and put HTTP response
return {
DOM: vDom$,
HTTP: rssSink.HTTP, // send HTTP request
IDB: idbSink.put, // send response to IDB store
log: idbSink.get, // get and log data stored in IDB
}
}
function RssList({ HTTP, props }, feedAdapter = x => x) {
const request$ = props.url$
.map(url => ({url: url, method: 'GET', category: 'rss'}))
const response$ = HTTP.select('rss').flatten().map(feedAdapter)
const idb$ = response$
const vDom$ = response$
.map(Feed)
.startWith(div('','...loading'))
return {
DOM: vDom$,
HTTP: request$,
IDB: { response: idb$ },
}
}
function Feed (feed) {
return div('> ' + feed)
}
function IdbSink(sources, idb) {
return {
get: sources.IDB.store('rss').getAll()
.map(obj => (obj['0'] && obj['0'].feed) || 'unknown'),
put: idb.response
.map(feedinfo => $put('rss', { feed: feedinfo }))
}
}
run(main, {
props: () => ({ url$: xs.of('http://lorem-rss.herokuapp.com/feed') }),
DOM: makeDOMDriver('#root'),
HTTP: makeHTTPDriver(),
IDB: makeIdbDriver('rss-db', 1, upgradeDb => {
upgradeDb.createObjectStore('rss', { keyPath: 'feed' })
}),
log: log$ => log$.addListener({next: log => console.log(log)}),
})
This is a contrived example, simply to explore the issues raised. Codesandbox.io example.

Relay subscriptions not working with react-native

I'm using Express Graphql server with react native and Relay. My device does connects to the subscription but it does not subscribe to it. Here's my index.js on the server
const subscriptionServer = SubscriptionServer.create(
{
execute,
subscribe,
schema,
onOperation: (message, params, webSocket) => {
console.log(params)
return params;
},
onConnect: () => {
// My device does connects
console.log("client connected")
}
},
{
server,
path: '/subscriptions'
},
);
app.use('/graphql', graphqlHTTP({
schema,
graphiql: true
}));
app.use('/graphiql', graphiqlExpress({
endpointURL: '/graphql',
subscriptionsEndpoint: `ws://127.0.0.1:8080/subscriptions`
}));
server.listen(PORT, ()=> {
console.log("Groceries running on port " + PORT)
console.log(
`subscriptions is now running on ws://localhost:${PORT}/subscriptions'}`
);
});
The resolver for subscription on the server, it was quite troublesome to figure out since everyone is using executable schema from apolloGraphql.
export default {
type: OrderEdges,
args: {
ShopId: {type: GraphQLID},
},
subscribe: withFilter(() => pubsub.asyncIterator('orderConfirmed'), (payload, variables) => {
console.log(payload)
console.log(variables)
return payload.orderConfirmed.node.ShopId == variables.ShopId;
}),
}
Now the react-native client. My subscription setup with relay environment.
const setupSubscriptions = (config, variables, cacheConfig, observer) => {
const query = config.text; //does console logs the query
const subscriptionClient = new SubscriptionClient(`ws://192.168.0.100:8080/subscriptions`, {reconnect:true});
subscriptionClient.request({query, variables}, (err, result) => {
console.log(err) // doesn't get call inside the request method
observer.onNext(data:result)
})
}
My subscription method,
export default function() {
const variables = {
ShopId: shop.getShop()[0].id
}
requestSubscription(
environment,
{
subscription,
variables,
onCompleted: (res, err) => {
console.log(res)
console.log(err)
},
updater: (store) => {...},
onError: error => console.error(error),
onNext: (response) => {console.log(response)}
});
}
the component where I'm calling to subscribe,
import subscription from '../../GraphQLQueries/subscriptions/orderConfirmed';
class OrdersBox extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
//initializing subscription
orderSubscriptions();
}
When the device starts the app, my device is connected to the web socket as I can see the console.log statement inside the onConnect method in SubscriptionServer. But when the payload is published after a mutation, the subscribe method doesn't get called. I can't seem to figure out what I'm doing wrong. Maybe it's some react-native specific config that I'm missing cuz everything seems to work fine when I test it on graphiql.
I can't find any example of react-native and relay subscriptions used with express graphql.
note: Everything is working when I use subscription with graphiql. But not with react-native and relay.
Thanks in advance guys
....
I wasn't returning the subscriptionClient.request method. Adding a return statement solved the problem. You don't have to return when using subscribe method in subscriptions-transport-ws#0.8.3. But version 0.9.1 replaces the subscribe function with request which does require it to return.
try:
function setupSubscription(config, variables, cacheConfig, observer) {
const query = config.text;
const subscriptionClient = new SubscriptionClient(websocketURL, {
reconnect: true
});
const client = subscriptionClient.request({ query, variables }).subscribe({
next: result => {
observer.onNext({ data: result.data });
},
complete: () => {
observer.onCompleted();
},
error: error => {
observer.onError(error);
}
});
return {
dispose: client.unsubscribe
};
}
subscriptions-transport-ws#0.9.1