Node/express endpoint - throwing unexpected error - express

I have an express endpoint that is throwing an unexpected error. I can't seem to figure out what is going on.
Here is the endpoint:
const express = require('express');
const bodyParser = require('body-parser');
const jsonParser = bodyParser.json();
const router = express.Router()
const { Article } = require('./model');
const passport = require('passport');
router.post('/highlight', jsonParser, (req, res) => {
const { removeId, addId } = req.body;
Article
.findByIdAndUpdate(removeId, {
featured: false
});
return Article
.findByIdAndUpdate(addId, {
featured: true
})
.then(article => {
res.status(202).json({ article });
})
.catch(err => {
res.status(404).json({ message: 'No record found' });
});
});
Here is the error:
SyntaxError: Unexpected token o in JSON at position 1
at JSON.parse (<anonymous>)
at parse (/Users/johnseabolt/Desktop/code/PERSONAL/dfpr/node_modules/body-parser/lib/types/json.js:89:19)
at /Users/johnseabolt/Desktop/code/PERSONAL/dfpr/node_modules/body-parser/lib/read.js:121:18
at invokeCallback (/Users/johnseabolt/Desktop/code/PERSONAL/dfpr/node_modules/raw-body/index.js:224:16)
at done (/Users/johnseabolt/Desktop/code/PERSONAL/dfpr/node_modules/raw-body/index.js:213:7)
at IncomingMessage.onEnd (/Users/johnseabolt/Desktop/code/PERSONAL/dfpr/node_modules/raw-body/index.js:273:7)
at emitNone (events.js:106:13)
at IncomingMessage.emit (events.js:208:7)
at endReadableNT (_stream_readable.js:1056:12)
at _combinedTickCallback (internal/process/next_tick.js:138:11)
at process._tickCallback (internal/process/next_tick.js:180:9)
I can't figure out what is happening here. It looked like it might be the jsonParser part, but I have another endpoint that has jsonParser and it works fine! Here is the working endpoint:
router.post('/create', jwtAuth, jsonParser, (req, res) => {
const { title, author, content } = req.body;
if (!title) {
return res.status(422).json({ message: 'Title is required' });
}
if (!author) {
return res.status(422).json({ message: 'Author is required' });
}
if (!content) {
return res.status(422).json({ message: 'Content is required' });
}
return Article
.create({ title, author, content })
.then(article => {
res.status(202).json({ article });
})
.catch(err => {
res.status(422).json({
error: err,
message: 'There was a problem with your request'
});
})
});
Can anyone see what's going on here? I've been stabbing at it for hours and I'm stumped.

Related

express crashes if req.file is undefined

