NestJS hybrid application port management problem - nestjs

I'am using NestJs to expose my business through an Http Rest API and a GRPC endpoint. To do this I'am using an "hybrid" application (as explained here) like this :
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.enableCors();
const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
port: 3000,
},
});
const microserviceGrpc = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.GRPC,
options: {
url: 'localhost:50051',
package: 'hero',
protoPath: join(__dirname, './hero.proto'),
},
});
await app.startAllMicroservices();
await app.listen(3000);
}
bootstrap();
If I run this application I have an error message like this :
Error: listen EADDRINUSE: address already in use :::3000
I think there's a conflict between the options.port parameter in the microserviceTcp app and the port passed in the app.listen() function.
To make it work I have to remove the options.port because app.listen() needs a port number, but I think there something incoherent here.
What if I wanted for example to expose 2 microservices with GRPC and REDIS ? In this case the app.listen() port is mandatory but is totally useless (Or there is something I miss somewhere).
Thank you for you help.

Related

Not able to connect to heroku redis

const redis = require("redis");
let client = redis.createClient({
host: process.env.host,
port: process.env.port,
password:process.env.password
});
(async () => {
client.on('error', (err) => console.log('Redis Client Error', err));
await client.connect();
console.log("connected to redis")
})();
I have added redis-heroku addon to my project, Now I am trying to access it from my code but its giving me this error: "AuthError: ERR Client sent AUTH, but no password is set".
Also when I am trying to connect from terminal, I am able to connect to it but when I type any redis command , I get this "Error: Connection reset by peer".
If I am using this on my localsystem and local redis server its working fine
it will be helpful if anyone can provide me a working code of heroku redis, I think redis has two urls: REDIS_URL, REDIS_TLS_URL. The problem might be arising because of this tls(more secure)
Kinldy help me
Thanks
Heroku redis does not expose a host, port, and password variables. Instead they expose a REDIS_URL that contains all of those things in one string.
I believe you need to call createClient like this...
createClient({
url: process.env.REDIS_URL
});
In node-redis v4 the host and port should be inside a socket object, not directly on the main config object (see https://github.com/redis/node-redis/blob/master/docs/client-configuration.md):
const client = redis.createClient({
socket: {
host: process.env.host,
port: process.env.port
},
password: process.env.password
});

WebSocket is closed before the connection is established. Socket.io and React

I want to make an Chat application with Socket.io and I've followed this tutorial: https://youtu.be/ZwFA3YMfkoc. I am using React and Node.js
Everything works fine while using it locally and even on different devices on my Network. However if I am hosting my Backend on Heroku it doesn't work.
The error Message is:
WebSocket connection to 'URL' failed: WebSocket is closed before the connection is established.
(URL is the URL of my Backend with the Port). I am using SSL.
I've already tried to enable session affinity but it already was enabled.
My backend code is: (atleast the code that I think is important to the Problem)
const app = express();
const server = http.createServer(app);
const io = socketio(server);
app.use(cors());
server.listen(PORT, () => console.log("Server started on " + PORT));
My frontend code is written in React and is:
var connectionOptions = {
"force new connection": true,
reconnectionAttempts: "Infinity",
timeout: 10000,
transports: ["websocket"],
};
const ENDPOINT = "URL";
socket = io(ENDPOINT, connectionOptions);
So I've fixed my Problem.
The URL on the client side had to be without the Port.
So for example:
const ENDPOINT = "https://web.site.com";
and not:
const ENDPOINT = "https://web.site.com:1337";
Call socket.connect() or just add autoconnect: true in options you are providing.

How can a NestJS app listen on the same port as a connected microservice?

I'm trying to understand how Nest microservices work.
Let's start with this example from the docs (https://docs.nestjs.com/faq/hybrid-application)
const app = await NestFactory.create(AppModule);
// microservice #1
const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
port: 3001,
},
});
// microservice #2
const microserviceRedis = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.REDIS,
options: {
url: 'redis://localhost:6379',
},
});
await app.startAllMicroservicesAsync();
await app.listen(3001);
After going through the source (https://github.com/nestjs/nest) , I understand connectMicroservice creates a net.Server when using TCP and the server starts listening when startAllMicroservicesAsync is called.
But then app.listen should initialize the listening of the base Nest webserver.
Why doesn't that cause an error?
I checked what happens if I have two microservices connected on the port. It sure does throw an error. What am I missing here?
// CODE CAUSES ERROR AS EXPECTED
const app = await NestFactory.create(AppModule);
// microservice #1
const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
port: 3001,
},
});
// microservice #2 <--- THIS WILL CAUSE AN ERROR AS EXPECTED
const microserviceTcp = app.connectMicroservice<MicroserviceOptions>({
transport: Transport.TCP,
options: {
port: 3001,
},
});
await app.startAllMicroservicesAsync();
await app.listen(3001);
In case anyone is interested: it is because microservices and "classic" Nest apps register ports with different defaults. Using lsof to check the open TCP sockets it is immediately clear.
localhost:3000 (LISTEN). // microservice
*:3000 (LISTEN) // http app
If I explicitly provide a hostname as localhost for app.listen in main.ts, HTTP app fails to start, as expected.

