I have followed this tutorial
https://joyofcode.xyz/using-websockets-with-sveltekit
and implemented a basic websocket server that sends a message to the client when he connects.
I want to also send a message when someone submits a form.
// socketIo.ts.js
import { Server } from 'socket.io';
let io
export function injectSocketIO(server) {
io = new Server(server);
io.on('connection', (socket) => {
socket.emit('eventFromServer', 'Hello, World 👋')
});
console.log('SocketIO injected');
}
export function send(sms){
io.emit('eventFromServer', sms);
}
export function getIO(){return io}
//vite.config.ts
import { sveltekit } from '#sveltejs/kit/vite';
import type { UserConfig } from 'vite';
import { injectSocketIO } from './socketIo.js';
import { Server } from 'socket.io'
const webSocketServer = {
name: 'webSocketServer',
configureServer(server) {
injectSocketIO(server.httpServer);
},
}
const config: UserConfig = {
plugins: [sveltekit(), webSocketServer]
};
export default config;
//my form action
import { send } from './socketIo.ts'
export const actions = {
default: async({ request }) =>{
console.log(send('da'))
I'm getting this error
Cannot read properties of undefined (reading 'emit')
TypeError: Cannot read properties of undefined (reading 'emit')
at Module.send (/home/runner/sveltekit-cpp/socketIo.ts:17:5)
at default (/home/runner/sveltekit-cpp/src/routes/+page.server.ts:10:16)
at call_action (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:204:9)
at handle_action_request (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/actions.js:134:22)
at render_page (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/page/index.js:65:26)
at async resolve (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/index.js:247:17)
at async respond (file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/runtime/server/index.js:298:20)
at async file:///home/runner/sveltekit-cpp/node_modules/#sveltejs/kit/src/exports/vite/dev/index.js:418:22
I have tried exporting a send function from my socketIo file but it didn't work
Related
I am trying to create a basic game using next.js and node.js. When I run it I get multiple "connected" logs and when I check adapters -using io.sockets.adapter.rooms- I get even though there is just one client (One tab on chrome) connected:
io.sockets.adapter.rooms log:
Map(4) {
'tiTjbdGFj3EGgbwRAAAB' => Set(1) { 'tiTjbdGFj3EGgbwRAAAB' },
'DsCKvImAuh8H6Vr3AAAD' => Set(1) { 'DsCKvImAuh8H6Vr3AAAD' },
'FtJispkyF08rhHBDAAAF' => Set(1) { 'FtJispkyF08rhHBDAAAF' },
'PFO72' => Set(1) { 'FtJispkyF08rhHBDAAAF' }
}
app.js(server backend)
const app = require('express')();
const server = require('http').createServer(app);
const fs = require('fs');
const dirImage = './public/images';
const path = require( 'path' );
const io = require('socket.io')(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
}
});
server.listen(8000);
frontend react context socket connection:
import {createContext} from 'react';
import io from "socket.io-client";
export const socket = io("http://localhost:8000", {transports : ['websocket']});
export const SocketContext = createContext(socket);
Also creating socket variable in every page using useContext hook:
const socket = useContext(SocketContext);
When I run the server I still get 5 connections from one client
Connected!
Connected!
Connected!
Connected!
Connected!
What is the problem?
from your frontend app, you are using context api to connect socket but not using useEffect Hooks, So your connection line is running multiple time as how many time your app is re-rendering. I suggest to use useEffect hook with empty dependency array to run the connection only in first render.
I am attaching a sample code for you:
import io from "socket.io-client";
useEffect(() => {
socket = io.connect("http://localhost:8000");
return () => {
socket.disconnect();
};
}, []);
This will connect your socket connection first time and it will disconnect when the component ejects.
Should it be like this?
context file:
const SocketContext = createContext();
const SocketContextProvider = ({children}) =>{
const [socket,setSocket] = useState(null);
useEffect(()=>{
setSocket(io.connect("http://localhost:8000"));
},[]);
return (
// the Provider gives access to the context to its children
<SocketContext.Provider value={socket}>
{children}
</SocketContext.Provider>
);
}
export {SocketContext,SocketContextProvider}
I'm trying to run an action in my store that initializes an authenticated user. It checks to see if there is a JWT stored in local storage, then sets that JWT to the store. Since there is no local storage on the server side I use the Nuxt helper "process.client" in my middleware. When the named middleware runs when I visit an authorized route it's only running once on the server, and not running again on the client. I could add it as SSR false in the Nuxt config, but I don't want it to run on every page load... is there something I'm missing?
// middlware/check-auth.ts
import { Middleware } from '#nuxt/types'
import { useCustomerStore } from '#/store/customer'
const checkAuth: Middleware = ({ $pinia }) => {
const { initAuth } = useCustomerStore($pinia)
if (process.client) {
initAuth()
}
}
export default checkAuth
// pages/account/details
// page calling middleware
<script lang="ts">
import { defineComponent } from '#nuxtjs/composition-api'
export default defineComponent({
middleware: ['check-auth', 'auth']
})
</script>
<template>
<coming-soon></coming-soon>
</template>
<style module lang="postcss"></style>
// store/customer.ts
// action being called in middleware
actions: {
initAuth(): void {
const accessToken = localStorage.getItem('accessToken')
if (!accessToken) {
return
}
this.accessToken = accessToken
},
}
What is the proper way to pass the orm object to my controller functions. I want to be able to access the orm instance I created so that I can perform CRUD operations.
My server file:
import { MikroORM } from '#mikro-orm/core';
import { __prod__ } from './constants';
import microConfig from './mikro-orm.config';
import express from 'express';
import expense from './routes/expense';
const main = async () => {
const orm = await MikroORM.init(microConfig);
await orm.getMigrator().up();
const app = express();
// mount routes
app.use('/api/v1/expense', expense);
app.listen(4000, () => {
console.log('server started on localhost:4000');
});
};
console.log('==== STARTING ====');
main().catch(err => console.log(err));
My controller:
import { Request, Response } from 'express';
import { Expense } from 'src/entities/Expense';
// Get all expenses for a user
// GET /api/v1/expenses/:id
// Private
export function getExpense(request: Request, res: Response) {
res.send(`Get Expense: ${request.params.id}`);
}
I'm trying to establish a websocket between a SpringBoot app at localhost:8090 and a React app at localhost:3000. Through Postman i'm able to establish a websocket connection and send/receive messages at localhost:8090/api/web-socket.
When I run the React app, the application loads but isn't able to establish the websocket connection to Springboot. I've tried the connection to both 127.0.0.1 and localhost with no luck. When I try to debug in the browser console I see the following error: "WebSocket connection to 'ws://localhost:8090/api/web-socket' failed:" notice there isn't any error details. The error location references line 28 of browser.js which is
else { native_instance = new NativeWebSocket(uri); }
Here is the full block:
function W3CWebSocket(uri, protocols) {
var native_instance;
if (protocols) {
native_instance = new NativeWebSocket(uri, protocols);
}
else {
native_instance = new NativeWebSocket(uri);
}
What is this error? How can I resolve it?
Here is the what the React code for establishing the socket connection:
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {w3cwebsocket as W3CWebSocket} from "websocket";
import './index.css';
const client = new W3CWebSocket('ws://localhost:8090/api/web-socket');
export default class App extends Component{
onButtonClicked = (value) => {
client.send(JSON.stringify({
type: "message",
msg: value
}
));
}
componentDidMount() {
client.onopen = () => {
console.log('websocket client connected');
};
client.onmessage = (message) =>{
const dataFromServer = JSON.parse(message.data);
console.log("reply from socket server: ", dataFromServer);
}
}
render() {
return(
<div>
<button onClick={() => this.onButtonClicked("Example message from client to server")}>Send Message(start SAP)</button>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
UPDATE - RESOLVED
So after some further digging i discovered the issue was related to cross origin setting on the SpringBoot side. For anyone that runs into this issue in the future try the to add the following to your Socket configuration class:
.setAllowedOrigins("*")
In full it would look something like this:
registry.addHandler(new SocketTextHandler(), "/your-route").setAllowedOrigins("*");
I use gRPC but I have a problem initializing the service in Next.js app.
Goal: Create client service only once in app and use it in getServerSideProps (app doesn't use client-side routing).
For example, we have a service generated with grpc-tools (only available on SSR) and then I just want to initialize it somewhere. At first I thought it can be realized in a custom server.js:
const { credentials } = require('#grpc/grpc-js');
const express = require("express");
const next = require("next");
const { MyserviceClient } = require('./gen/myservice_grpc_pb');
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
// Init & Export
exports.myService = new MyserviceClient(
'http://localhost:3000',
credentials.createInsecure(),
);
(async () => {
await app.prepare();
const server = express();
server.get("*", (req, res) => handle(req, res));
server.listen(process.env.PORT, () => {
console.log(`Listening at http://localhost:${process.env.PORT}`);
});
})();
And then use it on the homepage, for example:
import React from 'react';
const { GetSmthRequest } = require('../gen/myservice_pb');
const { myService } = require('../server.js');
const IndexPage = () => (
<div>
<span>My HomePage</span>
</div>
)
const getServerSideProps = async () => {
const request = new GetSmthRequest();
request.setSomeStuff('random');
myService.getStmh(GetSmthRequest, (err, res) => {
//...
})
return {
props: {
}
}
}
export default IndexPage;
But for some reason it's not possible to initialize the client service in the server.js.
Also I tried doing it with next.config.js:
const { credentials } = require('#grpc/grpc-js');
const { MyserviceClient } = require('./gen/myservice_grpc_pb');
module.exports = {
serverRuntimeConfig: {
myService: new MyserviceClient(
'http://localhost:3000',
credentials.createInsecure(),
),
},
};
This solution works, so I can use the service through serverRuntimeConfig, thereby initializing it only once in the entire application, but when I make a request somewhere using getServerSideProps, I get an error:
Request message serialization failure: Expected argument of type ...
Error explanation: (https://stackoverflow.com/a/50845069/9464680)
That error message indicates that message serialization
(transformation of the message object passed to gRPC into binary data)
failed. This generally happens because the message object doesn't
match the expected message type or is otherwise invalid
Does anyone know why I am getting this error?
It's also interesting to see some examples of using Next.js with grpc-node.
For such a case you can use Node.js global