Sending empty {} after form "post" with pug in node.js - javascript

I'm trying to pass form data from login page to signin page via post using fetch with this pug code:
form(id="form-login")
input(type="text", name="email", value="", placeholder="Tu email")
br
input(type="password", name="password", value="", placeholder="Tu contraseña")
br
input(type="submit" value="Conectar")
script.
const formLogin = document.querySelector('#form-login');
const formData = new FormData(formLogin);
formLogin.addEventListener('submit', function(event) {
console.log('Form Data: ', formData);
event.preventDefault();
fetch('/signin', {
method: 'POST',
body: formData
})
.then(function(res) {
res.json();
})
.then(function(data) {
console.log(data)
localStorage.setItem('token', data.token)
})
});
The problem is an empty req.body reaching to signin.. After trace it gives this console.log
Form Data: FormData {}
and also an undefined req.body.
If I comment this script and just send it through form adding action="/signin" and method="post", it works and the answer is printed, but calling storage.setItem({ token: <token> }) returns an Uncaught (in promise) TypeError: Cannot read property 'token' of undefined
I'm wondering why this script is not sending the data... can't figure out... so any help will be much apreciated.
Signin function:
function signIn (req, res) {
if (!req.body.email) return res.status(200).send({message: 'No recibo el usuario'})
User.findOne({ email: req.body.email }, (err, user) => {
if(err) return res.status(500).send({ message: err })
if(!user) return res.status(404).render('login', { title: 'Intenta loguearte de nuevo' })
user.comparePassword(req.body.password, (error, isMatch) => {
if (error) return res.status(500).send({ message: error })
if (!isMatch) {
return res.redirect('login')
} else {
req.user = user
res.status(200).send({
message: 'Te has logueado correctamente',
token: service.createToken(user)
})
//$window.localStorage.setItem({token: service.createToken(user)}); // NO WORKS
return res.body = service.createToken(user) // TRYING THIS WITHOUT KNOWLEDGE ABOUT WHAT AM I DOING :O
}
})
})
}
Thanks in advance.
****EDIT****
As #MichałSałaciński suggest, commenting first .then res.json().... At least gives a response, but still don't undestand what's hapenning here and in order to learn properly and make things better, also hope someone can explain how to correctly do stuff like this.
Response: body : ReadableStream
locked : false
__proto__ : Object
bodyUsed : false
headers : Headers
__proto__ : Headers
ok : true
redirected : false
status : 200
statusText: "OK"
type : "basic"

So I was having the same issue where the POST request from my pug form was sending back an empty {} as the req.body object. The code was a simple create action using these:
bookController.js
exports.createBookForm = (req,res) => {
res.render("create_book_form", { title: "Add A New Book"})
}
exports.createBook = (req,res) => {
const reqFields = ["title", "author"];
for (let i = 0; i < reqFields.length; i++) {
const field = reqFields[i];
if (!field in req.body) {
const message = `Missing ${field} in the request body`;
console.log(message)
return res.status(400).send(message)
}
}
Book
.create({
title: req.body.title,
author: req.body.author,
summary: req.body.summary
})
.then((book) => {
res.status(201).json(book.serialize())
})
.catch(err => {
console.log(err);
})
}
And the create book form:
block content
h1 Add a Book
h3 Do use real details. Otherwise, what's the point?
form(method="POST" action="/books")
div.form-group
label(for="title") Title:
input#title.form-control(type="text", placeholder="Small Gods" name="title")
label(for="author") Author:
input#author.form-control(type="text", placeholder="Terry Pratchett" name="author")
label(for="summary") Summary:
textarea#summary.form-control(type="text", placeholder="God is turtle, world is flat" name="summary")
div.form-group
button.btn.btn-primary(type="submit" role="submit") Add Book
What finally fixed getting the actual req.body to show up for the POST action was adding (within server.js)
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
Let me know if this works for you. Took me a couple hours to come to this conclusion and I hate seeing questions go unanswered.

