Deno: How to use WebSocket with oak? - websocket

As Deno was released last Wednesday, I tried to play with it and redo the little example Chat App, I tried this:
import { Application, Router, send } from 'https://deno.land/x/oak/mod.ts';
import { listenAndServe } from 'https://deno.land/std/http/server.ts'
const app = new Application();
const router = new Router();
router
.get('/ws', handleSocket);
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: HTTP_PORT });
app.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std/ws/mod.ts'
import { v4 } from 'https://deno.land/std/uuid/mod.ts'
const users = new Map<string, WebSocket>()
export const handleSocket = async (ctx: any) => {
if (acceptable(ctx.request.serverRequest)) {
const { conn, r: bufReader, w: bufWriter, headers } = ctx.request.serverRequest;
const socket = await acceptWebSocket({
conn,
bufReader,
bufWriter,
headers,
});
await socketEventHandlers(socket);
} else {
throw new Error('Error when connecting websocket');
}
}
...
export const socketEventHandlers = async (ws: WebSocket): Promise<void> => {
// Register user connection
const userId = v4.generate()
users.set(userId, ws)
await broadcast(`> User with the id ${userId} is connected`)
// Wait for new messages
for await (const event of ws) {
const message = typeof event === 'string' ? event : ''
await broadcast(message, userId)
// Unregister user conection
if (!message && isWebSocketCloseEvent(event)) {
users.delete(userId)
await broadcast(`> User with the id ${userId} is disconnected`)
}
}
}
socket.ts
The websocket connection works perfectly with the import { listenAndServe } from 'https://deno.land/std/http/server.ts'
, but with the code above I got errors like WebSocket connection to 'ws://localhost:3000/ws' failed: Invalid frame header.
Does anybody have any tips to solve it? Thx ;)

TL;DR - This has been updated since answer was accepted and is much simpler now.
router.get('/ws', async ctx => {
const sock = await ctx.upgrade();
handleSocket(sock);
});
Credit https://github.com/oakserver/oak/pull/137

The issue happens because you're using the wrong version of the libraries. Always use versioned URLs in Deno.
For Deno 1.0.0, you'll need to use oak v4.0.0 & std v0.51.0
app.ts
import { Application, Router, send } from 'https://deno.land/x/oak#v4.0.0/mod.ts';
socket.ts
import { WebSocket, acceptWebSocket, isWebSocketCloseEvent, acceptable } from 'https://deno.land/std#0.51.0/ws/mod.ts'
import { v4 } from 'https://deno.land/std#0.51.0/uuid/mod.ts'
Once you make those changes, you'll be able to connect correctly to the WebSocket Server.
const ws = new WebSocket("ws://127.0.0.1:8080/ws")
ws.onopen = function () {
ws.send('OAK is working!')
}

Related

RXStomp WebSocket Client not receiving data sometimes

