Express await database connection - node.js

I am following a related post here
I am struggling with awaiting the module import from my express application.
I understand that to use await, it must be wrapped in an async function. However I can't wrap my entire node program in an async function because it will exit without doing anything useful.
How can I properly await the database connection?
node/express:
require('dotenv').config();
var express = require('express');
var loginRouter = require('./routes/login/login');
var app = express();
async() => {
const { client } = await require('./db/db');
app.use('/login', loginRouter);
app.set('port', process.env.PORT || 3000);
app.listen(app.get('port'));
console.log('Server listening on port ' + app.get('port'));
}
db module:
const { Client } = require('pg');
module.exports = (async() => {
const client = new Client();
await client.connect();
return { client };
})();

One option would be to export a Promise that resolves to a connected client. Then, when you import it, call .then on the imported Promise to get to the connected client:
const { Client } = require('pg');
const client = new Client();
module.exports = {
clientProm: client.connect().then(() => client)
};
And:
const { clientProm } = require('./db/db');
clientProm.then((client) => {
// do stuff with connected client
});

Related

how to use make express endpoints listen to same server as mongoose (apollo server)

i currently have an apollo server interacting with my mongodb database running on a port (localhost or the port given from the host when deployed). i also have another file (app.js) for web scraping that has routes and uses express, running on a different port.
i want the app.js express logic to run on the same port as the call
server.listen({ port: port}) in index.js. how do i do this, please? essentially something like putting the app.get calls in the mongoose.connect which does not seem possible, but so you can get the idea.
moving all the logic in app.js to index.js so the server listens to mongoose, apollo, and all the express endpoints i have in one place on the same, single port.
//index.js
const { ApolloServer } = require('apollo-server');
const mongoose = require('mongoose');
const express = require("express");
const bodyParser = require('body-parser');
const nodemailer = require('nodemailer');
let port = process.env.port || 5000;
const typeDefs = require('./graphql/typeDefs');
const resolvers = require('./graphql/resolvers');
const { MONGODB } = require('./config.js');
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => ({ req })
});
mongoose.connect(MONGODB, { useNewUrlParser: true })
.then(() => {
console.log("MongoDB connected");
return server.listen({ port: port})
})
.then((res) => {
console.log(`Server running at ${res.url}`);
})
app.js (snippet) used for endpoints that return web scraping data
const express = require("express");
const cors = require('cors')
const cheerio = require('cheerio');
const axios = require('axios');
const app = express();
const port = 5001;
app.get("/events/", function(req, res) {
// redacted logic
let { zipCode } = req.query;
// Create a new yelpAPI object with your API key
let apiKey =[redacted];
let yelp = new yelpAPI(apiKey);
// Set any parameters, if applicable (see API documentation for allowed params)
let params = [{ location: zipCode }];
// Call the endpoint
yelp.query('events', params)
.then(data => {
// Success
res.send(data);
})
.catch(err => {
// Failure
console.log(err);
});
});
app.listen(port, () => console.log(`CORS-enabled web server listening on port ${port}!`));

export a function inside socket connection