Adding GraphQL subscriptions to the existing express app using single server

GraphQL playground subscription fails with 400 error code.
WebSocket connection to 'ws://localhost:3000/graphql' failed: Error during WebSocket handshake: Unexpected response code: 400
I have an existing code based on express. I've integrated Apollo v2 this way:
const { ApolloServer, PubSub, gql } = require('apollo-server-express');
...
const app = express();
const server = new ApolloServer({
typeDefs,
resolvers
});
server.applyMiddleware({ app });
...
app.listen(port, () =>
console.log(chalk.blue(`App listening on port ${port}!`)),
);
and then i start my existing app on port 3000 and can access the GraphQL playground on http://localhost:3000/graphql. All queries and mutations work as expected
Now I want to add subscriptions. Turns out I need to use an http server:
const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);
and then listen to it on another port:
httpServer.listen(3005, () => {
console.log(`Server ready at http://localhost:3005${server.graphqlPath}`);
console.log(`Subscriptions ready at ws://localhost:3005${server.subscriptionsPath}`);
});
So I have two servers running. My own on port 3000 and the subscriptions server on 3005. Is that the right way? Is it the only way?
There's no need to call both app.listen and httpServer.listen -- calling httpServer.listen will expose both your Express app and the subscription server.
Additional reading: Express.js - app.listen vs server.listen

Can't connect to database with process.env variables but process.env vars print in log

I'm connecting to a redshift database with Node/express. I put the variables to connect to the database in a .env file, and on my local machine, I'm able to connect to the website on localhost.
However, when I upload the files to the server and change the clientConfiguration, it no longer works, even after I've changed my require('dotenv').config({path: }) to the correct path. I'm pretty sure the path is correct because process.env.HOST will print in the logs.
This error will show up: password authentication failed for user "root"
This is the hardcoded part that works.
var clientConfiguration = {
user: "user",
database: "database",
password: "password",
port: 1234,
host: "hosturl.com",
};
When I swap this part in, it no longer works.
var clientConfiguration = {
user: process.env.USER,
database: process.env.DATABASE,
password: process.env.PASSWORD,
port: process.env.PORT,
host: process.env.HOST,
};
I thought it was because process.env variables get read in as strings, but that didn't help even after I used parseInt(process.env.PORT) -- I also didn't need the parseInt on my local machine, so I dont understand the
Are you calling dotenv.config() as early as possible? I call it right after creating a new Express instance and it usually works.
Also, not sure if this is the 'accepted way', but I have had a similar issue before and found making an async dotenv.config() call inside the IIFE where I start my server solved the issue:
//AWAIT DB CONNECTION BEFORE STARTING SERVER
(async function () {
try {
await dotenv.config();
await connectDB();
app.listen(PORT, ()=> {
console.log(`Server listening in ${MODE} mode on ${PORT}`);
});
} catch (err) {
console.log(`Failure to start server: ${err}`);
}
})();
connectDB() is my attempt to connect to a MongoDB database via mongoose.connect(). Obviously not the ideal solution as you want to call it earlier, but it did work.

Resources