Here is my configuration for RXStomp on frontend;
import { RxStompConfig } from '#stomp/rx-stomp';
import { environment } from '../../../environments/environment';
export const myRxStompConfig: RxStompConfig = {
reconnectDelay: 20000,
debug: (msg: string): void => {
if (!environment.production) {
console.log(msg);
}
},
};
Here is the code for the rxStompService;
import { Injectable } from "#angular/core";
import { RxStomp } from "#stomp/rx-stomp";
import SockJS from "sockjs-client";
import { environment } from "../../../environments/environment";
import { myRxStompConfig } from "../configurations/rx-stomp.config";
#Injectable({
providedIn: "root",
})
export class RxStompService extends RxStomp {
public currentRetry = 0;
public resetRetry() {
this.currentRetry = 0;
}
}
export function rxStompServiceFactory() {
const rxStomp = new RxStompService();
myRxStompConfig.webSocketFactory = function () {
return new SockJS(`${environment.baseUrl}/public/chatsocket`);
};
rxStomp.resetRetry();
myRxStompConfig.beforeConnect = (): Promise<void> => {
return new Promise<void>((resolve, reject) => {
if (rxStomp.currentRetry <= 5) {
rxStomp.currentRetry++;
resolve();
}
});
};
rxStomp.configure(myRxStompConfig);
rxStomp.activate();
return rxStomp;
}
The websocket was working fine until a couple of days ago when the socket broke on the production server and then we changed the URL of the websocket on the backend from public/websocket to public/chatsocket. The socket gets connected and then I subscribe to the required channel using
this.rxStompService.watch(`/user/${id}/message`).subscribe((message) => {
console.log(message.body);
};
The messages send correctly on the backend (Springboot) using this code;
private void sendWebMessageToConversation(MessageResponseDto messageResponseDto, String destinationId) {
try{
simpMessagingTemplate.convertAndSendToUser(destinationId, "/message", messageResponseDto);
}
catch (Exception e){
logger.error(e.getMessage(), e);
}
}
After debugging we found that the message gets filtered correctly and sent to all the users in a particular conversation via simpMessagingTemplate.convertAndSendToUser but the message does not get received on the frontend socket client RXStomp, even though the socket connection is established and the client is subscribed/watching the correct channel.
This feature works correctly on all of the test environments but for some reason there is an inconsistency when working on the production server. Sometimes one user subscribed to the channel will receive messages but the other won't. Or both users won't receive messages, or the feature works correctly and both receive messages properly. How do I fix this to make it work correctly all the time?

Setting up graphql yoga with websockets in nextjs api

In graphql yoga documentation, I found this example for using graphql yoga with websockets but it's in nodejs environment. How can I setup a server in nextjs api using this example? All advice is appreciated, thanks.
import { createServer } from '#graphql-yoga/node'
import { WebSocketServer } from 'ws'
import { useServer } from 'graphql-ws/lib/use/ws'
async function main() {
const yogaApp = createServer({
graphiql: {
// Use WebSockets in GraphiQL
subscriptionsProtocol: 'WS'
}
})
// Get NodeJS Server from Yoga
const httpServer = await yogaApp.start()
// Create WebSocket server instance from our Node server
const wsServer = new WebSocketServer({
server: httpServer,
path: yogaApp.getAddressInfo().endpoint
})
// Integrate Yoga's Envelop instance and NodeJS server with graphql-ws
useServer(
{
execute: (args: any) => args.rootValue.execute(args),
subscribe: (args: any) => args.rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx)
const args = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe
}
}
const errors = validate(args.schema, args.document)
if (errors.length) return errors
return args
}
},
wsServer
)
}
main().catch((e) => {
console.error(e)
process.exit(1)
})

Getting list with the Elrond's esdt tokens (and balances) from an address (a wallet)

I want to get a list with the Elrond's esdt tokens (and balances) from an address (a wallet). I don't have any example, I tried things such as:
const { address, account } = useGetAccountInfo();
const objAddress = new Address(address);
// const data1 = getAccount(address);
const { network } = useGetNetworkConfig();
const proxy = new ProxyProvider(network.apiAddress);
proxy
.getAddressEsdtList(objAddress)
.then(({ returnData }) => {
console.log(returnData);
})
.catch((err) => {
console.error('Unable to call VM query', err);
});
But in the console I get "undefined".
Thanks a lot!
In the latest erdjs version I don't have any getAddressEsdtList function for the provider; what could work is to extend the network providers.
So we know we can extend the available network providers with other functions and we now need to make a request with an address and receive all the tokens that the address helds. api.elrond.com has an endpoint that does this at accounts/{address}/tokens.
As we have an endpoint to make a request to, we can now extend the ApiNetworkProvider, making use of doGetGeneric.
// CustomNetworkProvider.js
import { ApiNetworkProvider } from "#elrondnetwork/erdjs-network-providers";
export class CustomNetworkProvider extends ApiNetworkProvider {
async getTokens(address) {
return await this.doGetGeneric(`accounts/${address}/tokens`);
}
}
// index.js
import {CustomNetworkProvider} from "./CustomNetworkProvider.js";
const getProvider = () => {
return new CustomNetworkProvider('https://api.elrond.com', { timeout: 5000 });
}
const provider = getProvider();
const address = 'erd1rf4hv70arudgzus0ymnnsnc4pml0jkywg2xjvzslg0mz4nn2tg7q7k0t6p';
const tokens = await provider.getTokens(address);
console.log(tokens);