I have created a socket server as shown below.
const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io')(server, {cors:{origin:'*'}})
const mongoose= require("mongoose")
const port = process.env.PORT || 4002;
server.listen(port, ()=>{
console.log(`Listening on port ${port}......`)
})
onlineUsers = [];
const addNewUser = (userId, socketId)=>{
!onlineUsers.some((user)=>user.userId === userId) &&
onlineUsers.push({userId,socketId})
}
const removeUser= (socketId) =>{
onlineUsers = onlineUsers.filter((user)=> user.socketId!==socketId)
}
const getUser = (userId)=>{
return onlineUsers.find(user=>user.userId ===userId)
}
io.on("connection", (socket=> {
console.log("User connected:", socket.id);
socket.on("disconnect",()=>{
removeUser(socket.id)
})
socket.on("newUser",(userId)=>{
addNewUser(userId, socket.id)
})
export function handleMessaging (userId,clientID,messageId )
{
const receiver = getUser(userId);
if(receiver)
{
io.to(receiver.socketId).emit("sendMessage", {data:"working properly"});
return true;
}
else return false
}
}));
I want to export the function handle messaging so that I can use it inside the API like (shown below) to see if a user is online and if yes, send a message.
But as someone new to programming, I can't figure out how to export handle messaging the proper way. I tried to use export but its telling me "Modifiers cannot appear here".
router.post('/:companyId' async (req, res) => {
const {userId,clientId,messageId} = req.body
handleMessaging (userId,clientID,messageId )
{
//do xyz
}
}

Trying to reuse socket io instance in a controller but getting an error that socket hasn't been initialized

I'm relatively new to node.js and I'm trying to include socket.io into a controller. The idea is to respond to a client when an order is placed through the response object of express but in addition I'd also like to emit an event so that the restaurant owner sees the orders from all the customers coming in 'live'.
I have an index.js file in an api folder with the following code, where I export api, server and PORT:
`
const express = require('express');
const morgan = require('morgan');
const http = require('http');
const cors = require('cors');
const api = express();
const server = http.createServer(api);
const PORT = process.env.PORT || 3000;
api.use(cors());
api.use(morgan('common'));
api.use(express.urlencoded({ extended: true }));
api.use(express.json({ extended: true }));
api.use('/api/v1', require('../routers'));
api.get('/', (req, res) => {
res.send('Backend running.');
});
module.exports = { api, server, PORT };
In the root of the project I have another index.js file with the following code:
/* eslint-disable no-console */
require('dotenv').config();
const mongoose = require('mongoose');
const { api, server, PORT } = require('./api');
const { MONGO_URI } = require('./config');
mongoose.connect(
MONGO_URI,
{ useNewUrlParser: true, useUnifiedTopology: true },
)
.then(() => console.log('Connected to DB'))
.catch((err) => console.log('Error occured while trying to connect to DB', err));
api.listen(PORT, () => console.log(`Listening on ${PORT}`));
const io = require('./socket').init(server);
io.on('connection', (socket) => {
console.log('Connection success', socket.id);
socket.on('disconnect', () => {
console.log('Connection disconnected', socket.id);
});
});
I've placed the code to initialize socket.io and get an instance of it in a folder named socket with the following code in the index.js file:
/* eslint-disable consistent-return */
/* eslint-disable global-require */
const { Server } = require('socket.io');
let io;
module.exports = {
init: (server) => {
try {
io = new Server(server);
return io;
} catch (err) {
console.log(err);
}
},
get: () => {
if (!io) {
throw new Error('socket is not initialized');
}
return io;
},
};
Then I import the io instance in a controller but when I emit an event I get the error that the socket is not initialized. This is how I import the socket instance and emit an event:
const { OrdersService } = require('../services');
const io = require('../socket/index').get();
module.exports = {
create: async (req, res) => {
const { body } = req;
try {
const order = await OrdersService.create(body);
io.emit('new order', order);
res.status(201).json(order);
} catch (err) {
res.status(400).json(err);
}
},
};
What am I doing wrong? Any help would be greatly appreciated :)
I configured socket.io like I did based on previous questions that were raised on this topic here in stackoverflow.

Await for function before end()

edit: added a bit more code.
const express = require('express');
var bodyParser = require('body-parser');
const app = express();
var urlencodedParser = bodyParser.urlencoded({extended: false})
const {google} = require('googleapis');
const {PubSub} = require('#google-cloud/pubsub');
const iot = require('#google-cloud/iot');
const API_VERSION = 'v1';
const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest';
app.get('/', urlencodedParser, (req, res) => {
const projectId = req.query.proyecto;
const cloudRegion = req.query.region;
const registryId = req.query.registro;
const numSerie = req.query.numSerie;
const command = req.query.command;
const client = new iot.v1.DeviceManagerClient();
if (client === undefined) {
console.log('Did not instantiate client.');
} else {
console.log('Did instantiate client.');
sendCom();
}
async function sendCom() {
const formattedName = await client.devicePath(projectId, cloudRegion, registryId, numSerie)
const binaryData = Buffer.from(command);
const request = {
name: formattedName,
binaryData: binaryData,
};
return client.sendCommandToDevice(request).then(responses => res.status(200).send(JSON.stringify({
data: OK
}))).catch(err => res.status(404).send('Could not send command. Is the device connected?'));
}
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
module.exports = app;
I have this function, that I call after the client initiate: sendCom();
async function sendCom() {
const formattedName = await client.devicePath(projectId, cloudRegion, registryId, deviceId)
const binaryData = Buffer.from(command);
const request = { name: formattedName, binaryData: binaryData, };
client.sendCommandToDevice(request)
.then(responses => {
res.status(200).send(JSON.stringify({ data: OK })).end();
})
.catch(err => {
res.status(404).send('Could not send command. Is the device connected?').end();
});
}
My problem is that sendCommandToDevice gets executed perfectly, however I get the catch error.
As I understand it, it's because in the .then ends the connection.
I've looked at this and thats's what I tried, however I'm not sure I understand what's going on.
You can not use send with end.
end() is used when you want to end the request and want to respond with no data.
send() is used to end the request and respond with some data.
You can found more about it here.

SocketIO – connection issues and clustering

I have a really simple NodeJS app that I want to run on Heroku. This is how the index.js file looks like:
Server (port 3030)
const http = require('http');
const os = require('os');
const express = require('express')
const throng = require('throng'); // For cluster management
const { port, env, isProduction } = require('./config/vars');
const SocketIO = require('socket.io');
// Setting up a simple express app and wrapping it with http server
const setupServer = () => {
const app = express();
app.use(express.static(path.join(__dirname, '../public')));
const server = http.createServer(app);
return server;
};
const setupSocket = (server) => {
const io = new SocketIO(server);
io.on('connection', (socket) => {
console.log(`[Socket] Connection established: ${socket.id}`);
socket.on(msg.rooms.join, (room) => {
socket.join(room);
socket.to(room).emit(msg.rooms.joined);
console.log(`[Socket] User ${socket.id} joined '${room}' room`);
});
socket.on('disconnect', () => {
console.log(`[Socket] Distonnected: ${socket.id}`);
});
});
return io;
};
const WORKERS = (() => {
if (!isProduction) return 1;
return process.env.WEB_CONCURRENCY || os.cpus().length;
})();
async function master() {
console.log(`Preparing ${WORKERS} workers...`);
console.log('Master started.');
}
// There should be one server instance for each worker
const start = () => {
const server = setupServer(); // Returns and `http` server instance
const socket = setupSocket(server);
server.listen(port, async () => {
Logger.info(`Server – listening on port ${port}`);
});
return server;
};
const instance = throng({
workers: WORKERS,
lifetime: Infinity,
start,
master,
});
module.exports = instance;
Client (port 3000)
const setupSocket = ({ room }) => {
// Fallback if already setup
if (window.sockets[room]) {
return window.sockets[room];
}
const socket = io('http://localhost:3030');
socket.on('connect', () => {
console.log('[Socket] Connection established!', socket.id);
socket.emit('room.join', room);
});
socket.on('room.joined', () => {
console.log(`[Socket] Connected to ${room} room!`);
});
window.sockets[key] = socket;
return socket
};
The problem – the connection is sometimes established properly but most of the time I get an error
Error during WebSocket handshake: Unexpected response code: 400
What might be the problem here? Is it because I have it on two different ports or is it because of the clusters?
I've tried removing the throng part of the code, and just calling start() method without any cluster setup, but the problem remains :(
why would you use http module? The server instance that you send in the socketIO constructor should be the return object of the expressInstance.listen
Something more like this:
const express= require('express')
const app = express()
const socketio = require('socket.io')
app.use(express.static(__dirname + '/public'))
const server = app.listen('4000',()=>{
console.log('Listening to port:4000')
})
const io = socketio(server)
io.on('connect',(socket)=>{
socket.broadcast.emit('new_user')
socket.on('new_message',(message)=>{
io.emit('new_message',message)
})
})
source code: socket-io chat

Resources