i am working on api with express js, in my api call i called 2 functions, first is login() and other function is get_organizations(), but get_organization() called without complete of login() function, i have used async await but it is not working properly, can anyone please help me to resolve this issue ? I have added my whole code here ,
router.get('/', async (req, res) => {
let token = await login();
await get_organizations(token);
});
const get_organizations = async function (api_token) {
try {
console.log('Get List Of Organizations');
console.log('Verifying token');
let verify_token_data = await jwt.verify(api_token, config['token-secret']);
console.log(verify_token_data);
return false;
} catch (error) {
console.log(error);
}
}
const login = async function () {
try {
console.log('Login process started');
const newToken = await jwt.sign({login:'login'},config['token-secret'],{ expiresIn: config['token-expires']});
let username = 'root_admin'
let password = 'Admin123';
let token = String(cryptojs.lib.WordArray.random(20));
console.log("token : "+token);
await connection.query('SELECT * FROM users where username = ? ',[username], async function(err, rows) {
if (err) {
console.log("Looged out failed");
} else {
const user = rows[0];
console.log("psdsdsd");
if(bcrypt.compareSync(password,user.passwordHash)) {
await connection.query('SELECT * FROM organizations where id = ? ',[user.organizationId], async function(err, org_data) {
if (err) {
console.log("Looged out failed");
} else {
console.log("sdsd");
//console.log(org_data);
if(typeof org_data.name!='undefined') {
organization = org_data.name;
} else {
organization = 'VeriCurious';
}
//console.log(organization);
// create a token
const token = await jwt.sign({ id: user.id, username: user.username, organizationId: user.organizationId, organization: organization}, config['token-secret'], {
expiresIn: config['token-expires'] // expires in 30 minutes
});
console.log("Successfull loggedin");
console.log("New generated token : "+token);
return token;
}
});
}
}
});
} catch (error) {
console.log(error);
}
}
When you use await when invoking a function, you're basically waiting for a promise inside that function to resolve. The invoked function is intended to return that promise.
In your code, the login function does invoke connection.query, but there isn't any promise that waits for the query to resolve.
In order to get await to really wait for connection.query, you need to return a Promise which resolves whenever the user finally logs in - i.e. you have the token:
const login = async function () {
try {
console.log('Login process started');
const newToken = await jwt.sign({login:'login'},config['token-secret'],{ expiresIn: config['token-expires']});
let username = 'root_admin'
let password = 'Admin123';
let token = String(cryptojs.lib.WordArray.random(20));
console.log("token : "+token);
return new Promise(function(resolve, reject){
connection.query('SELECT * FROM users where username = ? ',[username], async function(err, rows) { // await won't do anything here,
// you should only use await with functions that return promises.
if (err) {
console.log("Looged out failed");
throw new Error(err);
} else {
const user = rows[0];
console.log("psdsdsd");
if(bcrypt.compareSync(password,user.passwordHash)) {
await connection.query('SELECT * FROM organizations where id = ? ',[user.organizationId], async function(err, org_data) {
if (err) {
console.log("Looged out failed");
throw new Error(err);
} else {
console.log("sdsd");
//console.log(org_data);
if(typeof org_data.name!='undefined') {
organization = org_data.name;
} else {
organization = 'VeriCurious';
}
//console.log(organization);
// create a token
const token = await jwt.sign({ id: user.id, username: user.username, organizationId: user.organizationId, organization: organization}, config['token-secret'], {
expiresIn: config['token-expires'] // expires in 30 minutes
});
console.log("Successfull loggedin");
console.log("New generated token : "+token);
resolve(token); // this signals the Promise to resolve.
// return token;
}
});
}
}
});
});
} catch (error) {
console.log(error);
}
}
Some things to note:
Await is intended to work with promises. Asynchronous functions in javascript, either accept a callback or return a promise. connection.query takes a callback, so await is useless there.
Chances are that the module your using to talk to your database -mongoose?- has a promisified API. Check it out, because if you're willing to use async/await it is better to work directly with Promises rather than wrapping code in a new Promise.
For instance, if connection.query returned a Promise, you could do something like:
const login = async function () {
// skipped a lot of code and logic since this is an example
const rows = await connection.query('SELECT * FROM users where username = ? ',[username]);
const user = rows[0];
const org_data = await connection.query('SELECT * FROM organizations where id = ? ',[user.organizationId]);
const organization = org_data.name;
return await jwt.sign({ id: user.id, username: user.username, organizationId: user.organizationId, organization: organization}, config['token-secret'], {
expiresIn: config['token-expires'] // expires in 30 minutes
});
}
And handling errors is really simple.
When you get an error un a callback inside an async function, and you're returning a new Promise -like in my example- I don't really know if is better to reject or to throw the error. I think both will do the same but I'm not sure.
Related
I am trying to get some data using an API with an accessToken which is encrypted in the database, I am fetching it then decrypting so I can use it to make the API call as follows:
async function getUserGuilds(discord_id) {
//Get Token
const tokenQuery = 'SELECT e_accesstoken FROM users WHERE discord_id = $1';
const discordID = [discord_id];
db.query(tokenQuery, discordID, (err, res) => {
if (err) {
console.log(err);
} else if (!err) {
const encryptedAccessToken = res.rows[0].e_accesstoken
const decrypted = decrypt(encryptedAccessToken);
const accessToken = decrypted.toString(CryptoJS.enc.Utf8);
}
})
//Call the API after getting the token
const response = await fetch(`${DISCORD_API}/users/#me/guilds`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`
}
});
return response.json();
}
as you can see the accessToken is outside the scope, how can I access it and use it for the API call? What is the best practice?
// Since you are using async keywork, this function automatically returns
// a Promise, therefore you will need to handle it with a .then() or await
// in order to get the result.
async function getUserGuilds(discord_id) {
return new Promise((resolve, reject) => {
//Get Token
const tokenQuery = 'SELECT e_accesstoken FROM users WHERE discord_id = $1';
const discordID = [discord_id];
db.query(tokenQuery, discordID, async (err, res) => {
if (err) {
console.log(err);
// Rejecting the promise error here will stop the execution, therefore you will not need to
// add an else statement
return reject(error);
}
const encryptedAccessToken = res.rows[0].e_accesstoken
const decrypted = decrypt(encryptedAccessToken);
const accessToken = decrypted.toString(CryptoJS.enc.Utf8);
// Always wrap async operations with try/catch because they will fail
// at some point, therefore your code must be prepared to handle it
try {
//Call the API after getting the token
const response = await fetch(`${DISCORD_API}/users/#me/guilds`, {
method: 'GET',
headers: { Authorization: `Bearer ${accessToken}` }
});
resolve(response.json());
} catch (error) {
reject(error);
}
});
});
}
The tip is, as soon you start to dealing with asynchronous code, try to write you own functions in an asynchronous manner.
function getUserByStudentId(NIM) {
db.query('SELECT * FROM data_admin WHERE id_mahasiswa = ?', [NIM], async (err, result) => {
if (!result) {
return null
} else {
var data = await {
id: result[0].id_Admin,
email: result[0].email,
jabatan: result[0].jabatan,
password: result[0].password,
id_mahasiswa: result[0].id_mahasiswa,
id_Acara: result[0].id_Acara,
id_Organisasi: result[0].id_Organisasi
}
console.log(data) // there is a value here
return data
}
})
}
console.log(getUserByStudentId('1301194051')) // undefined returned
I'm a student and start learning nodejs. Would you explain to me, why my function returning undefined
console.log(getUserByStudentId('1301194051')) // undefined
but when I console.log on the function I got returned value
I'll promisify the function for you:
function getUserByStudentId(NIM) {
return new Promise(function(resolve, reject) => {
db.query('SELECT * FROM data_admin WHERE id_mahasiswa = ?', [NIM], (err, result) => {
if (!result) {
resolve(null);
} else {
var data = {
id: result[0].id_Admin,
email: result[0].email,
jabatan: result[0].jabatan,
password: result[0].password,
id_mahasiswa: result[0].id_mahasiswa,
id_Acara: result[0].id_Acara,
id_Organisasi: result[0].id_Organisasi
}
console.log(data) // there is a value here
resolve(data);
}
});
});
}
If you're going to use this function in global scope, use then:
getUserByStudentId('1301194051').then(result => {
console.log(result);
});
If you want to use this function inside an async function, you can await the result:
async function doSomethingWithUser(NIM) {
const user = await getUserByStudentId(NIM);
}
For example, if you're using express:
app.get('/user/:id', async (res, req) => {
const NIM = req.param.id;
const user = await getUserByStudentId(NIM);
res.json({ user });
});
Hi i have written an export module for nodejs and use to other modules in there. Unfortunately the script don't wait for the last await
authentication.prototype.adAuth = async function(loginUser, loginPassword, User) {
var authStatus = new Boolean();
var query = User.where({
username: loginUser
}).populate("provider").populate("roles");
await query.findOne(async function(err, user) {
console.log("first await");
if (err) {
authStatus = false;
}
if (user) {
if (user.provider.authModule === "activedirectory") {
var configAD = {
url: user.provider.optionFields.adURL,
baseDN: user.provider.optionFields.adUserBaseDN,
username: user.provider.optionFields.adUserDN,
password: user.provider.optionFields.adUserPassword
};
var ad = new ActiveDirectory(configAD);
await ad.authenticate(loginUser, loginPassword, function(err, auth) {
console.log("second await");
if (err) authStatus = false;
authentication.prototype.setSession(user);
console.log(auth);
authStatus = true;
});
} else {
authStatus = false;
}
} else {
authStatus = false;
}
});
return authStatus;
};
first await
POST /login 500 314.923 ms - 2497
second await
true
I don't know why the ad.authenticate doesn't wait of the result.
Presumably, because the function you are calling does not return a Promise, it uses the older style - callbacks.
You need to turn the callback function into a Promise. Try something like this:
await new Promise((resolve, reject) => {
ad.authenticate(loginUser, loginPassword, function(err, auth) {
console.log("second await");
if (err) authStatus = false;
authentication.prototype.setSession(user);
console.log(auth);
authStatus = true;
resolve();
});
})
As a general note, there's a lot of places where authStatus = false;, this suggests that you should write this piece in a more functional style. Mixing Promises and static values has a tendency to cause bugs. But that snippet above should fix your problem.
I'm trying to develop an API post, in middle execution I have validation such as check name already in use or not. I set error handler callback, it successfully send response 'Already registered', but when I checked to CLI, it show error
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I dont know whats wrong, I use this error handler in the past and it seems look ok.
Here is my code in router:
createUserAccount: async function (req, res) {
const programData = req.body;
try {
await service.create(programData, function (code, err, result) {
if (err) {
if(code === 409){
res.status(HTTPSTATUS.CONFLICT).send(err.message);
} else {
res.status(HTTPSTATUS.BAD_REQUEST).send(err.message);
}
} else {
res.status(HTTPSTATUS.CREATED).json(result);
}
})
} catch (e) {
console.log(e)
res.status(HTTPSTATUS.BAD_REQUEST).json("Failed.");
}
Here is my function in my service:
const config = require('#configs/config.json')
const sequelize = require('sequelize');
const SEQUELIZE = new sequelize(config[env]);
module.exports = {
createAccount: async (name, password, callback) => {
try {
let check,
institution_id;
const checkName = await Profile.count(
{
where: {
name: name
}
}
);
//result checkName = 1
if(checkName > 0){
//then successfully execute this condition and
return callback(409, 'Already registered.', null);
//this show in console ----> POST /API/v1/user/profile 409 616.152 ms - 31
}
await Login.create({
username: email,
password: password
}).then(resLogin => {
const response = {
id: resLogin.id,
}
callback(201, null, response);
}).catch( error => {
callback(400, error, null);
})
} catch (e) {
callback(400, e, null);
}
},
create: async (payload, callback) => {
let loginID = null;
let {
profile,
address
} = payload;
let {
name,
email,
password
} = profile;
try {
await module.exports.createAccount(name, password, function (code, error, result) {
if(error){
const res = {message: error};
//what I need is the execution is end in here
return callback(code, res, null);
}
loginID = result.id;
});
//but the fact is it still execute this whole function if got callback error from createAccount()
let transaction = await SEQUELIZE.transaction();
await Address.create(address, {transaction})
.then( async resAddress => {
await transaction.commit();
return callback(201, null, resProfile);
}).catch(async e => {
return callback(400, e, null);
})
} catch (e) {
console.log(e);
callback(e, null);
}
};
I'm making a api to register users and i like to return in the json response the user and the jwt token.
Actually this is my function:
initializeCreate( {request} ){
const data = request.only(["username", "password", "permission", "status"])
return new Promise(function(resolve, reject) {
user.create(data, function(err, resp, body) {
if (err) {
reject(err);
} else {
resolve(JSON.parse(body))
}
})
})
}
createUser({ auth }){
var initializePromise = initializeCreate();
initializePromise.then(function(result) {
const token = await auth.attempt(result.username, result.password)
return token
}, function(err) {
console.log(err);
})}
I suppose that i have to wait the event User.create() finish to make the auth.attempt, so i create this promise, but this is the better way to do this? There's a way that i make this in only 1 function?
Actually i'm receiving this error:
Unexpected token const token = await auth.attempt(result.username,
result.password)
You can use .generate(user) - documentation
const user = await User.find(1)
const token = await auth.generate(user)
or .attempt(uid, password) - documentation
const token = await auth.attempt(uid, password)
I suppose that i have to wait the event User.create() finish to make
the auth.attempt, so i create this promise, but this is the better way
to do this?
Yes. You need to use await. like :
await user.create(...)
(And now you can put everything in a function)
You can use try/catch :
async login({ auth, response, request }) {
const data = request.only(['email', 'password'])
try {
await auth.check()
return response.status(200).send({ message: 'You are already logged!' })
} catch (error) {
try {
return await auth.attempt(data.email, data.password)
} catch (error) {
return response.status(401).send({ message: error.message })
}
}
}
Sample code for a personal project