when making a post request to an express api without an attached file in the request, the api crashes and provides the TypeError: Cannot read properties of undefined (reading 'filename') error. However i would like to make it so the api does not crash when a post request is made without an attached image. any ideas ?
express code :
const storage = multer.diskStorage({
destination: (req, res, cb) => {
cb(null, dir)
},
filename: (req, file, cb) => {
cb(null, Date.now() + file.originalname)
}
})
const upload = multer({
storage: storage
})
router.get('/', async (req, res) => {
try {
const members = await Member.find();
res.json(members);
} catch (err) {
res.status(500).json({ message: err.message });
}
})
router.get('/:id', getMember, async (req, res) => {
res.json(res.member)
})
router.post('/', upload.single('image'), async (req, res) =>{
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
nextjs code to actually send the file:
const submitHandler = (e) => {
e.preventDefault()
const formDatas = new FormData()
formDatas.append('name', name)
formDatas.append('occupation', occupation)
formDatas.append('bio', paragraph)
formDatas.append('join', date)
formDatas.append('image', img)
console.log(formDatas)
axios
.post(api + '/members', formDatas)
.then(res => console.log(res))
.catch(err => console.log(err))
}
Error occurred because you're trying to access object of undefined variable req.file;
You can make changes according to your need
1 If you don't want to accept request without any file
router.post('/', upload.single('image'), async (req, res) => {
if (!req.file) { //or you can check if(req.file===undefiend)
return res.status(400).json({ message: 'Please attach a file' });
}
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
2 If you want to store null/empty string (in case of no file upload)
router.post('/', upload.single('image'), async (req, res) => {
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file!==undefined ? req.file.filename : null
})
try {
const newMember = await member.save()
res.status(201).json(newMember)
} catch (err) {
res.status(400).json({ message: err.message });
}
})
Change as shown below
const member = new Member({
name: req.body.name,
occupation: req.body.occupation,
bio: req.body.bio,
join: req.body.join,
image: req.file.filename ? req.file.filename : ""
})
This should stop crashing but until I see the whole code wont know how you are handling error

req.body is undefined after installing express-static-gzip

I recently added express-static-gzip to my server and have since noticed that my req.body is undefined in my router.post when submitting a form.
Previously it was working without issue but now I am getting a POST 500 internal server error, a Cannot read property "name" of undefined & a Uncaught (in promise) SyntaxError: Unexpected token < in JSON at position 0 error.
here is my form submission:
const handleSubmit = async (e) => {
e.preventDefault();
setStatus("Sending...");
const { name, email, message } = e.target.elements;
let details = {
name: name.value,
email: email.value,
message: message.value,
};
console.log(typeof JSON.stringify(details))
let response = await fetch("/api/v1/mail", {
method: "POST",
headers: {
"Content-Type": "application/json;charset=utf-8"
},
body: JSON.stringify(details),
});
setStatus("Send Message");
let result = await response.json();
alert(result.status);
};
here is my server setup, the route in question is '/api/v1/mail':
const express = require('express')
const path = require('path')
const router = express.Router();
const cors = require("cors");
var expressStaticGzip = require("express-static-gzip")
const mailRoutes = require('./routes/mail');
const server = express()
server.use('/api/v1/mail', mailRoutes)
server.use(cors())
server.use(express.json())
server.use("/", router)
server.use(expressStaticGzip(path.join(__dirname, 'public'), {
enableBrotli: true
}))
server.use(express.static(path.join(__dirname, 'public')))
and here is my POST request:
router.post("/", (req, res) => {
const name = req.body.name;
const email = req.body.email;
const orderId = req.body.orderId
const message = req.body.message;
const mail = {
from: name,
to: "info#example.com ",
subject: "Contact Form Submission",
html: `<p>Name: ${name}</p>
<p>Email: ${email}</p>
<p>Order ID: ${orderId}
<p>Message: ${message}</p>`,
};
contactEmail.sendMail(mail, (error) => {
if (error) {
res.json({ status: "ERROR" , req});
} else {
res.json({ status: "Message Sent" });
}
});
});
})
If the route you're trying to use req.body.name in is this one:
server.use('/api/v1/mail', mailRoutes)
Then, you have to move this:
server.use(express.json())
to be BEFORE that route definition.
As it is, you're trying to handle the route request before your middleware has read and parsed the JSON. Route handlers and middleware are matched and executed in the order they are registered. So, any middleware that needs to execute for a route handler to function must be registered before the route handler itself is registered.

timeout async callback testing with sinon jest supertest to simulate error 500 on express api

