I am using PostgreSQL for the first time with an express server and am running into an error. On my register user route I am trying to check if the username or email already exists, because they need to be unique. What keeps happening is, say I pass in a username that is already in the database then the first return will run and return that the username is already in use. But what is happening is it is returning the username is already in use and it still running the rest of the code so it trying to return multiple json responses.
module.exports.register = async (req, res, next) => {
try {
const { username, email, password } = req.body;
postgres
.query("SELECT * FROM users WHERE username = $1", [username])
.then((data) => {
if (data.rows.length > 0) {
return res.status(409).json({
msg: "Username is already in use",
status: false,
});
}
})
.catch((err) => {
console.log(err);
});
postgres
.query("SELECT * FROM users WHERE email = $1", [email])
.then((data) => {
if (data.rows.length > 0) {
return res.status(409).json({
msg: "Email is already in use",
status: false,
});
}
})
.catch((err) => {
console.log(err);
});
const hashedPassword = await bcrypt.hash(password, 10);
postgres.query(
"INSERT INTO users (username, email, password) VALUES ($1,$2,$3) RETURNING *",
[username, email, hashedPassword],
(err, data) => {
if (err) {
console.log(err.stack);
} else {
return res.json({ user: data.rows, status: true });
}
}
);
} catch (err) {
next(err);
}
};
I can't figure out why the rest of the code is running even though I am returning res.json. If anybody has any suggestions/solutions I would really appreciate it!
The return in front of the res.status(409) is returning you out of the then of the postgres.query function instead of the full register function. As a result it jumps out of the then and runs the rest of the code from there, so it's still hashing the password and attempting an insert into the users table (which hopefully fails on a unique index).
In order to fix this you can either 1) Define a variable before the function, change it if something was found and then do a return outside of the then statement if the variable was changed 2) perform all the rest of the code in the then statement (since you're returning out of that it will not be run) or 3) use awaits instead and throw/next+return/res.json+return an the HTTP 409 error.
Option 3 will take the most effort but you should definitely learn to use this route as soon as possible as it makes writing async code a lot easier (plus you'll avoid getting a bunch of nasty nested then statement). You could try out using option 1 and 2 just to get a feel for how the flow of the express code works.
Related
I'm now trying to build the user authentication logic of a website. Now I should check, when a user tries to log in, does he have a registered email/password already. But I cannot just simply get the field's value from the DB, as a string.
In the database, I have 6 fields (isSuperuser, fullName, institution, password, email, approved). For the authentication, the unfinished method is here:
router.post("/login", async (req, res, next)=> {
const email = req.body.email;
const data = await client.query(`SELECT * FROM user_table WHERE email= $1;`, [email])
const arr = data.rows
//const arr = data.rows;
.then(user=> {
if(arr==null || arr== undefined || arr.length==0){
return res.status(401).json({
message: "Login authentication failed!"
})
}
})
})
My problem is, that, when I check, if the email, provided at login, already exists, I should simply compare it (req.body.email) with the value(s) of the email field in the database. But I did not find any solution, how to just get a string, as a result for the query. The const arr gives an array, from which I cannot get the value of the email key. So far, this code might works (though ugly as hell), but when it comes to password compare, I will bleed.
If I try
client.query("SELECT password FROM user_table WHERE email= $1;", [email])
it only gives back an empty array.
What do I do wrong? Any solution, please?
Iam not sure about PostgreSql but I know sql so give it a try;
router.post("/login", async (req, res, next) => {
const { email } = req.body;
const data = await client.query(`SELECT * FROM user_table WHERE email= ${email}`).then((result) => {
return result
})
if (!data) { // OR if data is an array then use "data.length == 0"
return res.status(401).json({
success: false,
message: "Login authentication failed!"
})
}
})
I am creating an application in which users can create posts and comment on these. Creating, updating and deleting posts works as intended, and so does creating comments.
When the user creates a comment, its accountId is passed to the database.
When deleting a specific comment, the accountId is passed to verify that the user is allowed to delete it.
The problem is, it seems like the accountId isn't fetched from the database, though the query asks for all details from the database table called "comments".
The app is divided into two files, db.js, and app.js.
I have tried modifying the request. In order to troubleshoot, I added a line of code checking if the comment.accountId was fetched, but that is where I get the error.
/* in db.js: */
//get comment by comment id
exports.getCommentById = (id, callback) => {
const query = 'SELECT * FROM comments WHERE id = ?'
const values = [ id ]
db.all(query, values, (error, comment) => {
if (error) {
console.log(error)
callback(['databaseError'])
return
} else if (!comment) {
console.log(error)
callback(['notFound'])
return
} else {
callback([], comment)
}
})
}
/* in app.js */
app.delete('/comments/:commentId', (req, res, next) => {
const commentId = req.params.commentId
db.getCommentById(commentId, (errors, comment) => {
if (errors.length > 0) {
res.status(500).json({
message: 'serverError'
}).end()
return
} else if (!comment) {
res.status(404).json({
message: 'notFound'
}).end()
return
}
const accountId = req.accountId //from my auth middleware
const commAccId = comment.accountId
if(!commAccId) {
console.log(accountId)
console.log(commAccId)
res.status(404).json({
message: 'AccIdNotFound'
}).end()
return
}
- - - - - ^ this is the error checking I inserted, and this is where the error is thrown, so it seems like the id is just not found.
if(!accountId) {
res.status(401).json({
message: 'notAuthenticated'
}).end()
return
} else if (comment.accountId != accountId) {
res.status(401).json({
message: 'notAuthorized'
}).end()
return
}
//plus code for deletion (will insert if it seems relevant, just ask)
})
})
The error message is "AccIdNotFound"
console.log returns 5 (same as the logged in user) and undefined
db.all delivers an array of rows, not just one row. You are assuming the result is a single comment only.
You should check result.length, then pull out result[0].
I'm writing a server using NodeJS, Express and SQLite in order to provice RESTful services.
I want to create an endpoint for adding a new user to the database. The endpoint receives a POST request with the parameters username and password, and responds with the id of the newly created user.
According to the node-sqlite3 package documentation, when the run method succeeds, the this object inside the callback function contains the properties lastID and changes.
My problem is that, even though the run method succeeds and the new user is successfully inserted into the database, the this object is empty.
What am I doing wrong? How can I access this.lastID?
app.post('/users', (req, res) => {
const {
username,
password
} = req.body;
db.get(
`SELECT id
FROM Users
WHERE username = ?`,
[username],
(err, row) => {
if (err) {
console.error(err);
return res.sendStatus(500);
} else if (row) {
return res.status(400).send('Username already exist.');
}
db.run(
`INSERT INTO Users (username, password)
VALUES (?, ?)`,
[username, password],
(err) => {
if (err) {
console.error(err);
return res.sendStatus(500);
}
console.log(this); // prints {} to the console
res.send(this.lastID); // response body is empty
}
);
}
);
});
Try using an old-school function instead of an arrow function for your callback. I think with arrow functions this cannot be bound to a value.
I have a website that offers a simple messaging service. Individuals can pay for the service, or a business can pay for a monthly subscription and then add their clients/users for free. When the business adds a client/user email, that triggers the function below. I'm using firebase functions and createUser to create the user on my server(less). However, sometimes a business tries to register a user and that user already exist. In this case, I want to send the user a reminder email.
The code I have works fine, but it feels funky having a chain within my catch/error. Is there another way to detect if an email is already registered with a Firebase account that won't throw an error?
exports.newUserRegisteredByBusiness = functions.database.ref('users/{uid}/users/invited/{shortEmail}').onWrite( (data, context) => {
//don't run function if data is null
if (!data.after.val()){
console.log('SKIP: newUserRegisteredByBusiness null so skipping')
return null
} else {
let businessUID = context.params.uid
let email = data.after.val()
let shortEmail = context.params.shortEmail
let password // = something I randomly generate
return admin.auth().createUser({ email: email, password: password}).then( (user)=> {
//write new user data
let updates = {}
let userData // = stuff I need for service to run
updates['users/' + user.uid ] = userData;
return admin.database().ref().update(updates)
}).then( () =>{
//email new user about their new account
return emailFunctions.newUserRegisteredByBusiness(email, password)
}).catch( (error) =>{
//if user already exist we will get error here.
if (error.code === 'auth/email-already-exists'){
//email and remind user about account
return emailFunctions.remindUsersAccountWasCreated(email).then( ()=> {
//Once email sends, delete the rtbd invite value that triggered this whole function
//THIS IS WHERE MY CODE FEELS FUNKY! Is it ok to have this chain?
return admin.database().ref('users/' + businessUID + '/users/invited/' + shortEmail).set(null)
})
} else {
//delete the rtbd value that triggered this whole function
return admin.database().ref('users/' + businessUID + '/users/invited/' + shortEmail).set(null)
}
});
}
})
To find if a user account was already created for a given email address, you call admin.auth().getUserByEmail.
admin.auth().getUserByEmail(email).then(user => {
// User already exists
}).catch(err => {
if (err.code === 'auth/user-not-found') {
// User doesn't exist yet, create it...
}
})
While you're still using a catch() it feels like a much less failed operation.
To avoid further implementation in the catch block you can wrap this Firebase function into this code:
async function checkUserInFirebase(email) {
return new Promise((resolve) => {
admin.auth().getUserByEmail(email)
.then((user) => {
resolve({ isError: false, doesExist: true, user });
})
.catch((err) => {
resolve({ isError: true, err });
});
});
}
...
const rFirebase = await checkUserInFirebase('abc#gmail.com');
I am trying to determine if a username exists before creating the user. I am using the following code. I need an elegant way to determine if the zero row is returned ...username doesn't exist. for example I know the returned row value would be zero if not found. How can I get access to the row value in the code. Can someone assist...thanks...BTW I am using neo4j3.0 Nodejs with express and Passport
neo4jSession
.run(MATCH (user {email: newUser.email}) RETURN user);
.then (function(result) {
if ((not found) {
.run(CREATE (user: {email:newUser.email, password:newUser.password} ) ASSERT email is UNIQUE RETURN user);
neo4jSession.close();
}) //end of if not found
else (found)
{
// email address already exist
console.log("email address already exist");
neo4jSession.close();
}
}); //end .then
.catch(function(error) {
console.log(error);
});
The Neo4j Driver for JavaScript record module can check if a value from record exists by index or field key using the has method. When evaluating or validating the existence of any field within a record (e.g. determining if a User node contains an existing email address property), using the has instead of the get method can allow for shorter Cypher statements and condensed javascript code; which (IMO) can lead to elegance you are seeking.
Using your original example, you can use a simple Cypher statement to search if a User node contains an email property by passing in a user's input. Utilizing the Neo4j Driver for JavaScript, you can return a result stream with a single record.
Cypher Statement:
MATCH ( u:User { email: $email } )
RETURN u, u.email
If an email address exists as a User node property in the Neo4j database, a stream of records with one field named "u.email" be will returned . The record represents one user found by the statement above. You can access the field value by key using the record module's has method.
Access Record by Field Key:
result.records[0].has('u.email')
The following example is one of many ways you could implement both the simple Cypher statement and has method:
async (_, { email, password }) => {
const session = await driver.session()
const closeSession = await session.close()
const endSession = await driver.close()
let query = 'MATCH (u:User{email: $email}) RETURN u, u.email'
return session
.run(query, { email })
.then(async result => {
closeSession()
let emailExists = result.records[0].has('u.email')
let newUser = result.records[0].get('u').properties
if (
(Array.isArray(result.records) && !result.records.length) ||
(Object.keys(result).length === 0 && result.constructor === Object)
) {
if (!emailExists) {
let query =
'MERGE (u:User { email: $email }) ON CREATE SET u.password = $password RETURN u'
return session
.run(query, { email, password })
.then(result => {
closeSession()
return newUser
})
} else if (Array.isArray(result.records) && result.records.length) {
const emailExists = result.records[0].has('u.email')
if (emailExists) {
closeSession()
throw new Error(emailExists + ' already exists.')
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
})
.catch(function(err) {
closeSession()
endSession()
if (err) throw err
})
}
Note: This example validates whether a record exists first by evaluating the result with conditional statements, then the e-mail property is checked. A few errors have been handled as well.
Query (use counter):
MATCH (user {email: newUser.email})
RETURN count(user)=1 as user_exists
In javascript:
if ( result.records[0].get('user_exists') !== true ) {
// create new user
}
And, of course, add a unique constraint to the email address for the user.
Though the answer from #stdob is an accepted answer on further research I found out that in case the record do exist, you can't retrieve any data on that query... so:
MATCH (user {email: newUser.email})
RETURN user.name AS Name, count(user)=1 as user_exists
wont yield any data if user exist. The following works:
MATCH (user {email: newUser.email}) RETURN user;
if (!result[0]) {
//no records found
}
else {get user properties}
Thanks to:
https://github.com/mfong/node-neo4j-passport-template/blob/master/models/user.js