Post method in Node.js - javascript

I need some help on with a post method.
Here is a post method I use for some card payment using stripe
app.post('/charge', (req, res) => {
const amount = 2500;
stripe.customers
.create({
email: req.body.stripeEmail,
source: req.body.stripeToken,
})
.then((customer) =>
stripe.charges.create({
amount,
description: 'Web Development eBook',
currency: 'usd',
customer: customer.id,
})
)
.then((charge) => res.render('success'));
});
in the same method I want to add the following method which works for now only with postman
exports.addTransaction = async (req, res, next) => {
try {
const { text, amount } = req.body;
const transaction = await Transactions.create(req.body);
return res.status(201).json({
success: true,
data: transaction,
});
} catch (err) {
return res.status(500).json({
success: false,
error: 'Server Error! Transaction was not added',
});
}
};
and the body that I want to send through the POST method is smth like this
{
"text": "Book",
"amount": -23
}

I think you are confused by the async/await example. The async/await syntax works on the promise, just like the then function, the await waits for the function execution and returns the data to the variable which awaits the same.
Consider the following examples to check the difference between async/await and the promise syntax
function myPromise() {
return new Promise((resolve, reject) => {
resolve(5);
});
}
Now, we can call the above function with two different syntax
myPromise.then((data) => {
console.log(data); // prints 5
});
Or, we can use modern async/await like this,
const data = await myPromise();
The above function will only work if the function in which it is wrapped has async in the beginning.
for example:
async function getData() {
const data = await myPromise();
console.log(data);
}
Notice the async keyword before the function keyword, without the async keyword the function would not work.
Also, just like catch in the regular promise, we use try/catch to catch the errors.
Now, coming back to your problem, you could convert the second function to use then().catch() order or you could convert your first function to use async/await style. Or you can use the combination of both.
Below, I've implemented the then/catch version of your second function inside your first function
app.post('/charge', (req, res) => {
const { text, amount } = req.body;
stripe.customers
.create({
email: req.body.stripeEmail,
source: req.body.stripeToken,
})
.then((customer) =>
stripe.charges.create({
amount,
description: 'Web Development eBook',
currency: 'usd',
customer: customer.id,
});
)
.then((charge) => {
Transactions.create(req.body).then(() => {
res.status(201).json({
success: true,
data: transaction,
});
}).catch((e) => {
res.status(500).json({
success: false,
error: 'Server Error! Transaction was not added',
});
});
});
});

Related

Using async and await to handle queries

Having issues with using async and await. I'm executing two queries and then saving the result to a temp variable. After I have collected the response from all executed queries, I'm going to send that to the client.
Here is my current example code.
module.exports = (app) => {
app.get('/api/stats', (req, res) => {
let fetch1 = '';
let fetch2 = '';
conn.query('query here', [], async (error, results) => {
if (error) {
return res.send({
success: false,
message: 'There was an error.'
});
} else {
fetch1 = results;
}
});
conn.query('query here', [], async (error, results) => {
if (error) {
return res.send({
success: false,
message: 'There was an error.'
});
} else {
fetch2 = results;
}
});
// I need to wait until the queries have resolved so that I can send the correct data
return res.send({
success: true,
fetch1: fetch1,
fetch2: fetch2
});
});
};
I basically need to wait until the queries have been resolved so that I can send the correct data towards the end.
Can anyone explain how I can use await and async to accomplish this?
Thanks.
You can only await a Promise, so for functions that don't return Promises you need to create a Promise wrapper. This needs to be done per call that would previously use a callback, but you can make a helper function per function you need to wrap.
function queryPromise(query, parameters) {
return new Promise((resolve, reject) => {
conn.query(query, parameters, (err, results) => {
if(err) {
reject(err);
} else {
resolve(results);
}
});
});
}
module.exports = (app) => {
app.get('/api/stats', async (req, res) => {
try {
let fetch1 = await queryPromise('query here', []);
let fetch2 = await queryPromise('query here', []);
res.send({
success: true,
fetch1: fetch1,
fetch2: fetch2
});
} catch {
res.send({
success: false,
message: 'There was an error.'
});
}
});
};
From my knowledge, I usually apply async to functions and perform await on certain variables (inside the function) that need to be acquired from a specific database.
So to implement this into your function containing the async tag, you could possibly do:
fetch1 = await results;
fetch2 = await results;
This will wait until the data is attached onto the variable fetch1 and fetch2 before continuing on with the code.
Sorry if this was very vague, hopefully this was somewhat helpful.

Node JS throwing cannot set headers after they are sent to the client, after using mongoose.removeOne