I am testing an api with all http 500 errors.
Here I try to use sinon.stub to test on a failing server and get a 500 error, but I get a timeOut async callback, or if I use my app a successfull 200 response statusCode as if sinon.stub has no effect. I must miss something and I am stucked...
would you see a horrifying error below ?
thanks for your precious help
process.env.NODE_ENV = "test";
const app = require("../../app");
const request = require("supertest");
const sinon = require("sinon");
// /************************** */
const usersRoute = require("../../routes/Users");
const express = require("express");
const initUsers = () => {
const app = express();
app.use(usersRoute);
return app;
};
describe("all 5xx errors tested with stub", function () {
it("should return a 500 when an error is encountered", async (done) => {
let secondApp;
sinon.stub(usersRoute, "post").throws(
new Error({
response: { status: 500, data: { message: "failed" } },
})
);
secondApp = initUsers(); //==========> Timeout Async Callback
//secondApp = require("../../app"); //==============> gives a 200 instead of 500
const fiveHundredError = await request(secondApp)
.post("/users/oauth?grant_type=client_credentials")
.send({
username: "digitalAccount",
password: "clientSecret",
});
expect(fiveHundredError.statusCode).toBe(500);
//sinon.restore();
done();
});
});
app is using express.Router to get users route :
const express = require("express");
const router = express.Router();
const axios = require("axios");
router.post("/users/oauth", async (req, res) => {
//if (all missing parts)
//else {
try {
if (req.fields) {
const response = await axios.post(
`${base_url}oauth/token?grant_type=${req.query.grant_type}`,
{},
{
auth: {
username: req.fields.username,
password: req.fields.password,
},
}
);
res.json(response.data);
}
} catch (error) {
return res.status(error.response.status).json(error.response.data);
}
}
});
module.exports = router;
See server.js :
const app = require("./app");
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`server starting on port ${port}!`));
and app.js :
// private environment:
require("dotenv").config();
const express = require("express");
const formidableMiddleware = require("express-formidable");
const cors = require("cors");
const app = express();
app.use(formidableMiddleware());
app.use(cors());
const usersRoute = require("./routes/Users");
app.use(usersRoute);
app.get("/", (req, res) => {
res.status(200).send("Welcome to Spark Admin back end!");
});
app.all("*", (req, res) => {
return res.status(404).json({ error: "Web url not found" });
});
module.exports = app;
I finally opted for 'nock' and deleted 'sinon'
const nock = require("nock");
const axios = require("axios");
describe("POST login: all 5xx errors tested with nock", function () {
it("should return a 500 when an error is encountered", async (done) => {
const scope = nock("http://localhost:5000")
.post(
"/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
)
.reply(500, {
response: {
statusCode: 500,
body: { error: "AN ERROR OCCURED" },
},
});
try {
await axios.post(
"http://localhost:5000/users/oauth",
{},
{
username: "blibli",
password: "blabla",
}
);
} catch (e) {
expect(e.response.status).toBe(500);
}
done();
});
});

Express can't set headers after they are sent to the client

