Reputation: 659
I'm trying to enable usage of socket.io acknowledgment callbacks in Nest.js WebSocketGateways.
I'd like to be able to emit this:
socket.emit('event', 'some data', function (response) {
//do something
})
And use the message handler like this:
@SubscribeMessage('event')
onStart(client, data, ack) {
//Do stuff
ack('stuff completed');
}
According to this nestjs/nest GitHub issue issue there is no support for it in the library so you'd have to build your own websocket adapter. I tried it but am not sure how to do it exactly. I guess I need to do something special in the bindMessageHandlers
function but my attempts have been in vain.
This is the bindMessageHandlers
implementation in the default socket.io adapter bundled in the framework:
public bindMessageHandlers(
client,
handlers: MessageMappingProperties[],
process: (data: any) => Observable<any>,
) {
handlers.forEach(({ message, callback }) =>
Observable.fromEvent(client, message)
.switchMap(data => process(callback(data)))
.filter(result => !!result && result.event)
.subscribe(({ event, data }) => client.emit(event, data)),
);
}
Does anyone have any pointers on how I would go about implementing this?
Upvotes: 19
Views: 12500
Reputation: 478
On client side:
socket.emit('my-message', payload, (response) => {
console.log(response)
})
On server side, just return response data:
@SubscribeMessage('my-message')
async myMessage (client, payload) {
console.log(payload)
return {
... your response data here
}
}
Upvotes: 1
Reputation: 1776
Simply use the return statement from SubscribeMessage
// server
@SubscribeMessage('message')
async onMessage(
client: Socket, query: string
) {
try {
console.log(query)
return 'hello'
} catch (e) {
// ...
}
}
on the client side use as third param a function
// client
this.socket.emit('message', query, (res) => {
console.log(res); // should log 'hello'
});
Upvotes: 8
Reputation: 659
Update: Support for acknowledgements have been added in Nest 5.0.
If the socket provider passes multiple arguments into the SubscribeMessage
handler the request
parameter will be an array with these arguments.
For example with the default socket.io-adapter:
@SubscribeMessage('event')
async onEvent(client, request) {
let data = request[0]
let ack = request[1] //the acknowledgement function
}
One issue is that if you don't provide a acknowledgement function request
will not be an array, and just be the data
object.
In one of my current projects I've worked around this by creating a helper function that extracts the data and acknowledgement function, or creates a placeholder, which means that I can always call the ack function without considering it's existence:
export function extractRequest (req: any): { data: any, ack?: Function } {
if (Array.isArray(req)) {
const [data, ack] = req
return { data, ack }
} else {
return { data: req, ack: () => {} }
}
}
Old answer: The current status is that this is not possible without modifying Nest source. It will be added in the upcoming 5.0 release. I'll update this answer with an example when it is released.
Source: https://github.com/nestjs/nest/issues/581
Upvotes: 3
Reputation: 382
You can try this module: https://www.npmjs.com/package/nestjs-socket-handlers-with-ack. It calls acknowledgement function under the hood, you only need to return some value or throw error. Hope it helps
Upvotes: 1
Reputation: 3009
After short time researching NestJS. Here is my solution.
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── common
│ └── adapters
│ └── ws-adapter.ts
├── events
│ ├── events.gateway.ts
│ └── events.module.ts
└── main.ts
main.ts
File
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { WsAdapter } from './common/adapters/ws-adapter.ts';
import * as cors from 'cors';
let corsOptions = {
origin: 'http://nestjs.test',
credentials: true
}
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useWebSocketAdapter(new WsAdapter(3000));
app.use(cors(corsOptions));
await app.listen(4000);
}
bootstrap();
Because when we using WebSocket Adapter we can't use same port with NestJS application anymore.
common\adapters\ws-adapter.ts
File
import * as WebSocket from 'ws';
import { WebSocketAdapter } from '@nestjs/common';
import { IoAdapter } from '@nestjs/websockets';
import { MessageMappingProperties } from '@nestjs/websockets';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/fromEvent';
import 'rxjs/add/observable/empty';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/filter';
export class WsAdapter extends IoAdapter {
public bindMessageHandlers(
client,
handlers: MessageMappingProperties[],
process: (data: any) => Observable<any>,
) {
handlers.forEach(({ message, callback }) => {
client.on('event', function (data, ack) {
console.log('DATA', data)
ack('woot')
})
Observable.fromEvent(client, message)
.switchMap(data => process(callback(data)))
.filter(result => !!result && result.event)
.subscribe(({ event, data }) => client.emit(event, data))
});
}
}
My Client side source code
socket.emit('event', {data: 'some data'}, function (response) {
console.log('RESPONSE', response)
});
socket.on('event', function(data) {
console.log('ON EVENT', data);
});
And here is my result
Hope this help!!
Upvotes: 5