I have a method that deletes products and before it does it check if the user who is trying to delete the product is the user who created it. When i execute it with Insomnia it successfully removes the product but i get an error on the console saying cannot set headers after they are sent to the client.
My method:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, () => {
return res.status(401).json("Not authorized");
})
.then(() => {
return res.status(200).json("Product deleted");
})
.catch((err) => {
return res.status(500).json({
error: err,
});
});
};
I'm pretty sure this is happening because I'm chaining a .then() and .catch() after executing it.
I tried to do this but it didn't work because the err parameter that I'm sending to the callback function is null.:
exports.deleteProduct = (req, res) => {
const id = req.params.productId;
Product.deleteOne({ _id: id, userId: req.user._id }, (err) => {
if (err) {
return res.status(401).json("Not authorized");
}
return res.status(200).json("Product deleted");
});
};
When i tried this second approach I always got the 200 status, meanwhile the product didn't delete.
Any idea how to deal with this?
You can try something like this:
Product.deleteOne({ _id: id, userId: req.user._id }, (err, result) => {
if(err) {
return "something"
}
return "something else"
});
or: in async / await way
try {
await Product.deleteOne({ _id: id, userId: req.user._id });
} catch (err) {
// handle error here
}
By the way, why you are passing userId at the deleteOne method?

Promise { <pending> } Error while working with Geoserver Rest API

Anyone has any idea about this and how would I be able to get the correct outcome?
I have used the Promise and async/await properties in here
layers = async () => {
return new Promise((resolve, reject) => {
let options = {
url: `http://geoserverIP/geoserver/rest/workspaces/datastores/featuretypes.json`,
auth: {
'user': 'admin',
'pass': 'geoserver'
}
}
request(options, (err, resp, body) => {
if (!err && resp.statusCode == 200) {
return resolve(JSON.parse(body))
} else if (!err) {
return reject({
status: 404,
errors: [{
messages: ["Workspace or datastore not found"]
}]
})
} else {
return reject({
status: 500,
errors: [{
messages: ["Failed connection with geoserver"]
}]
})
}
})
})
}
console.log(layers())
I expect to get the list of layers but I get "Promise {pending}"
When you mark a function as async by default returns a Promise also you are returning Promise explicitly.
To use the value either you can await the function or can make a .then chain.
(async ()=>{
const data = await layers();
console.log(data);
})();
or
layers().then((data)=>{
console.log(data)
});
Note: As you are not using any await keyword inside the function you don't need to mark it as async
EDIT1: You can use axios instead of request, it by default returns promise.

How to return json from callback function within the Lambda?

