Server initiated server-client websocket messages in nodejs? - node.js

I have a node js app that receives data from a sensor device via POST requests. It also serves frontend to monitor that data. I want it to send data updates via websocket to all connected clients
Here's what I came up with:
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const ws = expressWs.getWss('/ws');
function sendAll(data) {
ws.clients.forEach(function each(client) {
client.send(JSON.stringify(data));
});
}
setInterval(async () => {
try {
let message = { message: 'dataUpdated', data: { foo: 'bar } };
sendAll(message);
} catch (err) {
console.error(err);
}
}, 1000 * 5);
app.use(express.static('./static/'));
// tell the app to parse HTTP body messages
app.use(bodyParser.json());
// routes
const apiRoutes = require('./api/routes/api-routes');
app.use('/api', apiRoutes);
module.exports = app;
I can use the sendAll function to broadcast data. But I'd like to be able to also use this function inside apiRoutes where I process the incoming requests from the sensor. How can I pass it there, or maybe get access to the expressWs instance and create same function in the included api-routes file?

The solution turned out to be quite simple =)
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
const ws = expressWs.getWss('/ws');
export function wsBroadcast(data) {
ws.clients.forEach(function each(client) {
client.send(JSON.stringify(data));
});
}
app.use(express.static('./static/'));
// tell the app to parse HTTP body messages
app.use(bodyParser.json());
// routes
const apiRoutes = require('./api/routes/api-routes');
app.use('/api', apiRoutes);
module.exports = app;
And then just import it in the file that needs it to broadcast messages:
import express from 'express';
import { wsBroadcast } from '../server';
const router = express.Router();
router.post('/data', (req, res) => {
wsBroadcast({ message: 'gotData', data: req.body });
...

Related

How do I grab json from an external api (serp api) from my backend and then make that same data available for my front end application?

Right now I have a front end react application using axios and and a backend server using node.js and express. I cannot for the life of me get my serp api data to post so that my front end can get it through axios and display the json data. I know how to get data to the front end but I am not a backend developer so this is proving to be incredibly difficult at the moment. I'm able to get the data from the the external api, I just don't know how to post it once I get it. Also I would not like to have all these request running on server.js so I created a controller but I think that is where it is messing up. Any help is appreciated
//pictures controller
const SerpApi = require('google-search-results-nodejs');
const {json} = require("express");
const search = new SerpApi.GoogleSearch("674d023b72e91fcdf3da14c730387dcbdb611f548e094bfeab2fff5bd86493fe");
const handlePictures = async (req, res) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com"
};
const callback = function(data) {
console.log(data);
return res.send(data);
};
// Show result as JSON
search.json(params, callback);
//res.end();
}
// the above code works. how do i then post it to the server so that i can retrieve it to the backend?
module.exports = {handlePictures};
//server.js
const express = require('express');
const app = express();
const path = require('path');
const cors = require('cors');
const corsOptions = require('./config/corsOptions');
const { logger } = require('./middleware/logEvents');
const errorHandler = require('./middleware/errorHandler');
const cookieParser = require('cookie-parser');
const credentials = require('./middleware/credentials');
const PORT = process.env.PORT || 3500;
// custom middleware logger
app.use(logger);
// Handle options credentials check - before CORS!
// and fetch cookies credentials requirement
app.use(credentials);
// Cross Origin Resource Sharing
app.use(cors(corsOptions));
// built-in middleware to handle urlencoded form data
app.use(express.urlencoded({ extended: false }));
// built-in middleware for json
app.use(express.json());
//middleware for cookies
app.use(cookieParser());
//serve static files
app.use('/', express.static(path.join(__dirname, '/public')));
// routes
app.use('/', require('./routes/root'));
app.use('/pictures', require('./routes/api/pictures'));
app.all('*', (req, res) => {
res.status(404);
if (req.accepts('html')) {
res.sendFile(path.join(__dirname, 'views', '404.html'));
} else if (req.accepts('json')) {
res.json({ "error": "404 Not Found" });
} else {
res.type('txt').send("404 Not Found");
}
});
app.use(errorHandler);
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
//api/pictures.js
const picturesController= require('../../controllers/picturesController');
const express = require('express')
const router = express.Router();
// for POST request use app.post
router.route('/')
.post( async (req, res) => {
// use the controller to request external API
const response = await picturesController.handlePictures()
// send the response back to client
res.json(response)
})
module.exports = router;
You just need to return the result from SerpApi in your handlePictures function. To do this make a new Promise and when search.json runs callback do what you need with the results and pass it in resolve.
Your picturesController.js with an example of returning all results.
//pictures controller
const SerpApi = require("google-search-results-nodejs");
const { json } = require("express");
const search = new SerpApi.GoogleSearch(process.env.API_KEY); //your API key from serpapi.com
const handlePictures = async (req, res) => {
return new Promise((resolve) => {
const params = {
q: "Coffee",
location: "Austin, Texas, United States",
hl: "en",
gl: "us",
google_domain: "google.com",
};
const callback = function(data) {
resolve(data);
};
search.json(params, callback);
});
};
module.exports = { handlePictures };
Output:
And I advise you to change your API key to SerpApi to prevent it from being used by outsiders.
Since I don't have the full context of your App I can just assume the context. But given the fact that you already have wrapped the logic of calling the external API into a dedicated controller you can use it in the following way in an express app (used the hello world example from express):
// import your controller here
const express = require('express')
const app = express()
const port = 3000
// for POST request use app.post
app.get('/', async (req, res) => {
// use the controller to request external API
const response = await yourController.method()
// send the response back to client
res.json(response)
})
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
Here's an example how to execute the http request from the frontend:
const response = await fetch('http://localhost:3000') // result from res.json(response)

Request body is undefined when the request is sent through the ARC in express

const express = require('express');
const app = express();
const oracle = require('oracledb');
const bodyParser = require('body-parser');
const urlEncodedBodyParser = bodyParser.urlencoded({extended:false});
app.use(bodyParser.json())
app.post("/addPlacement", urlEncodedBodyParser, function(request,response) {
console.log(request.body);
console.log(request.body.id);
/*console.log(request.body.name);
console.log(request.body.placementType);
console.log(request.body.company);
console.log(request.body.salary);
console.log(request.body.salaryType);
*/
response.send({"success": true});
});
app.listen(5050, function(err) {
if (err) {
console.log(err);
}
console.log("Server is ready to request on port 5050")
})
The console bar shows undefined when the request is being sent by the Advanced REST Client how I can solve this error?

Can't retrieve information from mongodb when deploying on heroku

The problem is as the title suggests. When I run my app locally, I'm able to retrieve information from MongoDB but on Heroku, undefined is returned. Should I connect to MongoDB in another way because if I hardcode some text everything works just fine. Here are my scripts:
function to get data
const MongoClient = require("mongodb").MongoClient;
const dbConnectionUrl = "mongodb+srv://xxxxxxx#cluster0.ro4dz.mongodb.net/data?retryWrites=true&w=majority";
const saySomething = (req, res, next) => {
// res.status(200).json({
// body: 'Hello from the server!'
// });
login()
.then(val=>res.send(val))
};
async function login(){
const client = new MongoClient(dbConnectionUrl)
try{
await client.connect();
const database = client.db("data");
const movies = database.collection("movies");
const query = { name: "toke" };
const movie = await movies.findOne(query);
return movie
}catch(err){
console.log(err)
}
}
module.exports.saySomething = saySomething;
router
const express = require('express');
const router = express.Router();
const controllers = require('./../controllers/controllers');
router.get('/say-something', controllers.saySomething);
module.exports = router;
server
// Import dependencies
const express = require('express');
const cors = require('cors');
const path = require('path');
// Create a new express application named 'app'
const app = express();
// Set our backend port to be either an environment variable or port 5000
const port = process.env.PORT || 5000;
// This application level middleware prints incoming requests to the servers console, useful to see incoming requests
app.use((req, res, next) => {
console.log(`Request_Endpoint: ${req.method} ${req.url}`);
next();
});
// Configure the CORs middleware
// Require Route
app.use(cors());
const api = require('./routes/routes');
// Configure app to use route
app.use('/api', api);
// This middleware informs the express application to serve our compiled React files
if (process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'staging') {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
};
// Catch any bad requests
app.get('*', (req, res) => {
res.status(200).json({
msg: 'Catch All'
});
});
// Configure our server to listen on the port defiend by our port variable
app.listen(port, () => console.log(`BACK_END_SERVICE_PORT: ${port}`));
front
import { useEffect, useState } from 'react';
import './App.css';
import axios from 'axios'
function App(){
useEffect(()=>{
get()
})
const[text, settext] = useState('')
async function get(){
let request = await axios.get('/api/say-something')
console.log(request.data.name)
settext(request.data.name)
}
return(
<div>{text}</div>
)
}
export default App;
I solved the issue! The first thing I did was that I added MongoDB connection URI as an environmental variable in my app via Heroku. Secondly, I added an option in MongoDB so that the cluster can be accessed from any computer. By default, the access is set to the local computer so I added another IP, namely 0.0.0.0/0 to my cluster, and now everything works just fine.

Error in sending a post request using Rest Client , Express and NodeJs

const express = require('express')
const routing = express.Router()
const flightMethods = require("../model/users")
//implement routing as per the given requirement
routing.post('/bookFlight', (req, res, err, next) => {
flightBookingObj = JSON.parse(req.body);
flightMethods.bookFlight(flightBookingObj).then((id) => {
return res.status(201).json({"message": `Flight booking is successful with booking id ${id}`})
})
if(err){
return next(err);
}
})
module.exports = routing;
Router.js File
Is this the correct way to pass a json data with Rest client, which sends an object of application/json type.
{"customerId": "P1001",
"bookingId": 2001,
"noOfTickets": 3,
"bookingCost": 1800,
"flightId":undefined
}
FlightBookingObj is defined as
class FlightBooking {
constructor(obj) {
this.customerId = obj.customerId;
this.bookingId = obj.bookingId;
this.noOfTickets = obj.noOfTickets;
this.bookingCost = obj.bookingCost;
this.flightId = obj.flightId;
}
}
POST request to this route gives an error on the REST Client.
Here is a snippet of code to handle Post requests in NodeJs with Express
main.js
const express = require('express');
const routing = require('./routes.js');
const app = express();
app.use('/', routing);
var server = app.listen(3000, function(){
});
routes.js
const express = require('express');
var routing = express.Router();
routing.route('/bookFlight').post(function(req, res, next)
{
var flightBookingObj = req.body.flight;
// Assuming your post request has an object called flight in its body
});
module.exports = routing;

How to Send a Response Using Seneca and Express

I'm using Seneca to route API calls and express to serve my files.
The problem is I can't seem to find a way to send a response back to the client after getting my data from the API.
With express, I would just use res.send, but since I'm in the Seneca context I can't. Haven't found any reference to this issue in the documentation.
"use strict";
const bodyParser = require('body-parser');
const express = require('express');
const jsonp = require('jsonp-express');
const Promise = require('bluebird');
const path = require('path');
const seneca = require('seneca')();
const app = express();
module.exports = (function server( options ) {
seneca.add('role:api,cmd:getData', getData);
seneca.act('role:web',{use:{
prefix: '/api',
pin: {role:'api',cmd:'*'},
map:{
getData: {GET:true} // explicitly accepting GETs
}
}});
app.use( seneca.export('web') )
app.use(express.static(path.join(__dirname, '../../dist/js')))
app.use(express.static(path.join(__dirname, '../../dist/public')))
app.listen(3002, function () {
console.log('listening on port 3002');
});
function getData(arg, done){
//Getting data from somewhere....
//Here I would like to send back a response to the client.
}
}())
Looks like the 'web' related functionality is now moved into module 'seneca-web' along with separate adapter for express. I got the below modified version to work.
"use strict";
const express = require('express');
const app = express();
const seneca = require('seneca')({ log: 'silent' });
const web = require('seneca-web');
let routes = [{
prefix: '/api',
pin: 'role:api,cmd:*',
map: {
getData: {
GET: true
}
}
}];
let config = {
context: app,
routes: routes,
adapter: require('seneca-web-adapter-express')
};
seneca.add('role:api,cmd:getData', getData);
seneca.use(web, config);
function getData(arg, done){
done(null, {foo: 'bar'});
}
seneca.ready(() => {
app.listen(3002, () => {
console.log('listening on port 3002');
});
});
According to the senecajs documentation, you should be able to just invoke done() within your getData method to return/send a value/response. Consider the following:
Here, I was able to hit /api/getData and receive {foo: 'bar'} the response.
"use strict";
const express = require('express');
const seneca = require('seneca')();
const app = express();
seneca.add('role:api,cmd:getData', getData);
seneca.act('role:web',{use:{
prefix: '/api',
pin: {role:'api',cmd:'*'},
map:{
getData: {GET:true} // explicitly accepting GETs
}
}});
app.use(seneca.export('web'));
app.listen(3002, function () {
console.log('listening on port 3002');
});
function getData(arg, done){
done(null, {foo: 'bar'});
}

Resources