Websockets not connecting with postman using nestjs - node.js

I am trying to make a connection with websockets. If I need to connect to the postman I need to include app.useWebSocketAdapter(new WsAdapter(app)); to make it work. But once I include this line it stop connecting with my react native code. When I remove this line it starts connecting with react native code and not with postman. How can I make it work with both react native client side and postman.
Below is my gateway code
import { Logger } from '#nestjs/common';
import {
OnGatewayConnection,
OnGatewayDisconnect,
OnGatewayInit,
SubscribeMessage,
WebSocketGateway,
WebSocketServer,
} from '#nestjs/websockets';
import { Server, Socket } from 'socket.io';
#WebSocketGateway()
export class AppGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
#WebSocketServer()
server!: Server;
private logger: Logger = new Logger('AppGateway');
#SubscribeMessage('msgToServer')
handleMessage(client: Socket, payload: string): void {
this.server.emit('msgToClient', payload);
}
afterInit(server: Server) {
this.logger.log('Init');
}
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
}
handleConnection(client: Socket, ...args: any[]) {
this.logger.log(`Client connected: ${client.id}`);
}
}
main.ts
import { AppModule } from 'app.module';
import { WsAdapter } from '#nestjs/platform-ws';
async function setupApplication(): Promise<INestApplication> {
app.useWebSocketAdapter(new WsAdapter(app));
await app.listen(port);
return app;
}
In the above main.ts file. If I remove app.useWebSocketAdapter(new WsAdapter(app)); then I am able to connect with my react native client code but then not with postman.
react native code
import React, {useEffect} from 'react';
import {Button, SafeAreaView} from 'react-native';
import io from 'socket.io-client';
const socket = io('http://localhost:3000');
export default function App() {
const receiveMessage = () => {
socket.on('msgToClient', msg => {
console.log('msg', msg);
});
};
const sendMessage = () => {
socket.emit('msgToServer','test message');
};
useEffect(() => {
receiveMessage()
}, []);
return (
<SafeAreaView>
<Button title="Message" onPress={sendMessage} />
</SafeAreaView>
);
}

Postman uses the ws: protocol, which is what the WsAdapter is for. By default Nest uses socket.io as the de facto WebSocket adapter which does not connect via ws diectly, but via the socket.io engine. Your react code is using socket.io so your server code should use socket.io too. This does mean that you won't be able to use postman to test your websockets though, unless you keep swapping back and forth.

