express router, req.body undefined - node.js

I have a post interface that does not submit data correctly.
test shows: req.body undefined
const express = require('express');
const router = express.Router();
const passport = require("passport");
const passportInfo = passport.authenticate('jwt',{ session: false });
const HomeSchema = require('../../../models/AnvizHome');
const homeBannerValidator = require('../../../validation/anviz/homeBanner');
router.post("/banner",passportInfo,(req,res) => {
const {msg,isValid} = homeBannerValidator(req.body);
if(!isValid){
return res.status(400).json(msg);
}
HomeSchema.findOne({handle:req.body.handle}).then(banner => {
console.log('current: ' + req.body);
const newBanner = {
bannerBg:req.body.bannerBg,
bannerName:req.body.bannerName,
bannerSubName:req.body.bannerSubName,
bannerFeather:req.body.bannerFeather,
bannerLink:req.body.bannerLink
};
banner.prodcutBanner = newBanner;
banner.then(home => res.json(home));
})
.catch((err) => res.json(err));
});
module.exports = router;
postman test:
In fact, the terminal can see the returned data.
[Object: null prototype] {
bannerBg: '5555555555555555555555555555555555',
bannerName: 'THE GLOBAL LEADING',
bannerSubName: 'PROVIDER OF INTELLIGENT SECURITY',
bannerLink: 'www.anviz.com',
handle: 'true' }
Seeking one or two!Thank you!

you forgot to import and use body parser
https://www.npmjs.com/package/body-parser
var bodyParser = require('body-parser');
var express = require('express');
express.use(bodyParser.json());
express.use(bodyParser.urlencoded({ extended: false }));

I think I know the reason, because this is based on the user's token to submit data, so must be in the post according to the user id to determine whether the data is successful?
Here is the code that has been successfully saved, but I don't know right and wrong:
router.post("/banner",passportInfo,(req,res) => {
const {msg,isValid} = homeBannerValidator(req.body);
if(!isValid){
return res.status(400).json(msg);
}
const profileFields = {};
profileFields.prodcutBanner = {};
if(req.body.handle) profileFields.handle = req.body.handle;
if(req.body.bannerBg) profileFields.prodcutBanner.bannerBg = req.body.bannerBg;
if(req.body.bannerName) profileFields.prodcutBanner.bannerName = req.body.bannerName;
if(req.body.bannerSubName) profileFields.prodcutBanner.bannerSubName = req.body.bannerSubName;
if(req.body.bannerFeather) profileFields.prodcutBanner.bannerFeather = req.body.bannerFeather;
if(req.body.bannerLink) profileFields.prodcutBanner.bannerLink = req.body.bannerLink;
HomeSchema.findOne({user: req.user.id}).then(profile => {
if(profile){
HomeSchema.findByIdAndUpdate({user: req.user.id},{$set:profileFields},{new:true}).then(profile => res.json(profile));
}else{
HomeSchema.findOne({handle:profileFields.handle}).then(profile => {
if(profile){
msg.handle = "The user's handle personal information already exists, please do not re-create it!";
res.status(400).json(msg);
}
new HomeSchema(profileFields).save().then(profile => res.json(profile));
})
}
})
.catch((err) => res.json(err));});
module.exports = router;

Related

SWAPI Pagination with Node.JS, Express and Axios

I wanted to fetch all people/films/etc. from SWAPI.
I tried a lot of things before finally get something usable. However, it only shows the first 10. people (in the people case)
const express = require("express");
const router = express.Router();
const axios = require("axios");
router.get("/people", (req, res) => {
axios
.get("https://swapi.dev/api/people/?format=json")
.then((data) => {
return res.send(data.data.results);
})
.catch((error) => {
console.log("error: ", error);
});
});
module.exports = router;
What I tried, thanks to a lot of searches from Stack Overflow, is this :
const express = require("express");
const router = express.Router();
const axios = require("axios");
router.get("/people", async (req, res) => {
let nextPage = `https://swapi.dev/api/people/`;
let people = [];
while (nextPage) {
res = await axios(nextPage)
const { next, results } = await res.data;
nextPage = next
people = [...people, ...results]
}
console.log(people.length) // 82
console.log(people) // shows what I wanted, all people !
return people;
});
module.exports = router;
When starting the server, the page doesn't finish loading (it's still loading at this moment), but the console.log managed to show exactly what I wanted.
So, how can I manage to show this in the page ?
My goal is to use that route for axios API calls from a React front-end (searching for a specific name)
Do not overwrite the res in your code and finish it with res.send:
let nextPage = `https://swapi.dev/api/people/`;
let people = [];
while (nextPage) {
let nextres = await axios(nextPage)
const { next, results } = await nextres.data;
nextPage = next
people = [...people, ...results]
}
console.log(people.length) // 82
console.log(people) // shows what I wanted, all people !
res.send(people);