I'm trying to return the login status from the Cognito callback function, which is written in the NodeJS Lambda. However when I call the API the response keep loading and I'm getting warning error.
Here is my code:
'use strict';
global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
module.exports.hello = async (event, context) => {
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello there"
}),
};
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
module.exports.register = async (event, context, callback) => {
let poolData = {
UserPoolId : 'xxxxx', // Your user pool id here
ClientId : 'xxxxxxx' // Your client id here
} // the user Pool Data
let userPool = new AmazonCognitoIdentity.CognitoUserPool(poolData);
let attributeList = [];
let dataEmail = {
Name : 'email',
Value : 'test#gmail.com'
};
let dataName = {
Name : 'name',
Value : 'Jack'
};
var dataPhoneNumber = {
Name : 'phone_number',
Value : '+94234324324234' // your phone number here with +country code and no delimiters in front
};
let attributeEmail = new AmazonCognitoIdentity.CognitoUserAttribute(dataEmail);
let attributeName = new AmazonCognitoIdentity.CognitoUserAttribute(dataName);
var attributePhoneNumber = new AmazonCognitoIdentity.CognitoUserAttribute(dataPhoneNumber);
attributeList.push(attributeEmail);
attributeList.push(attributeName);
attributeList.push(attributePhoneNumber);
userPool.signUp('test#gmail.com', 'H1%23$4jsk', attributeList, null, function(err, result){
let data = {};
if (err) {
callback(null, {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
} else {
let cognitoUser = result.user;
callback(null, {
statusCode: 200,
body: JSON.stringify({
status: 'SUCCESS',
message: '',
data: {
username: cognitoUser.getUsername(),
id: result.userSub
}
}),
});
}
})
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
The warning error as follows:
Serverless: Warning: handler 'register' returned a promise and also uses a callback!
This is problematic and might cause issues in your lambda.
Serverless: Warning: context.done called twice within handler 'register'!
serverless.yml
service: test-auth
plugins:
- serverless-offline
provider:
name: aws
runtime: nodejs8.10
stage: dev
region: us-east-1
functions:
hello:
handler: handler.hello
events:
- http:
path: message
method: get
register:
handler: handler.register
events:
- http:
path: register
method: post
Any help would be appreciated, Thanks in advance.
EDIT (2019-04-01):
module.exports.register = (event, context) => {
...
userPool.signUp('test#gmail.com', 'H1%23$4jsk', attributeList, null, function(err, result){
// for testing purpose directly returning
return {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
})
}
})
};
Its exactly what the error message states.
All async functions return promises.
module.exports.register = async (event, context, callback) => {}
You are also using the callback by calling
callback(null, {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
Instead of using the callback, just return the either an error or a valid response.
Well the error is accurate. async wraps your return with promise. Either use callback all the way through like:
global.fetch = require('node-fetch');
const AmazonCognitoIdentity = require('amazon-cognito-identity-js');
// remove async
module.exports.register = (event, context, callback) => {
...
// if you're using callback, don't use return (setup your callback to be able to handle this value as required) instead do:
// calback({ message: 'Go Serverless v1.0! Your function executed successfully!', event })
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Or don't use callback, use async/await (Promise) all the way through like:
module.exports.register = async (event, context) => {
...
// needs promise wrapper, when using with promise, you might want to break up your code to be more modular
const mySignUp = (email, password, attributes, someparam) => {
return new Promise((resolve, reject) => {
userPool.signUp(email, password, attributes, someparam, function(err, result) {
let data = {};
if (err) {
reject({
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
}),
});
} else {
let cognitoUser = result.user;
resolve({
statusCode: 200,
body: JSON.stringify({
status: 'SUCCESS',
message: '',
data: {
username: cognitoUser.getUsername(),
id: result.userSub
}
}),
});
}
})
});
}
// call the wrapper and return
return await mySignUp('test#gmail.com', 'H1%23$4jsk', attributeList, null);
// don't use double return
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
};
Now register will return a promise. Elsewhere in your code you can call register like:
var result = register();
result
.then(data => console.log(data))
// catches the reject from Promise
.catch(err => console.error(err))
or in async/await function (Note: `await` is valid only inside `async` function)
async function someFunc() {
try {
var result = await register();
// do something with result
console.log(result);
} catch (err) {
// reject from Promise
console.error(err)
}
}
Also note use strict is not required here as node modules use strict by default.
You are using an async function with a call back.
Try it this way:
Remove the callback from the async function.
async (event, context)
And modify the return as:
if (err) {
return {
statusCode: 500,
body: JSON.stringify({
status: 'FAIL',
message: err.message
})
}
}
And put an await on the function call.
If it helps anyone else catching this, you can add headers to the return:
return {
statusCode: 200,
headers: {"Content-Type": "application/json"},
body: JSON.stringify(response.data)
};

Node JS Callback function return nothing

I'm a newbie in node js Development. I just learn node js in short time ago. Here I create a router file
import express from 'express';
import storyController from '../../controllers/story';
const router = express.Router();
router.post('/', (req, res) => {
const { author, title } = req.body;
console.log(author);
const story = {
author: req.body.author,
title: req.body.title,
content: req.body.content,
tags: req.body.tags
};
storyController.createStory(story, function(error, result){
console.log("halo");
if(error)
res.status(500).send({ success: false, message: error.message});
res.status(200).send({ success: true, message: "Success"});
});
});
Then, i create one more file referred as the controller here
import mongoose from 'mongoose';
const Story = mongoose.model('Story');
exports.createStory = async (story) => {
const { author, title } = story;
if(!author){
console.log("hahaAuthor");
return {
error: true,
message: 'You must write an author name!'
};
}
if(!title) {
console.log("haha");
return {
error: true,
message: 'You must write a title!'
}
}
const newStory = new Story({
author: author,
title: title,
content: story.content,
tags: story.tags,
slug: ''
});
newStory.save().then((story) => {
return { error: false, result: story};
}).catch((error) => {
return { error: error};
})
};
But, unfortunately I don't know why my function in router file doesn't call the callback function. The console.log doesn't even called yet. Please help. Otherwise, maybe you have a better way to do this. Thanks!
As createStory is an async function. Change your code like this. You are mixing async with Promise and callback
exports.createStory = async (story) => {
...
// Change the promise to await
let story = await newStory.save();
return { error: false, result: story};
};
Error should be handled in the controller with Promise catch clause.
Something like
router.post('/', (req, res) => {
storyController.createStory.then(data => {
return res.json({error: false, data: data});
}).catch(e => {
return res.json({error: true});
})
});
Note: Either use callback or async. async is the best option now adays
May be this can work:
// 1. callback style
newStory.save().then((story) => {
return cb(null, story);
}).catch((error) => {
return cb(error);
})
// 2. await
await newStory.save();
// controller
router.post('/', (req, res) => {
storyController.createStory.then(data => {
return res.json(...);
}).catch(e => {
return res.json(...);
});
If you use callback style, Error-First Callback is better.

Categories

Resources