Postman now has support to Socket.io servers (it's in beta):
Step by step from the provided link:
In the left-hand sidebar, click New.
Select WebSocket Request.
Within the new tab’s header, click the drop-down that says Raw, and select Socket.IO instead.
Type the address of your Socket.IO server into the URL bar.
Click Connect.
As you do not provide a specific namespace on the #WebSocketGateway() decorator and assuming that you are using the default NestJS port 3000, the step 4 address would be:
http://localhost:3000
Additionally, if you are running NestJS in WSL, you might want to see this answer

Related

Inversify - Jest - call API endpoint to test auth guard

Iam using Inversify & inversify-express-utils for building an Express Server. I am new to testing(Jest).
I need to call API instead of calling the controller class so that I can test the AuthGuard and RolesGuard. I tried using supertest library. Iam facing some issues. Im pretty sure that I have a wrong test setup.
user.controller.spec.ts:
import 'reflect-metadata';
import { mock } from 'jest-mock-extended';
import request from 'supertest';
import { MailService } from '../../../shared/mail/mail. Service';
import { UserController } from '../controller/users.controller';
import { UserService } from '../service/users.service';
import { ValidationException } from '../../../core/exception';
import { createUserFixture, updateUserFixture, userFixture } from '../fixtures/userFixture';
import { bootstrap } from '../../../main';
const userServiceMock = mock<UserService>();
const mailServiceMock = mock<MailService>();
describe('Users Controller', () => {
let app: any;
let sut: UserController;
sut = new UserController(userServiceMock, mailServiceMock);
beforeEach(async () => {
app = await bootstrap();
});
test('should throw auth error', async () => {
const req = await request(app).get('/user');
console.log(req);
});
});
main.ts
import 'reflect-metadata';
import cors from 'cors';
import express, { NextFunction, Response, Request } from 'express';
import helmet from 'helmet';
import { InversifyExpressServer } from 'inversify-express-utils';
import { container } from './core/inversify/inversify.config';
import { Sql } from './database/sql';
import { GlobalErrorConfig } from './core/server';
export const bootstrap = () => new Promise((resolve) => {
/// /Connect to SQL Server
new Sql().connect();
/// Start Server
const server = new InversifyExpressServer(container);
server.setConfig((app) => {
app.use(express.json());
app.use(cors());
app.use(helmet());
});
/// Global Error Config
GlobalErrorConfig(server);
/// Build Server
const app = server.build();
app.listen(process.env.PORT || 5000, () => {
console.log(
`Server is running on http://localhost:${process.env.PORT || 5000}`,
);
resolve(app);
});
});
bootstrap();
While trying to start the test, I get these following error:
The error:
Jest trying to warn delayed logs.
Same controller name error(app working normally on npm start).
Jest warning a file import after Jest environment torn down.
I have no clue what went wrong. I would really appreciate if anyone points out the issue or suggesting me the proper Jest API testing setup. Thanks in advance.

Messages not reaching handler while using WebSocket and nestjs

I am trying to implement WebSocket into nestjs app and I have problems with messages not reaching my handler in nestjs.
I am able to connect both ends and send message from nestjs to client but not the other way.
this is my nestjs code: (please note that i am not using socket.io i am implementing ws as WebSocket
import {
OnGatewayInit,
WebSocketGateway,
WebSocketServer,
} from '#nestjs/websockets';
import { Logger } from '#nestjs/common';
#WebSocketGateway(5015)
export class ExportFeedsGateway implements OnGatewayInit {
#WebSocketServer() wss: any;
private logger: Logger = new Logger('ExportFeedsGateway');
afterInit(server: any): any {
this.logger.log('Export Feeds Initialized');
}
handleConnection(client) {
client.send('message from server'); // this message is properly send and read on the client side
}
handleMessage(message) {
console.log('message', message); // this is never logged in nest
}
}
and some client code:
const WebSocket = require( 'ws');
...
this.ws = new WebSocket('ws://localhost:5015');
this.ws.on('open', () => {
this.ws.send('message from client') // this message never reaches server
});
...
Question is what is the problem with nestjs messages handler that it doesnt pick up messages?
You're missing the #SubscribeMessage('message') that you'll need for Nest to register the handler to the websocket server. handleConnection is a bit of a special case method as defined inthe lifecycle methods. Also, when sending the message from the ws library, use the form: ws.send('{"event": "message", "data": whateverDataObjectYouWant }', callback). This way, Nest can parse the event and properly route it

How to handle when client disconnects on NestJS

I am working with NestJS and I need to know when a client has forced the disconnection or has canceled it. (either by mistake or because they wanted to).
For exaple, in Express it's as easy as:
const express = require('express')
const app = express()
const port = 3000
app.get('/', (expressRequest, expressResponse) => {
// Detecting close event
expressRequest.on('close', function() {
console.log('Client connection closed....!');
});
// Detecting end event
expressRequest.on('end', function() {
console.log('Client connection end....!');
});
expressResponse.send('Hello World!')
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
The question is: working with NestJS, what is the correct way to do it?
The first thing I would try is using the #Req() param decorator. Assuming you're using Nests default Express adapter, then the request object received is the Express req object.
The following should work for you. The rest of this post is just cleaning it up and making it more "Nest".
import { Controller, Get, Req } from '#nestjs/common';
import { Request } from 'express';
#Controller()
export class AppController{
#Get()
test(#Req() req: Request): string {
req.on('close', () => console.log('Doing something with closed connection'))
return "Hello, world!"
}
}
If you're planning to reuse this logic in a few controller methods, then I would also consider creating a custom decorator for it:
import { createParamDecorator, ExecutionContext } from '#nestjs/common';
import { Observable } from 'rxjs';
import { Request } from 'express';
export const OnConnectionClosed = createParamDecorator(
(data: unknown, ctx: ExecutionContext) =>
new Observable((observer) => {
const request = ctx.switchToHttp().getRequest<Request>();
request.on('close', () => observer.complete());
}),
);
And then using it like the following:
#Controller()
export class AppController{
#Get()
test(#OnConnectionClosed() onClosed: Observable<void>): string {
onClosed.subscribe({
complete: () => console.log('Connection closed'),
});
return 'Hello, world!';
}
}
And with that, you've created your own "Nest" way to listen for close events on the incoming request.
Nestjs has many different components that are executed at different times during the life cycle of a request.
The order in which these components are executed would be the following
NestJs request Life cycle
Incoming request
Globally bound middleware
Module bound middleware
Global guards
Controller guards
Route guards
Global interceptors (pre-controller)
Controller interceptors (pre-controller)
Route interceptors (pre-controller)
Global pipes
Controller pipes
Route pipes
Route parameter pipes
Controller (method handler)
Service (if exists)
Route interceptor (post-request)
Controller interceptor (post-request)
Global interceptor (post-request)
Exception filters (route, then controller, then global)
Server response**
The answer to your question:
I think it should be detected in the following points
Global interceptor (post-request)
Controller interceptor (post-request)

Network error: Failed to fetch on ionic react

it's the first time that i post on forums,
I really needs your help.
I'm stuck with a problem, I have an Ionic/React application, a Node.js application and a graphQL/Apollo API,
when i'm calling the graphql API from my browser it's all working fine but when i'm building the app with capacitor and running it on my Android device, I get "Network error: Failed to fetch".
Here is my client.ts code where i'm setting up my ApolloClient
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { setContext } from 'apollo-link-context';
import { InMemoryCache, NormalizedCacheObject } from 'apollo-cache-inmemory';
class RequestUtils {
clientApollo: any;
constructor() {
const httpLink = createHttpLink({
uri: 'http://192.168.1.86:4000/graphql'
});
const authLink = setContext((req, { headers }) => ({
headers: {
...headers
}
}));
this.clientApollo = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache()
});
}
}
const requestUtils = new RequestUtils();
const client: ApolloClient<NormalizedCacheObject> = requestUtils.clientApollo;
export default client;
and here is my graphql.ts
import resolvers from "../resolvers/index";
import { ApolloServer, AuthenticationError } from "apollo-server-express";
import { getModels } from "../models/index";
import schemas from "../schemas/index";
import * as express from "express";
import * as cors from "cors";
import { connectToMongo } from "./config";
import { info, success, warning, error } from "./logger";
export class query {
public app: any;
constructor() {
const models = getModels();
this.app = express();
this.app.use(cors());
try {
info("Starting connection to graphql");
const server = new ApolloServer({
typeDefs: schemas,
resolvers,
context: async ({ req }) => {
if (req) {
return {
models: models
};
}
}
});
this.app = express();
server.applyMiddleware({ app: this.app, path: "/graphql" });
success(`Connected to graphql`);
} catch (error) {
warning(error);
}
this.app.listen(4000, () => {
connectToMongo();
});
}
}
I think that it's a problem with something like cors or ip adress but i don't find any solution to my problem.
I hope someone can help me !
EDIT:
I tried to run my node server on another computer and cal the graphql API from my main computer with my ionic react react webapp. The origin is in fact different but there is no error, all works perfectly. But with my builded app on my android device, always same error.
So, I think it's not cors, maybe it's something with Capacitor/cordova, or something like this.
At first, i thought android app wasn't allowed to connect to network, I checked and it's connected, but I'm not sure.
If someone could help me, it would be very sympathic , I'm really stuck with this, my app is useless if i can't connect to server en database XD
Had exactly the same problem.
It looks like the cause is that Android forbids HTTP requests by default.
The following fix worked for me:
To allow http requests, I added android:usesCleartextTraffic="true" to my app's AndroidManifest.xml
Solution post
I "solved" my problem by launching my app with " ionic capacitor run android -l --external "
I don't what will happens if the app is in production but at least I can progress in my dev.
I'll see later what happens ¯_(ツ)_/¯

Axios in a Node.js container on Kubernetes is returning "ECONNREFUSED 127.0.0.1:30561"?

Full error message: connect ECONNREFUSED 127.0.0.1:30561 at TCPConnectWrap.afterConnect
The axios request is running in a Node.js environment (Next.js), which is where the error occurs, strangely the axios request works perfectly fine when it is being run in the browser.
My component (running in Node.js) that calls axios:
import axios from 'axios'
import Router from 'next/router'
import React, { Component } from 'react'
import { initializeStore } from '~/reducers'
import { authenticate } from '~/actions/auth'
import { getCookieByName } from '~/helpers/cookie'
const isServer = typeof window === 'undefined'
const __NEXT_REDUX_STORE__ = '__NEXT_REDUX_STORE__'
function getOrCreateStore(initialState) {
// Always make a new store if server, otherwise state is shared between requests
if (isServer) {
return initializeStore(initialState)
}
// Create store if unavailable on the client and set it on the window object
if (!window[__NEXT_REDUX_STORE__]) {
window[__NEXT_REDUX_STORE__] = initializeStore(initialState)
}
return window[__NEXT_REDUX_STORE__]
}
export default App => {
return class AppWithRedux extends Component {
static async getInitialProps(appContext) {
const reduxStore = getOrCreateStore()
appContext.ctx.reduxStore = reduxStore
let appProps = {}
if (typeof App.getInitialProps === 'function') {
appProps = await App.getInitialProps(appContext)
}
const JWT = (isServer ? getCookieByName('JWT', appContext.ctx.req.headers.cookie) : getCookieByName('JWT', document.cookie))
const pathname = appContext.ctx.pathname
//set axios baseURL
axios.defaults.baseURL = (isServer ? `${appContext.ctx.req.headers['x-forwarded-proto']}://${appContext.ctx.req.headers.host}` : window.location.origin)
//if user has a JWT
if(JWT){
axios.defaults.headers.common['Authorization'] = `Bearer ${JWT}`
//get user from API layer
reduxStore.dispatch(authenticate())
}
return {
...appProps,
initialReduxState: reduxStore.getState()
}
}
constructor(props) {
super(props)
this.reduxStore = getOrCreateStore(props.initialReduxState)
}
render() {
return <App {...this.props} reduxStore={this.reduxStore} />
}
}
}
Specifically reduxStore.dispatch(authenticate())
And my actual axios call (using redux thunk), looking at the authenticate method:
import axios from 'axios'
import { setCookieByName } from '~/helpers/cookie'
const BASE_URL = '/api/auth'
export const TYPE_REGISTER = 'TYPE_REGISTER'
export const TYPE_AUTHENTICATE = 'TYPE_AUTHENTICATE'
export const register = values => (dispatch) => {
return axios.post(`${BASE_URL}/register`, values)
.then(function({data: {token, user}}){
setCookieByName('JWT', token, 365)
dispatch({
type: TYPE_REGISTER,
payload: user
})
})
}
export const authenticate = () => (dispatch) => {
return axios.post(`${BASE_URL}/me`)
.then(function({data: {user}}){
dispatch({
type: TYPE_AUTHENTICATE,
payload: user
})
})
.catch(function(err){
console.log(err)
dispatch({
type: TYPE_AUTHENTICATE,
payload: {}
})
})
}
I'm running my local Kubernetes cluster using Docker for Mac, and my Ingress controller is being accessed on http://kludge.info:30561. My domain is mapped from 127.0.0.1 kludge.info locally to allow the Ingress controller to hit the container. My theory is that when I send a request to http://kludge.info:30561/api/auth/me for example, the docker container running the Node.js app thinks it is a request to localhost (inside the container), and results in a connection error. Please note that the Node.js app inside the container is running on http://localhost:8080. Basically I'm running localhost on my machine, and localhost inside the Node instance. How could I send a request outside to http://kludge.info:30561/ where the Ingress controller is running.
I've also configured the baseURLin axios, but it does not solve the problem. My ingress controller has a path /api that will point to a PHP instance, so I need my Node.js axios call to hit that inside it's container. Any help would be much appreciated.
When I ran my K8 cluster on Minikube I did not have this problem, however minikube does provide you with the IP of the VM, while Docker for Desktop uses localhost directly on your machine.
If I understand you correctly I see that you open a socket on lcoalhost(127.0.0.1) so it is only accessible locally. If you want it to be accessible from outside you need to bind it to 0.0.0.0 meaning "all interfaces".
Listening on 0.0.0.0 means listening from anywhere with network access to this computer. For example, from this very computer, from local network or from the Internet. And listening on 127.0.0.1 means from this computer only.
Please let me know if that helped. Or if I have misunderstood you.

Resources