Cannot GET /[object%20Object] when calling axios.get()

When I paste the endpoint URL with query directly inside the axios.get(), it responds correctly and I can see the json object returned. (i.e axios.get(http://localhost:3000/api/products/product_search?secretKey=${secret}&id=${blabla})). However, if I call the url with the summonerByNameUrl method, it crashes when I make a request. What is the problem in my code?
Crash report:
...
data: '<!DOCTYPE html>\n' +
'<html lang="en">\n' +
'<head>\n' +
'<meta charset="utf-8">\n' +
'<title>Error</title>\n' +
'</head>\n' +
'<body>\n' +
'<pre>Cannot GET /[object%20Object]</pre>\n' +
'</body>\n' +
'</html>\n'
},
isAxiosError: true,
toJSON: [Function: toJSON]
Code:
config.js
const summonerByNameUrl = (summonerName) => `${URL(hidden)}${summonerName}`;
module.exports = {
summonerByNameUrl
}
summoner.js
const config = require('../config');
const axios = require('axios');
const getSummonerByName = async (summonerName) => {
const res = await axios.get(config.summonerByNameUrl(summonerName));
return res.data;
}
const summonerParser = async (req, res) => {
if(!req.query.secretKey)
return res.status(403).json({error: 'missing secret key.'})
let data = await getSummonerByName(req.query)
return res.status(200).json(data);
}
module.exports = {
getSummonerByName,
summonerParser
}
products.js
var express = require('express');
var axios = require('axios')
var router = express.Router();
const summoner = require('../services/summoner');
router.get('/product_search', summoner.summonerParser)
module.exports = router;
app.js
...
app.use('/api/products', productsRouter);
...
You're calling your function with getSummonerByName(req.query) where it is clear from the lines just before that req.query is an object and not a string. When objects are used in a string-context (like your URL), they become "[object Object]", hence the error.
Taking some guesses here but it seems you want to forward some req.query information to the Axios call as query params. Try this instead...
const PRODUCT_SEARCH_URL = "http://localhost:3000/api/products/product_search"
const getSummonerByName = async ({ secretKey, id }) => {
const { data } = await axios.get(PRODUCT_SEARCH_URL, {
params: { secretKey, id }
})
return data
}
If you've got a helper function that returns the base URL (ie http://localhost:3000/api/products/product_search) then by all means, use that instead of a string literal in the Axios call.
The req.query is a Object, not a string.
You can try map the req.query object to make a string. Something like that:
Object.keys(req.query).map(key => {
return key + '=' + req.query[key]
}).join('&')
This code return a string like that: 'id=1&name=test', so you can pass to the endpoint.

Switching databases in Mongo on ExpressJS application without reconnecting each time

I want to preface by saying I have looked for this issue online and found solutions using mongoose, I do not use that so I don't know how well it translate here. Plus, I also using async/await instead of .then, so my code is different.
My question is that the API I have made in ExpressJS is super slow compared to the same API I have in Flask. I tried seeing why this is an issue, since JS is supposed to be faster than Python. I noticed it was due to me connecting to Mongo each time. I need to do this as my MongoDB has a different databases for different clients, so I cannot just have 1 constant connection to one database. Below is my Mongo code.
const {MongoClient} = require('mongodb');
const client = new MongoClient("mongodb+srv://abc#xyz.mongodb.net/<Cluster");
class MongoMethods {
constructor(company, collection){
this.company = company;
this.collection = collection;
}
async get(accessParameters){
try{
await client.connect();
const db = client.db(this.company);
const collection = db.collection(this.collection);
const val = await collection.findOne({"_id":accessParameters["at"]});
return val
} finally {
await client.close();
}
}
async putIn(putParameters, type){
try{
await client.connect();
const db = client.db(this.company);
const collection = db.collection(this.collection);
var existing = await collection.findOne({"_id":putParameters["at"]});
if(type === "array"){
var toAdd = existing !== undefined && putParameters["in"] in existing? existing[putParameters["in"]] : [];
toAdd.push(putParameters["value"]);
}else if(type === "dict"){
var toAdd = existing !== undefined && putParameters["in"] in existing? existing[putParameters["in"]] : {};
toAdd[putParameters["key"]] = putParameters["value"];
}else{
var toAdd = putParameters["value"];
}
await collection.updateOne({"_id":putParameters["at"]}, {"$set": {[putParameters["in"]]: toAdd}}, {upsert: true});
} finally {
await client.close();
}
}
async remove(removeParameters){
try{
await client.connect();
const db = client.db(this.company);
const collection = db.collection(this.collection);
if(removeParameters["key"] !== undefined){
await collection.updateOne({"_id":removeParameters["at"]}, {"$unset": {[removeParameters["in"] + "." + removeParameters["key"]] : ""}})
}else if(removeParameters["in"] !== undefined){
await collection.updateOne({"_id":removeParameters["at"]}, {"$unset": {[removeParameters["in"]] : ""}})
}else{
await collection.deleteOne({"_id":removeParameters["at"]})
}
}finally{
await client.close();
}
}
}
module.exports.MongoMethods = MongoMethods;
Below is how functions in my ExpressJS file look (I cannot post this file so this is an excerpt):
var express = require('express');
var cors = require('cors')
const bodyParser = require('body-parser');
const { validate, ValidationError } = require('express-superstruct');
const { MongoMethods } = require('./mongo.js')
let app = express()
app.use(cors())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.route('/enp1/')
.get(validate({uid : "string", site : "string"}), function (req, res) {
var args = req.query
var mongoClient = new MongoMethods(args.site, "collection_1")
mongoClient.get({at : args.uid}).catch(console.dir).then(result => {
if(result == undefined){ // This may need to take into account an empty dict
res.status(404).json({"Database Error" : "UID does not exists or site is not a valid website"})
}
res.status(200).json(result)
})
})
app.route('/enp2/')
.put(validate({site : "string", uid : "string", time : "string"}), function (req, res) {
var args = req.body;
var mongoClient = new MongoMethods(args.site, "time")
mongoClient.putIn({at : args.uid, in : "time", value : parseInt(args.time)}, "int").catch(console.dir).then(result => {
res.status(200).end()
})
})
It seems like this code is connecting to Mongo each time as I have to initialize the MongoMethods object each time. Can I prevent it from trying to connect each time so that my API doesn't have slow speeds? When I compare speeds, JS endpoints without Mongo are 50% faster than their Python counterpart but when using Mongo endpoints, it is around 300ms slower.
Let me know if you need anymore clarification.
Thanks in advance!
Edit 1: I wanted to mention, the API runs on AWS API Gateway and AWS Lambda#Edge functions.

AWS-Serverless-Express How To Get Previous Route

I have an export button on my front-end that when clicked, sends a POST on our Express server to log the button click. This uses the route app.post(usagereport) . What I want to be able to do is capture the route the user was at when they clicked export. However, since the code to send the POST request is it's own route, it only ever returns that route's name when trying something like req.route.
I'm using API Gateway + Lambda + AWS-Serverless-Express.
I was thinking I could store something like req.session.previousRoute in req.session to capture the last route the user loaded and then return this to the access log code. However, I was unsure if this approach would work on Lambda or perhaps there is just a better way to handle it.
Here is my server.js (trimmed down)
// create the server and setup routes
const app = express();
const mysql = require("mysql");
// AWS-Serverless-Express https://github.com/awslabs/aws-serverless-express
const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware");
app.use(awsServerlessExpressMiddleware.eventContext());
//Setup paths to database connection pools
const nawfprojectsDB = require("../lib/naWfProjectsDb.js");
const queries = require("./queries.js");
const accessLog = require("../lib/accessLog.js");
//Setup a timestamp for logging
const timestamp = new Date().toString();
// S3 Data Mitigation is needed when a data set exceeds 5 MB in size.
// This is a restriction of Lambda itself (they say 6 MB but want to ensure we dont ever hit the limit)
const s3DataMitigation = require("../lib/s3DataMitigation.js");
let environment = process.env.NODE_ENV;
app.get("/wg_data", (req, res, callback) => {
const dataSet = "wg_data";
nawfprojectsDB.query(queries.wg_data, (err, result) => {
if (err) {
console.log(err);
}
s3Data(dataSet, res, callback, result);
console.log(
timestamp,
"Returned " + result.length + " rows from " + dataSet
);
});
accessLog({ dataSet, req });
});
// Usage report everytime export button is clicked
app.post("/usagereport", (req) => {
const currentPath = dataSet;
const dataSet = "Data Exported: " + currentPath;
console.log(timestamp, "Data exported");
accessLog({ dataSet, req });
});
module.exports = app;
accessLog.js
let nawfprojectsDB = require("./naWfProjectsDb.js");
let queries = require("../routes/queries.js");
let environment = process.env.NODE_ENV;
//Insert data into access_logs table when usageLog is called
const accessLog = ({ dataSet, req }) => {
// We only want to log access when in beta, gamma, or prod. Not in development.
if (environment === "development") {
console.log("No access log as we are in dev");
} else {
// req.apiGateway comes from AWS-Serverless-Express - https://github.com/awslabs/aws-serverless-express
const user = req.apiGateway.event.requestContext.authorizer.principalId;
let sqlData = [dataSet, user, environment];
// Run the log_access query using the sqlData above
nawfprojectsDB.query(queries.log_access, sqlData, (err) => {
if (err) {
console.error("MySQL query error: " + err);
}
console.log("Access log added for: ", user, " at data set: ", dataSet);
});
}
};
module.exports = accessLog;
Solved my own question.
The way solved this was using express-session. I set req.session.previousRoute within each of my routes. I can then access this in my usagereport route.
const { v4: uuidv4 } = require("uuid");
const express = require("express");
const cookieParser = require("cookie-parser");
const session = require("express-session");
const randomString = uuidv4();
let sessionOptions = {
cookie: {
secret: randomString,
maxAge: 269999999999,
},
saveUninitialized: true,
resave: true,
};
// create the server and setup routes
const app = express();
// Add express-session Middleware - https://www.npmjs.com/package/express-session
app.use(cookieParser(randomString)); // Need cookieParser to properly parse our random string into the type of value expected by session
app.use(session(sessionOptions));
// AWS-Serverless-Express - https://github.com/awslabs/aws-serverless-express
const awsServerlessExpressMiddleware = require("aws-serverless-express/middleware");
app.use(awsServerlessExpressMiddleware.eventContext());
app.get("/wg_data", (req, res, callback) => {
const dataSet = "wg_data";
const action = "Accessed";
req.session.previousRoute = dataSet; // This is where we set the previousRoute in session
nawfprojectsDB.query(queries.wg_data, (err, result) => {
if (err) {
console.log(err);
}
s3Data(dataSet, res, callback, result);
console.log(
timestamp,
"Returned " + result.length + " rows from " + dataSet
);
});
accessLog({ dataSet, action, req });
});
// Usage report everytime export button is clicked
app.post("/usagereport", (req) => {
// Here we grab the previousRoute set in session to see the true place the data was exported from
const action = "Exported";
const previousRoute = req.session.previousRoute; // Now when usagereport is triggered, it knows the previous route from the session and uses that here.
const dataSet = previousRoute;
console.log(timestamp, "Data exported from ", previousRoute);
accessLog({ dataSet, action, req });
});

Dialogflow webhook fulfillment parameter not accessible

I want to get input of a parameter in my webhook fulfillment.
Here is the my code:
const bodyParser = require('body-parser')
var request = require('request-promise-native');
const { dialogflow } = require('actions-on-google');
const assistant = dialogflow({
clientId: "30xxxxx08407-rv9kxxxxxxxxuuq8f9ul2eg.apps.googleusercontent.com"
});
module.exports = (app) => {
const logger = console;
assistant.intent('Sales', conv => {
const pcode = agent.parameters['PCODE'];
console.log(pcode)
const token = '3369708919812376';
const serviceID = '502';
const P_STATE_CD = 'ALL';
const P_FO_CD = 'ALL';
const P_DISTT_CD = 'ALL';
const P_DATE = '16/12/2019';
const P_PRD_GROUP = 'UREA';
const P_PERSONAL_NO = '106296';
var data = {"token" : token,"serviceID" : serviceID,"P_STATE_CD" : P_STATE_CD,"P_FO_CD" : P_FO_CD,"P_DISTT_CD" : P_DISTT_CD,"P_DATE" : P_DATE,"P_PRD_GROUP" : P_PRD_GROUP,"P_PERSONAL_NO" : P_PERSONAL_NO };
var sdata = JSON.stringify(data);
const options = {
method: 'POST',
uri: 'http://Webservice/resources/webservice/service' ,
body: JSON.parse(sdata) ,
json: true
}
return request(options)
.then( body => {
var unit = body
console.log(body)
unit.intent = "Sales"
unit.value1 = unit.saleInfo[0].QMTD
unit.value2 = unit.saleInfo[0].QYTD
unit.value3 = unit.saleInfo[0].O_UOM
unit.value4 = null
unit.value5 = null
delete unit.saleInfo
var unit2 = JSON.stringify(unit)
console.log(unit2)
conv.ask(unit2);
})
.catch( err => {
console.error( err );
conv.ask('Something went wrong. What should I do now?');
});
})
I tried with const pcode = agent.parameters.PCODE but it is not working. Giving me error:
ReferenceError: agent is not defined
at assistant.intent.conv (/home/dbalounge/GoogleDF/service.js:15:16)
at Function. (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:151:27)
at Generator.next ()
at /home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:22:71
at new Promise ()
at __awaiter (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:18:12)
at Function.handler (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/service/dialogflow/dialogflow.js:85:16)
at Object. (/home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/assistant.js:55:32)
at Generator.next ()
at /home/dbalounge/GoogleDF/node_modules/actions-on-google/dist/assistant.js:22:71
agent is not defined anywhere in your code, that's why you're getting:
ReferenceError: agent is not defined
In any case if you use assistant.parameters won't work either. Dialogflow intent parameters can be accessed through the second argument of .intent callback.
assistant.intent('Sales', (conv, params) => {
const pcode = params.PCODE;
/* ... */
})
For more info you can check the docs

Resources