apollo explorer say "isTrusted": true when i try use Subscription

guys, I have a problem when I want to use a Subscription I'm facing this issue I don't find a solution in any place, I'm a GQL user and i decide to use Subscription to make real-time website but I'm facing a a this issue ,
hare is code
I'm trying to show apollo docs but I'm facing another issue(graphql doesn't find my resolver), so I try to use this pace of code in my mind it's work but the issue is it says
Unable to connect wss://localhost:4001 also I'm trying to use Unable to connect wss://localhost:4001/graphql and Unable to connect wss://localhost:4001/subscription, also i try this three way with using ws
// my resolver
const somethingChanged = () => {
// subscribe: () => {
console.log("subscribe")
pubsub.asyncIterator(SOMETHING_CHANGED_TOPIC)
// }
}
const makeId = () => {
// make id generator 36 symbols
let id = Math.random().toString(36).split(".")[1]
pubsub.publish(SOMETHING_CHANGED_TOPIC, {
somethingChanged: {
id,
},
})
return id
}
const resolvers = {
Subscription: {
somethingChanged,
},
Query: {
hello: () => "Hello world!",
},
Mutation: {
makeId,
},
}
// app.ts
import { createServer } from "http"
import express from "express"
import { ApolloServer, gql } from "apollo-server-express"
import { typeDefs } from "./graphql/schema"
import "colors"
import resolvers from "./graphql/root"
import connect from "./db/connect"
import { PubSub } from "graphql-subscriptions"
const SOMETHING_CHANGED_TOPIC = "something_changed"
require("dotenv").config()
export const pubsub = new PubSub()
// 1 creating one function for app
const startServer = async () => {
// 2 // declaring app as express
const app = express()
const httpServer = createServer(app)
setInterval(() => {
console.log(`Server was work ${Math.random().toString()}`.green)
}, 2000)
// middleware's
connect()
// 5
const apolloServer = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => req,
})
// 6
await apolloServer.start()
// 7
apolloServer.applyMiddleware({
app,
path: "/graphql",
})
// 8
httpServer.listen({ port: process.env.PORT || 4001 }, () =>
console.log(
`Server listening on localhost:4001${apolloServer.graphqlPath}`.blue
)
)
}
startServer()
I just want to test in apollo explorer but it doesn't work

Could anyone provide a fastapi websocket endpoint which could connect with the example given for RTK Query streaming updates

I'm trying to get my head around RTK Query as it applies to websockets. The example given is
import { createApi, fetchBaseQuery } from '#reduxjs/toolkit/query/react'
import { createEntityAdapter, EntityState } from '#reduxjs/toolkit'
import { isMessage } from './schemaValidators'
export type Channel = 'redux' | 'general'
export interface Message {
id: number
channel: Channel
userName: string
text: string
}
const messagesAdapter = createEntityAdapter<Message>()
export const api = createApi({
baseQuery: fetchBaseQuery({ baseUrl: '/' }),
endpoints: (build) => ({
getMessages: build.query<EntityState<Message>, Channel>({
query: (channel) => `messages/${channel}`,
transformResponse(response: Message[]) {
return messagesAdapter.addMany(
messagesAdapter.getInitialState(),
response
)
},
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved }
) {
const ws = new WebSocket('ws://localhost:8080')
try {
await cacheDataLoaded
const listener = (event: MessageEvent) => {
const data = JSON.parse(event.data)
if (!isMessage(data) || data.channel !== arg) return
updateCachedData((draft) => {
messagesAdapter.upsertOne(draft, data)
})
}
ws.addEventListener('message', listener)
} catch {}
await cacheEntryRemoved
ws.close()
},
}),
}),
})
export const { useGetMessagesQuery } = api
for the frontend. It looks as though the idea is to make a request to /messages/{channel} and on successful receipt and caching of these messages to connect to a websocket api. I'm struggling to create a fastapi app that connects with this example so I can figure out the workings. Does anyone have an example they might be willing to please share?

Resources