I have the following code:
router.post('/:email/addWorkflow', async function (req, res, next) {
const params = req.params;
const workflow = req.body;
const email = params.email;
User.findOne({ email: email }, function (err, user) {
if (err) {
res.status(500).send({
error: 'Error while querying database'
});
} else if (user) {
const workflows = user.workflows;
workflows.forEach(wf => {
if (wf) {
if (wf.workflowId === workflow.workflowId) {
res.status(409).send({
error: 'Workflow with that id already exists'
});
}
}
});
workflows.push(workflow);
User.updateOne({ email: email }, { $set: { workflows: workflows } }, { upsert: false }, function (err) {
if (err) {
res.status(500).send({
message: 'Error while updating database'
});
} else {
res.status(200).send({
message: 'Wf added successfully'
});
}
});
} else {
res.status(404).send({
message: 'No such user'
});
}
});
});
After I make a post with an already existing workflowId, I get the following error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:485:11)
..........
at /home/petar/Documents/jsProjects/p/backend/routes/users.js:50:29
at CoreDocumentArray.forEach (<anonymous>)
at /home/petar/Documents/jsProjects/p/backend/routes/users.js:47:17
at /home/petar/Documents/jsProjects/p/backend/node_modules/mongoose/lib/model.js:4915:16
at /home/petar/Documents/jsProjects/p/backend/node_modules/mongoose/lib/model.js:4915:16
at /home/petar/Documents/jsProjects/linear-mixed-models/backend/node_modules/mongoose/lib/query.js:4380:11
[... lines matching original stack trace ...]
at processTicksAndRejections (internal/process/task_queues.js:76:11) {
code: 'ERR_HTTP_HEADERS_SENT'
Any ideas? I looked at other posts for the same error. I understand that it happens if I try to send response 2 time: res.send({...}) and res.send({...}). However, this does not happen in my case. Thanks in advance
I am not completely sure what line the error message is indicating, but the following loop is the only place I can think of a multiple response on your code
workflows.forEach(wf => {
//foreach is looping
if (wf) {
if (wf.workflowId === workflow.workflowId) {
res.status(409).send({
error: 'Workflow with that id already exists'
});
//but I don't think this guy will stop looping after the first "send()"
}
}
});

I can't get the data from my server using reactjs and express

I want to get the data from my server and show it in reactjs. I think that my error is in the client side. I'm getting the data from a API and i know that the data calling works but i'm not expert using fetch and i'm learning.
this is my server
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const mysql = require('mysql');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
// connection configurations
const mc = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '12345',
database: 'node_task_demo',
//socketPath: '/Applications/MAMP/tmp/mysql/mysql.sock'
});
// connect to database
mc.connect();
// default route
app.get('/', function (req, res) {
return res.send({ error: true, message: 'hello' })
});
// Here where I'm calling in the client side
app.get('/todos', function (req, res) {
mc.query('SELECT * FROM tasks', function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results, message: 'Todo list' });
});
});
// Search for todos with ‘bug’ in their name
app.get('/todos/search/:keyword', function (req, res) {
var mensaje = 'Todos search list.';
let keyword = req.params.keyword;
mc.query("SELECT * FROM tasks WHERE task LIKE ? ", ['%' + keyword + '%'], function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results, message: mensaje});
});
});
// Retrieve todo with id
app.get('/todo/:id', function (req, res) {
let task_id = req.params.id;
if (!task_id) {
return res.status(400).send({ error: true, message: 'Please provide task_id' });
}
mc.query('SELECT * FROM tasks where id=?', task_id, function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results[0], message: 'Todos list.' });
});
});
// Add a new todo
app.post('/todo', function (req, res) {
let task = req.body.task;
if (!task) {
return res.status(400).send({ error:true, message: 'Please provide task' });
}
mc.query("INSERT INTO tasks SET ? ", { task: task }, function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results, message: 'New task has been created successfully.' });
});
});
// Update todo with id
app.put('/todo', function (req, res) {
let task_id = req.body.task_id;
let task = req.body.task;
if (!task_id || !task) {
return res.status(400).send({ error: task, message: 'Please provide task and task_id' });
}
mc.query("UPDATE tasks SET task = ? WHERE id = ?", [task, task_id], function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results, message: 'Task has been updated successfully.' });
});
});
// Delete todo
app.delete('/todo', function (req, res) {
let task_id = req.body.task_id;
if (!task_id) {
return res.status(400).send({ error: true, message: 'Please provide task_id' });
}
mc.query('DELETE FROM tasks WHERE id = ?', [task_id], function (error, results, fields) {
if (error) throw error;
return res.send({ error: false, data: results, message: 'Task has been updated successfully.' });
});
});
// all other requests redirect to 404
app.all("*", function (req, res, next) {
return res.send('page not found');
next();
});
// port must be set to 8080 because incoming http requests are routed from port 80 to port 8080
app.listen(8081, function () {
console.log('Escuchando por el puerto 8081');
});
// allows "grunt dev" to create a development server with livereload
module.exports = app;
This is my client
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
data: []
}
}
componentDidMount() {
return fetch('/todos')
.then((response) => response.json())
.then((responseJson) =>{
this.setState({
data: responseJson.data
});
})
}
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
Este es el resultado de la consulta = <b>{this.state.data}</b>
</p>
</div>
);
}
}
export default App;
React will only render JSX or strings. Unless this.state.data is a string, you will need to transform it into something React render.
You can test this by stringifying your data:
<p className="App-intro">
Este es el resultado de la consulta = <b>{JSON.stringify(this.state.data)}</b>
</p>
If this.state.data is a array, you will need to map it to JSX elements.