How to Send a Response Using Seneca and Express - node.js

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'});
}

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)

Why my POST request work only with debugger?

I try to write CRUD and checking it up with Postman.
When I use a debugger at the end of the post request it's working.
Problem: When I remove the debugger and try to run the code nothing happens - only error: "Could not get any response". How can I make my post to work well without debugger?
***For example, I send this as JSON through postman like this:
{
"id":3,
"name": "Ron",
"phone": "01323133333"
}
this is the relevant code:
index.js:
const express = require('express');
const app = express();
db = require('./db');
app.use(express.static('public'));
const bodyParser = require('body-parser');
app.use(bodyParser.json());
const port = 1400;
app.listen(port,()=>{
console.log(`Server is running on port ${port}`);
})
//post request:
app.post('/user' , (req,res)=> {
const {body} = req,
{id,name,phone} = body
db.query(`INSERT INTO public.users(
id, name, phone)
VALUES (${id}, '${name}', '${phone}');`,(err,dbRes)=>{
if(err)
res.status(400).send(err);
else
{
res.send(dbRes.rows);
}
})
})
db.js:
const {Client} = require ('pg');
const client = new Client ({
user:'postgres',
host:'localhost',
database:'nodeapp',
password:'123456',
port:5432
})
client.connect();
module.exports = client;
Thank you !

Export object or variable between units or modules in node js

I need export an object or variable from my app to a router module.
The object is called "page" into "ClientClass".
I read something in SO and I tried to use a global var to save the object, exports it on end of unit.
This object will be used in the router module.
But, I have no success. In the router module "page" is always undefined.
How could I do that?
Main App JS - ClientClass.js:
const express = require('express');
const app = express();
const WARoutes = require('./routes/WARoutes');
var globalpage;
export default class ClientClass {
constructor(options) {
this.options = ...
}
async start() {
const browser = ....
const page = await browser.newPage();
// set globalpage to export
globalpage = page;
console.log('Done!');
app.use(express.json({ limit: '20mb' }));
app.use('/whats', WARoutes);
app.listen(port, () => {
console.log(`Listening on ${this.callbackUrl}...`);
});
}
start();
};
module.exports.page =globalpage;
WARoutes.js:
const express = require('express');
const router = express.Router();
const pagebrowser = require('../ClientClass.js');
const page = pagebrowser.page;
router.get('/getChats', async (req, res) => {
const chats = await page.evaluate((includePict, done) => {
do sometthing; //Here is my problem - page is undefined
}, includePict, done);
res.send(chats);
});
module.exports = router;
You have a cyclical dependency. You need to pass your page variable to the WARoutes.js implementation. Here's one way to do it:
WARoutes.js:
const express = require('express');
const router = express.Router();
//export a function which takes the `page` variable, *returning* the router which used to be _exported_
module.exports = function(page){
router.get('/getChats', async (req, res) => {
const chats = await page.evaluate((includePict, done) => {
do something;
}, includePict, done);
res.send(chats);
});
return router;
}
ClientClass.js:
const express = require('express');
const app = express();
const WARoutes = require('./routes/WARoutes');
export default class ClientClass {
constructor(options) {
this.options = ...
}
async start() {
const browser = ....
const page = await browser.newPage();
console.log('Done!');
app.use(express.json({ limit: '20mb' }));
app.use('/whats', WARoutes(page));
app.listen(port, () => {
console.log(`Listening on ${this.callbackUrl}...`);
});
}
start();
};
P.S.
I am also curious about what you're passing to page.evaluate. The first is a function with two arguments, the second and third are those two arguments again. I have a sneaking suspicion this is not going to work even as modified. You're going to need to provide more information about the page.evaluate API for additional help with that.

Node, Express - Index file not calling files correctly

Hi in my express project, I have my index file where I require different files to startup my application. These require a database connection file, a file for logging stuff using winston and a file for configuring routes.
I use the require() statement within express to call these files, and when I run the application(using nodemon), I expect some messages to be logged to the terminal verifying that the files have been called, however no messages occur.
Here is my code:
index.js:
const express = require('express')
const app = express()
require('./startup/logging') ()
require('./startup/db') ()
require('./startup/routes') (app)
const port = process.env.PORT || 3000
app.listen(port, () => winston.info(`Listening on port: ${port}`))
db.js:
const mongoose = require('mongoose')
const winston = require('winston')
module.exports = function() {
mongoose.connect('mongodb://localhost/dwg', {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => winston.info('Connected to MongoDB...'))
.catch(err => console.error("Error"))
}
logging.js:
const winston = require('winston');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' }));
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'logfile.log' });
}
routes.js:
const express = require('express');
module.exports = function(app) {
app.use(express.json())
}
No database is created when running the application. I can confirm this by looking at mongodb compass. The message that is meant to be printed by app.listen() is also not printed to the console. Does anybody know the issue? Thank you.
The problem doing it this way is your app starts before it gets a chance to do rest of work like creating db connection etc. You should start the app only when these tasks are done. something like this
const express = require('express')
const app = express()
const logging = require('./startup/logging');
const db = require('./startup/db');
const routes = require('./startup/routes');
const port = process.env.PORT || 3000
app.listen(port, async () => {
await logging();
await db();
await routes();
// assuming you have winston here.
winston.info(`Listening on port: ${port}`)
})
Mongo part is defintely async so need await. Check if routes and logging needs await or not.

Server initiated server-client websocket messages in nodejs?

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 });
...

Resources