You should move "new FormData" inside "send" event listener. Also, there's missing comma after type="submit", but overall, the problem got nothing to do with pug :)
form(id="form-login")
input(type="text", name="email", value="", placeholder="Tu email")
br
input(type="password", name="password", value="", placeholder="Tu contraseña")
br
input(type="submit",value="Conectar")
script.
const formLogin = document.querySelector('#form-login');
formLogin.addEventListener('submit', function(event) {
const formData = new FormData(formLogin);
console.log('Form Data: ', formData);
event.preventDefault();
fetch('/signin', {
method: 'POST',
body: formData
})
.then(function(res) {
res.json();
})
.then(function(data) {
console.log(data)
localStorage.setItem('token', data.token)
})
});

Related

Separating Mongoose code from Express Router

So basically, I'm trying to separate my code that handles data (mongoose) from my express Router code, since I might want to use it elsewhere too.
The first thing I did was, I got rid of the res.json() calls, since I don't want the code to only work returning a http response. I want it to return data, so I can then return that data from my router as a http response, but still use it as regular data elsewhere.
Here is a function I wrote to get data from mongoose.
module.exports.user_login = data => {
console.log(data);
ModelUser.findOne({email: data.email}).then(user => {
if(!user){
console.log({email: 'E-mail address not found'});
return {
status: response_code.HTTP_404,
response: {email: 'E-mail address not found'}
}
}
bcrypt.compare(data.password, user.password).then(isMatch => {
if(!isMatch){
console.log({password: 'Invalid password'});
return {
status: response_code.HTTP_400,
response: {password: 'Invalid password'}
}
}
const payload = {
id: user.id,
email: user.email
};
jwt.sign(
payload,
config.PASSPORT_SECRET,
{
expiresIn: "1h"
},
(err, token) => {
console.log({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
return {
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
}
}
);
});
});
};
When this code gets executed in my route like so:
router.post("/login", (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
console.log("ret", dm_user.user_login(req.body));
});
The log says the return value of user_login() is undefined, even though right before the return statement in user_login() I am logging the exact same values and they are getting logged.
Before I changed it to a log, I tried to store the return value in a variable, but obviously that remained undefined as well, and I got the error: 'Cannot read propery 'status' of undefined' when trying to use the value.
I am definitely missing something..
Well you have an small callback hell here. It might be a good idea to go with async / await and splitting up your code into smaller chunks instead of putting everyhing in 1 file.
I rewrote your user_login function:
const { generateToken } = require("./token.js");
module.exports.user_login = async data => {
let user = await ModelUser.findOne({ email: data.email });
if (!user) {
console.log({ email: "E-mail address not found" });
return {
status: response_code.HTTP_404,
response: { email: "E-mail address not found" }
};
}
let isMatch = await bcrypt.compare(data.password, user.password);
if (!isMatch) {
console.log({ password: "Invalid password" });
return {
status: response_code.HTTP_400,
response: { password: "Invalid password" }
};
}
const payload = {
id: user.id,
email: user.email
};
let response = await generateToken(
payload,
config.PASSPORT_SECRET,
response_code
);
return response;
};
I have moved your token signing method into another file and promisfied it:
module.exports.generateToken = (payload, secret, response_code) => {
return new Promise((res, rej) => {
jwt.sign(
payload,
secret,
{
expiresIn: "1h"
},
(err, token) => {
if (err) {
rej(err);
}
res({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
}
);
});
};
Now you need to change your router function into an async:
router.post("/login", async (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
let result = await dm_user.user_login(req.body);
console.log(result);
});
In addition: You get undefined because you return your value to an callback function
I also would seperate your routes from your controllers instead of writing your code inside an anonymous function
Please notice that whenever you are trying to return any value you are always present in the callback function and that is definitely not going to return any value to its intended place.
There are a couple of things you can improve about your code :
1.Donot use jwt inside your code where you are making database calls, instead move it where your routes are defined or make a separate file.
2.If you are intending to re-use the code, I would suggest you either use async-await as shown in the answer above by Ifaruki or you can use something like async.js. But the above shown approach is better.
Also always use 'error' field when you are making db calls like this:
ModelUser.findOne({email: data.email}).then((error,user) => {

How do I add an object to a database in Knex where the object is the child of two parents?

I have the code below. Its a standard blog type of setup with users which have posts and comments. Comments are the child of both users and post . Posts belong just to users. Im having a problem posting to comments table. IM not getting any errors when using the insert function , however, when I post a comment to the database nothing gets saved to the comments table . If i do a request to retrieve the comments table , the table still shows empty. What am i doing wrong here .
server.post("/users/:id/posts/:id2/comments", async (req, res) => {
const userID = req.params.id;
const postID = req.params.id2;
db("users")
.where({ id: Number(userID)})
.then((user) => {
db('posts') .where({ id: Number(postID)})
.then((post) => {
//verify if post and user exists
if (post && user) {
req.body.content ? insertComment({
content: req.body.content,
user: userID,
post: postID
})
.then(
res.status(201).json(req.body)
)
.catch((err) => {
console.log(err);
})
: res.status(400).json({
errorMessage: "Please insert text .",
});
} else {
res.status(404).json({
message: "user not found",
});
}
})
})
.catch((err) => {
res.status(500).json({
err,
message: "Error processing request",
});
});
});
function insertComment(comment) {
return db("comments").insert(comment).where({
user: comment.user,
post: comment.post
});
}
since you're already using async function i'd first recommend to use async/await, second notice is that knex returns an array and not an object for example
db("users")
.where({ id: Number(userID)})
.then((user) => {
// user is an array
});
you can chain a query with .first() to retrieve the first object and not an array
Reference from knex documentation
using async/await could save you from callback hell
server.post("/users/:id/posts/:id2/comments", async (req, res) => {
const userID = req.params.id;
const postID = req.params.id2;
try {
const user = await db("users").where("id", Number(userID)).first();
const post = await db("posts").where("id", Number(postID)).first();
if (post && user) {
if (req.body.content) {
await insertComment({
content: req.body.content,
user: userID,
post: postID,
});
return res.status(201).json(req.body);
} else {
return res.status(400).json({
errorMessage: "Please insert text .",
});
}
} else {
return res.status(404).json({
message: "user or post not found",
});
}
} catch (err) {
return res.status(500).json({
err,
message: "Error processing request",
});
}
});
async function insertComment(comment) {
return db("comments").insert(comment).where({
user: comment.user,
post: comment.post,
});
}
and if you have lots of relationships in your application you might find it useful if you want to use an ORM like Objection as it is built on knex.

How to get message property of an error returned from an express server

I'm returning an error response from express like below.
router.post("/", authCheck, authCheckAdmin, (req, res, next) => {
const burger = new Burger({
_id: mongoose.Types.ObjectId(),
...req.body
});
console.log(burger);
Burger.find({ name: req.body.name })
.exec()
.then(result => {
if (!result) {
burger
.save()
.then(result => {
res.status(201).json({
message: "Burger Successully Created",
burger: result
});
})
.catch(error => {
res.status(500).json(new Error('Something went wrong when saving the burger')); // <------
});
} else {
res.status(500).json(new Error('Burger already exist'));
}
});
});
This is the React code
export const addBurger = burgerData => (dispatch, getState) => {
const currentState = getState();
axios
.post("/burgers", burgerData, {
headers: {
Authorization: `Bearer ${currentState.auth.token}`
}
})
.then(response => {
console.log(response);
})
.catch(error => {
console.error(error.message); <-------- Here
});
};
Below is the console preview
How can I get the message that I have passed inside express? I want to get "Burger already exist" as the error message. Is there another way to do this? Thanks
Server side fix
res.status(500).send({ error: 'Something failed!' })
According to me, error is not defined in the last else of your Burger.find method which is causing this error. This type of error's are mostly server side errors. Hope this work for you.
res.status(500).json(new Error('Burger already exiist')); // <------
if you would look at response in dev tools, you see that in this case payload is empty object. So you need directly tell the server what you need to send back. Try this if you already waiting for message key in react part:
res.status(500).json({message: 'Burger already exist'});

TypeError: res.status is not a function

I'm making a function that permits me to upload a picture to imgur in my express api (nodejs),
i'm encoutering an error when calling a function returning a promise:
TypeError: res.status is not a function
at uploadpicture.then
This is my code:
Where error is raised:
router.post('/upload', (req, res, next)=> {
var busboy = new Busboy({headers: req.headers});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
if(fieldname == 'image') {
// the buffer
file.fileRead = [];
file.on('data', function(data) {
// add to the buffer as data comes in
this.fileRead.push(data);
});
file.on('end', function() {
// create a new stream with our buffered data
var finalBuffer = Buffer.concat(this.fileRead);
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
console.log(res);
res.status(200).json({success: true, message: "Successfully uploaded !", url: res.data.link});
},(err)=>{ //error
res.status(500).json({success: false, message: "Error happenned while uploading !"});
}).catch((error)=>{
console.log(error);
res.status(500).json({success: false, message: "Error happenned while uploading !"});
});
})
}
});
busboy.on('finish', function() {
//busboy finished
});
req.pipe(busboy);
});
And the function :
function uploadpicture(stream){ //get picture stream
return new Promise((resolve, reject)=>{
var options = {
uri: 'https://api.imgur.com/3/image',
method: 'POST',
headers: {
//'Authorization': 'Client-ID ' + config.client_id_imgur // put client id here
},
formData: {
image: stream,
type: 'file'
},
auth: {
bearer: config.access_token_imgur,
}
};
request(options)
.then((parsedBody)=> {
resolve(parsedBody);
})
.catch((err)=> {
console.log(err);
reject(err.toString())
});
});
}
The code works perfectly, but i don't know why suddendly this error happened,
i tried to :
change arrow functions to function(){}
Add next to the route parameters
Nothing worked, Thanks for your help
The accepted answer directly addresses the OP's problem, but I post another solution since you can also encounter this error in other places.
When you have:
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse) => {
response.status(500).end() // response.status is not a function
})
Because the error handling route must accept 4 arguments for express to identify it as an error middleware.
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse, next: NextFunction) => {
response.status(500).end()
})
Just adding the next function (or whatever argument you're missing) will fix it.
https://github.com/visionmedia/supertest/issues/416#issuecomment-514508137
At this point:
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
the resis the result of promise uploadpicture function (that is the parsedBody), not the res from the express route. So indeed, it has no status function. Try change the then callback name like:
upload = uploadpicture(finalBuffer).then((otherName)=>{ //success request
You are getting this error:
TypeError: res.status is not a function
Because the order should be (err, res, req, next) not (req, res, err, next),
example below
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode)
res.json({
message : err.message,
stack :process.env.NODE_ENV === 'production' ? null : err.stack,
})
}
Order of parameters really matters i had error in below code
const getImagesByBrand = async (res) => {
try {
const images = await Image.find();
res.status(200).json(images);
} catch (error) {
res.status(500).json(error);
}
};
I was not giving req as parameter and that was the reason for error i just add req,res and it worked
If you are using the async/await method:
const notifications = await notifications.aggregate({...})
if(notifications){
return res.status(200).json({ data: notifications })
}else{
return res.status(404).json({ message: 'No notifications found'})
}
Make sure that you are including your return statements. Not including a return statement will cause this. Something else that I was doing is I had JSON instead of json, which will most definitely throw an error.

Check if variable is empty not working | NodeJS, Express

I am trying to build a blog API, and right now I have three fields in my schema:
const PostSchema = new Schema({
timestamp: {
type: Date,
default: Date.now
},
title: {
type: String,
required: [true, "Title is required"]
},
content: {
type: String,
required: [true, "Content is required"]
}
})
I also have createPost function, that is supposed to create a post (no shit):
// Create post
const createPost = (req, res, next) => {
const title = req.body.title
const content = req.body.content
console.log('body', req.body) // getting output
if (!title) {
res.status(422).json({ error: "Titel saknas!!!" })
}
if (!content) {
res.status(422).json({ error: "Skriv något för fan!" })
}
const post = new Post({
title,
content
})
post.save((err, post) => {
if (err) {
res.status(500).json({ err })
}
res.status(201).json({ post })
})
}
I have those two if statements to check if the title or the content is empty, but that is not working. I tried to send a POST request with Postman:
But the error says that my title is missing. But I am passing in my title key.
So I wonder why this is not working, it feels like some obvious stuff, but I just can't get this to work.
Thanks for reading.
I don't know Postman too well, but I'm going to guess that setting the body content type to raw uploads the body as text/plain, which means body-parser will not parse it in any way (console.log('body', typeof req.body) will show "body string").
Instead, try setting the content type to application/json (and make sure that your server uses the JSON middleware from body-parser).

Categories