This question already has an answer here:
Express routes parameters
(1 answer)
Closed last month.
I'm trying to send a POST to my server, in order to edit a user's details. I've made sure it's sending to the right URL, however get a 404 response. GET requests work fine, however my POST doesn't seem to get through for whatever reason. I've been searching for solutions for a while, with no luck hence posting here!
user.js (server side)
userRoutes.route('/user/update/:id').post(function (req, response) {
let db_connect = dbo.getDb("preview");
let myquery = { _id: ObjectId(req.params.id) };
let newValues = {
$set: {
name: req.body.name,
user_name: req.body.user_name
},
};
db_connect
.collection("users")
.updateOne(myquery, newValues, function (err, res) {
if (err) throw err;
console.log('user updated');
response.json(res);
})
});
middleware
export const updateUser = async (id, userDetails) => {
const endpoint = `${serverIp}/user/update/?id=${id}`;
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify(userDetails)
})
const result = await response.json();
return result;
} catch (error) {
console.log(error)
}
}
and a simple function to handle submitting
function handleSave() {
const newUserDetails = {
name: accountHolder,
user_name: accountUsername
};
updateUser(userId, newUserDetails);
}
Under networking in dev tools I can see the URL is indeed correct, so I can't see why this isn't working
chrome dev tools
Any help would be greatly appreciate it!
I've tried sending a basic response (i.e. a string instead of object), changing the endpoint, and more all to no avail
It seems like you are passing the id as a query param and not as part of the path
const endpoint = `${serverIp}/user/update/?id=${id}`;
^
What I can see from first glance is that in server-side you are using request parameter for id, but in the client you're sending id as a request query
I am following a MailChimp API tutorial
When I test the API, I get a 401 response saying my API key is invalid.
Error -
Status: 401
"Your API key may be invalid, or you've attempted to access the wrong datacenter."
I have yet to register a domain yet, this is being testing using a local server. Could this be error be caused by MailChimp's refusing the request for another reason, perhaps CORS?
app.post('/signup', (req, res) => {
// Get form data
const { email } = req.body;
// Make sure field is filled
if(!email) {
res.redirect('/html/fail.html');
return;
}
// Construct req data
const data = {
members: [
{
email_address: email,
status: 'subscribed'
}
]
}
// Convert to JSON
const postData = JSON.stringify(data);
const options = {
url: 'https://us19.api.mailchimp.com/3.0/lists/listID',
method: 'POST',
headers: {
Authorization: 'auth xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-us19'
},
body: postData
};
request(options, (err, response, body) => {
if(err) {
console.log(err);
res.redirect('/html/fail.html');
} else {
if(response.statusCode === 200) {
res.redirect('/html/success.html');
} else {
console.log(response.body);
res.redirect('/html/fail.html');
}
}
});
})
I tried running the same code in request in PostMan and I got back a 200 response.
I was initially importing the API key from a config file, that I had not destructured...
I'm new to nodejs and I'm migrating my current API from python to nodejs using express.
What I'm trying to do is to make a request to an external API. I'm pretty sure my API call is right, since I copied from the external API example:
exports.getBalance = function() {
return new Promise(function(resolve, reject) {
var command_url = "/v1/transaction/getBalance";
var full_url = API_URL + command_url;
var nonce = getNonce();
var data = "username=" + convertUsername(API_USER) + "&nonce=" + nonce;
const signature = makeSignature(nonce, data, command_url);
var form = {
username: API_USER,
nonce: nonce
};
var formData = querystring.stringify(form);
var contentLength = formData.length;
var headers = {
"X-API-KEY": API_KEY,
"X-API-SIGN": signature,
"X-API-NONCE": nonce,
"Content-Length": contentLength,
"Content-Type": "application/x-www-form-urlencoded"
};
request(
{
url: full_url,
method: "POST",
headers: headers,
body: formData
},
function(error, response, body) {
if (!error) {
body = JSON.parse(body);
if (response.statusCode == 200) {
resolve(body);
} else {
reject(error);
}
} else {
console.log("error:", error);
reject(error);
}
}
);
});
This is my express route:
routes.post("/balance", mistertango.getBalance);
However, when I try to POST to this route, I don't receive nothing. I use Insomnia to run API tests, so Insomnia keeps running with no response from my express API.
I'd like to know how can I debug my code? I'd like to make an API call using Insomnia to my express API, and check if I'm getting a response from my external API request.
Thanks
mistertango.getBlance returns a Promise but express doesn't handle promises by default. You need to call res.send(data) to actually send a response to the client.
routes.post("/balance", async (req, res, next) => {
try {
const balance = await mistertango.getBalance()
res.send({ balance })
} catch (error) {
next(error)
}
})
Or without async/await:
routes.post("/balance", (req, res, next) => {
mistertango.getBalance()
.then(balance => res.send({ balance }))
.catch(error => next(error))
})
Note 1: You might be able to use res.send(balance) instead of res.send({ balance }) as long as balance is not a number. (Response body cannot be a raw number, so I've wrapped it in an object).
Note 2: In both cases, we have to use .catch or try/catch to handle any errors because express won't handle rejected promises on its own. You can use express-promise-router to fix that!
My external routes is setup so that a POST form sends data to my server and my server file validates the input. It then takes this input and passes it to another file which will then perform authentication. I have a few questions regarding this, which are:
1) Do I need to pass the data asynchronously?
2) Is the way I am currently passing synchronous? Should it be changed
3) If I want to pass my variables into this file and it uses an async waterfall, (I believe I can't pass variables into the first function) should I create a main function which has two sub functions (one for storing the variables and one for executing the async waterfall)?
I am new to Node.js so have only just started learning the concept of asynchronous functionality. Any tips, help or questions, please let me know. If i have left something out please ask
My Server function is as such:
//Route for POST requests to Login
//First Sanitise input!
app.route('/login').post([
//ACCOUNTTYPE CHECKS -------------------------------------------------------------------------------
check('accountType').exists()
.trim()
.isAlpha()
.isLength({
min: 8,
max: 10
})
.matches(/(Production|Internal)/).withMessage('Must be either \'Production\' or \'Internal\'\!')
.escape(),
//CUSTOMERID CHECKS --------------------------------------------------------------------------------
check('customerID').exists()
.trim()
.isNumeric()
.isInt()
.isLength({
max: 40
})
.escape(),
//CLIENTID CHECKS ---------------------
check('clientID').exists()
.trim()
.isAlphanumeric()
.isLength({
max: 40
})
.escape(),
//CLIENTSECRET CHECKS ---------------------
check('clientSecret').exists()
.trim()
.isAlphanumeric()
.isLength({
min: 0,
max: 45
})
.escape(),
//USERNAME CHECKS ---------------------
check('username').exists()
.trim()
.isEmail()
.isLength({
min: 0,
max: 100
})
.escape(),
//PASSWORD CHECKS ---------------------
check('password').exists()
.trim()
.isLength({
min: 0,
max: 100
})
.escape()
], urlencodedParser, (req, res) => {
let now = new Date().toString(),
log = `${now}: ${req.method} ${req.url}`,
sess = req.session;
console.log(req.body);
fs.appendFile('server-requests.log', log + '\n', (err) => {
if (err) {
console.log('Unable to append to server-requests.log!');
}
});
// Finds the validation errors in this request and wraps them in an object with handy functions
const errors = validationResult(req);
if (!errors.isEmpty()) {
//return a bad status and array of errors
//if the validation fails
// return res.status(422).json({
// errors: errors.array()
// });
//OR:
return res.render('login.hbs', {
currentYear: new Date().getFullYear(),
//This does not work realtime - FIX TODO
currentTime: moment().format('MMMM Do YYYY - h:mm:ss a'),
data: {},
errors: errors.mapped()
});
// If there aren't any errors, proceed to save variable data and use 2.0
} else {
//Handle with event emiitters and make asynchronous - TODO
const data = matchedData(req);
console.log('Data stored in session:')
console.log('Sanitised:', data);
//Store Form Data in session variables
sess.accountType = data.accountType,
sess.customerID = data.customerID,
sess.clientID = data.clientID,
sess.clientSecret = data.clientSecret,
sess.username = data.username,
sess.password = data.password;
console.log('Sanitised POST Form! Now initialising request.');
req.flash('success', 'Successful POST request, proceeding to authorisation...)');
//Make asynchronous?
//use each function and get variables
auth.saveVariables(sess.accountType, sess.customerID, sess.clientID, sess.clientSecret, sess.username, sess.password);
auth.asyncWaterfall(callback)
res.redirect('/index');
//Or alternatively should it redirect with a query string?
// const url = require('url');
// app.get('/index', function (req, res) {
// res.redirect(url.format({
// pathname: "/",
// query: {
// "acccountType": 1,
// "customerID": 2,
// "clientID": 3,
// "clientSecret": 4,
// "username": 5,
// "password": 6
// }
// }));
// });
}
})
Here is the start of my auth controller code:
const saveVariables = (accountType, customerID, clientID, clientSecret, username, password, callback) => {
//Test parameters
if (accountType.type() !== String || customerID.type() !== String || clientID.type() !== String || clientSecret.type() !== String || username.type() !== String || password.type() !== String) {
let fields = [accountType, customerID, clientID, clientSecret, username, password];
test(fields);
}
accountType,
customerID,
clientID,
clientSecret,
username,
password;
}
const test = (array) => {
array.forEach((field) => {
let errorString;
if (field.type() !== String) {
errorString += `The field ${field} is not a String. Type: ${field}.`
}
return errorString;
})
}
//This const creates the auth URL and auth code grant URL. It also assigns the host URL.
const createAuthURL = (callback) => {
//If the paramter test passes then proceed
if (accountType === "Production") {
try {
let authURL = `https://${prodURL}${url1}`,
authCodeURL = `https://${prodURL}${url2}`,
host = prodURL;
} catch (e) {
console.log(`Error occurred when assigning url variables (Production). Error: ${e}`);
console.error('Error occurred when assigning url variables (Production). Error:', e);
}
} else if (accountType === "Internal") {
try {
let authURL = `https://${internURL}${url1}`,
authCodeURL = `https://${internURL}${url2}`,
host = internURL;
} catch (e) {
console.log(`Error occurred when assigning url variables (Internal). Error: ${e}`);
console.error('Error occurred when assigning url variables (Internal). Error:', e);
}
} else {
console.log(`Bad String Input into auth URL! Entry: ${accountType}`);
console.error('Bad String Input into auth URL: ', accountType);
}
}
const asyncWaterfall = (req, res) => {
//Asynchronous Waterfall Call of 'Getting Access Tokens' (Stages 1-3)
async.waterfall([
getCookieData,
getAuthCode,
getAccessToken
], (err, result) => {
if (err) {
alert('Something is wrong!');
}
return alert('Done!');
});
//STAGE 1 - USE CUSTOMER DETAILS TO RETRIEVE COOKIE AND CSRF SESSION DATA
getCookieData = async (callback) => {
//For testing purposes - Remove after development
console.log(`The type of account used is: ${accountType}`);
console.log(`The customerID used is: ${customerID}`);
console.log(`The clientID used is: ${clientID}`);
console.log(`The client secret used is: ${clientSecret}`);
console.log(`The username used is: ${username}`);
console.log(`The password used is: ${password}`);
console.log(`Making a URL request to: ${authURL}`);
//If the paramter test passes then proceed
var options = {
//Type of HTTP request
method: 'POST',
//URI String
uri: authURL,
//HTTP Query Strings
qs: {
'client_id': clientID
},
//HTTP Headers
headers: {
'cache-control': 'no-cache',
'Accept': 'application/json',
'Host': host,
'Content-Type': 'application/json'
},
//HTTP Body
body: {
'username': username,
'password': password
},
json: true // Automatically stringifies the body to JSON
//resolveWithFullResponse: true // Get the full response instead of just the body
//simple: false // Get a rejection only if the request failed for technical reasons
};
console.log(`Beginning HTTP POST Request to ${authURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let csrf = response.body('csrftoken'),
ses = response.body('session'),
sesk = `session=${ses}`;
} catch (e) {
console.log(`STAGE 1 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 1 - Error occurred when assigning url variables. Error:', e);
}
console.log(`Successful grab of the cookie: ${ses} and csrf token: ${csrf}. Getting authorisation code now!`);
//Asynchronous callback for the next function - return = defensive architecture
return callback(null, authCodeURL, customerID, clientID, csrf, sesk);
})
.catch((err) => {
if (res.statusCode == 400) {
console.log(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
console.error('Error Message:', res.body.message, 'Status:', res.body.status);
} else if (res.statusCode == 401) {
console.log(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
console.error('Error Message:', res.body.message, 'Status:', res.body.status);
} else {
console.log(`Failed to retrieve the cookie data! Error: ${error}`);
console.error('Failed to retrieve the cookie data! Error:', error);
}
});
},
//STAGE 2 - USE COOKIES AND CSRF TOKEN TO GET AUTH CODE
//Is the word async needed? it is not asyncchronous but sequential
getAuthCode = async (authCodeURL, customerID, clientID, csrf, sesk, callback) => {
//Make sure all the data is in string format - Run Time Tests
if (authCodeURL !== String || cutomerID !== String || clientID !== String || csrf !== String || sesk !== String) {
let fields = [authCodeURL, cutomerID, clientID, csrf, sesk];
test(fields);
}
//If successful, proceed:
var options = {
method: 'POST',
uri: authCodeURL,
qs: {
'client_id': clientID,
'response_type': 'code',
'scope': 'all'
},
headers: {
'X-CSRF-TOKEN': csrf,
'Accept': 'application/json',
'Cookie': sesk,
'Content-Type': 'application/json'
},
body: {
'customer_id': customerID
},
json: true // Automatically stringifies the body to JSON
};
console.log(`Beginning HTTP POST Request to ${authCodeURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let authCode = response.body.JSON('auth_code'),
swapurl = `https://${host}${url3}`;
} catch (e) {
console.log(`STAGE 2 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 2 - Error occurred when assigning url variables. Error:', e);
}
console.log(`The authorisation Code is ${authcode}. Getting Access Token now!`);
//Asynchronous callback for the next function - return = defensive architecture
return callback(null, swapURL, clientID, clientSecret, authCode);
})
.catch((err) => {
if (res.statusCode == 400) {
console.log(`Error Message: ${res.body.message}. Extra: ${res.body.extra}`);
console.error('Error Message:', res.body.message, 'Extra:', res.body.extra);
} else {
console.log(`Failed to retrieve the authorisation code! Error: ${error}`);
console.error('Failed to retrieve the authorisation code! Error: ', error);
}
});
},
//STAGE 3 - USE AUTH CODE TO GET ACCESS TOKEN
//ASYNC NEEDED?
getAccessToken = async (swapURL, clientID, clientSecret, authCode, callback) => {
//Make sure all the data is in string format - Run Time Tests
if (swapURL !== String || clientSecret !== String || clientID !== String || authCode !== String) {
let fields = [swapURL, clientSecret, clientID, authCode];
test(fields);
}
//If successful, proceed:
var options = {
method: 'POST',
uri: swapURL,
qs: {
'client_id': clientID,
'grant_type': 'authorization_code',
'client_secret': clientSecret,
'code': authCode
},
json: true // Automatically stringifies the body to JSON
};
console.log(`Beginning HTTP POST Request to ${swapURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let accessToken = response.body('access_token'),
refreshToken = response.body('refresh_token');
} catch (e) {
console.log(`STAGE 3 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 3 - Error occurred when assigning url variables. Error:', e);
}
console.log(`The access Token is ${accessToken} and the refreshToken which is ${refreshToken}! These are only valid for 2 hours!`);
//Asynchronous callback for the waterfall - return = defensive architecture
return callback(null, 'done');
})
.catch((err) => {
console.log(`Failed to retrieve the access/refresh Token! Error: ${error}`);
console.error('Failed to retrieve the access/refresh Token! Error:', error);
});
}
}
I would like to use a NodeJS Server to obtain the current users on my Website from Google Analytic using the Real Time Reporting API:
So far I try to do this via an HTTP request with request and gtoken. The getToken-Part works. I get a token. But the HTTP-Request doesnt work. I get an "Invalid Credentials" Error with Code 401.
Does anybody have an idea what to do? Maybe this is the completely wrong approach to get these data.
var received_token;
var url = "https://www.googleapis.com/analytics/v3/data/realtime";
var paramsObject = { ids:"ga:123456789"};
const gtoken = new GoogleToken({
keyFile: 'pathToServiceAccountJSONKeyFile:',
scope: ['https://www.googleapis.com/auth/analytics.readonly']
});
gtoken.getToken(function(err, token) {
if (err) {
console.log(err);
return;
}
received_token = token;
console.log(token);
request({
url: url,
qs: paramsObject,
headers: {
'Authorization': received_token
}
}, function(err, response, body) {
if(err) { console.log(err); return; }
// console.log(response);
console.log(body);
});
I found the error :-)
In the Authorization Header "Bearer" was missing, now it works like charm and I receive data from the Google Real Time API.
headers: {
'Authorization': "Bearer " +received_token
}