API get call returns status 400 - javascript

I'm trying to learn how to use the MERN stack. I've been following a YouTube tutorial (https://www.youtube.com/watch?v=WT67-OETeGU). Currently, I've defined and created my server.js file as follows:
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const cors = require("cors");
const mongoose = require("mongoose");
const todoRoutes = express.Router();
const PORT = 4000;
let Todo = require("./todo.model.js");
app.use(cors());
app.use(bodyParser.json());
mongoose.connect("mongodb://127.0.0.1:27017/todos", {
useUnifiedTopology: true,
useNewUrlParser: true
});
const connection = mongoose.connection;
todoRoutes.route("/").get(function(req, res) {
Todo.find(function(err, todos) {
if (err) {
console.log("error getting data");
} else {
res.json(todos);
}
});
});
todoRoutes.route("/:id").get(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (err) {
console.log("error finding todo object with id: " + id);
} else {
res.json(todo);
}
});
});
todoRoutes.route("/add").post(function(req, res) {
let todo = new Todo(req.body);
todo
.save()
.then(todo => {
res.status(200).json({ todo: "Todo added successfully" });
})
.catch(err => {
res.status(400).send("failed to save new todo");
});
});
todoRoutes.route("/update/:id").post(function(req, res) {
let id = req.params.id;
Todo.findById(id, function(err, todo) {
if (!todo) {
res.status(400).send("cant update id: " + id);
} else {
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
todo
.save()
.then(todo => {
res.json("Todo updated");
})
.catch(err => res.status(400).send("update not possible"));
}
});
});
app.use("./todos", todoRoutes);
I am trying to test my API out, so I installed Postman and set it up. I'm trying to make a GET request to: http://localhost:4000/todos. However, Postman only returns a 404, saying "Could not get any response".
I've run the commands mongod and mongo on my terminal to get Mongo running.
Any suggestions on where I've gone wrong?

It seems you are setting your endpoints as relative paths. Can you remove the dot proceeding "/todos"? in app.use("./todos", todoRoutes);
`

Related

Cannot POST /api/sentiment

I'm testing the endpoint for /api/sentiment in postman and I'm not sure why I am getting the cannot POST error. I believe I'm passing the correct routes and the server is listening on port 8080. All the other endpoints run with no issue so I'm unsure what is causing the error here.
server.js file
const express = require("express");
const cors = require("cors");
const dbConfig = require("./app/config/db.config");
const app = express();
var corsOptions = {
origin: "http://localhost:8081"
};
app.use(cors(corsOptions));
// parse requests of content-type - application/json
app.use(express.json());
// parse requests of content-type - application/x-www-form-urlencoded
app.use(express.urlencoded({ extended: true }));
const db = require("./app/models");
const Role = db.role;
db.mongoose
.connect(`mongodb+srv://tami00:MEUxClWqUNbLz359#cluster0.gmvao.mongodb.net/test?retryWrites=true&w=majority`, {
useNewUrlParser: true,
useUnifiedTopology: true
})
.then(() => {
console.log("Successfully connect to MongoDB.");
initial();
})
.catch(err => {
console.error("Connection error", err);
process.exit();
});
// simple route
app.use('/api/favourite', require('./app/routes/favourite.routes'));
app.use('/api/review', require('./app/routes/review.routes'));
app.use('/api/sentiment', require('./app/routes/sentiment-analysis.routes'));
// routes
// require(".app/routes/favourite.routes")(app);
require("./app/routes/auth.routes")(app);
require("./app/routes/user.routes")(app);
// set port, listen for requests
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}.`);
});
function initial() {
Role.estimatedDocumentCount((err, count) => {
if (!err && count === 0) {
new Role({
name: "user"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'user' to roles collection");
});
new Role({
name: "creator"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'creator' to roles collection");
});
new Role({
name: "watcher"
}).save(err => {
if (err) {
console.log("error", err);
}
console.log("added 'watcher' to roles collection");
});
}
});
}
sentiment-analysis routes file
const express = require('express');
const router = express.Router();
const getSentiment = require('../sentiment-analysis/sentimentAnalysis')
router.post('/api/sentiment', (req, res) => {
const data = req.body.data
const sentiment = getSentiment(data)
return res.send({sentiment})
})
module.exports = router;
sentimentAnalysis.js file
const aposToLexForm = require("apos-to-lex-form");
const {WordTokenizer, SentimentAnalyzer, PorterStemmer} = require("natural");
const SpellCorrector = require("spelling-corrector");
const stopword = require("stopword");
const tokenizer = new WordTokenizer();
const spellCorrector = new SpellCorrector();
spellCorrector.loadDictionary();
const analyzer = new SentimentAnalyzer('English', PorterStemmer, 'afinn')
function getSentiment(text){
if(!text.trim()) {
return 0;
}
const lexed = aposToLexForm(text).toLowerCase().replace(/[^a-zA-Z\s]+/g, "");
const tokenized = tokenizer.tokenize(lexed)
const correctSpelling = tokenized.map((word) => spellCorrector.correct(word))
const stopWordsRemoved = stopword.removeStopwords(correctSpelling)
console.log(stopWordsRemoved)
const analyzed = analyzer.getSentiment(stopWordsRemoved);
console.log(analyzed)
}
module.exports = getSentiment;
console.log(getSentiment("Wow this is fantaztic!"))
console.log(getSentiment("let's go together?"))
console.log(getSentiment("this is so bad, I hate it, it sucks!"))
I see that you use your routes like: app.use('/api/sentiment', require('./app/routes/sentiment-analysis.routes'));. But then in your sentiment-analysis you again use /api/sentiment so your request URL should be /api/sentiment/api/sentiment
Shouldn't it be:
const data = req.body.data

How come fetch only works here when I add an alert to the end of the line? Express + NodeJS + Fetch. What's a good fix here

I'm using NodeJS w/ Express to create a web app that records your audio using the VMSG library and posts the BLOB audio to my file system using HTTP Requests and multer. It also adds that instance of a recording into a MongoDB database.
I'm having an issue with the fetch command. It's not working unless I put an alert right after the fetch. The way I have it set up is that I have my main express app (index.js), and a router to the /recordingsDirectory (recordings.js) which is the endpoint for processing the posts. My main index HTML page uses Handlebars and uses a separate JS script (recorder.js) to 1) use the VMSG library and 2) fetch a POST to the /recordingsDirectory once someone submits the audio file w/ the name and the AudioBlob present. This is where I'm stuck. I can fetch in recorder.js with an alert line after the fetch, but I can't have the fetch on the end of the else if block by itself. I'd like to do it without this since the alert is ugly. A solution I've tried is that I tried to make the onsubmit function async and await fetch since I thought maybe it's waiting for a promise but that didn't work.
Here are the files. I commented CRITICAL and SUPER CRITICAL to the lines of code that you should check out and I think where the issues lie:
index.js
const express = require('express')
const handlebars = require('express-handlebars')
const path = require('path')
const XMLHttpRequest = require('xmlhttprequest').XMLHttpRequest
const xhr = new XMLHttpRequest()
const db = require('./db')
const app = express()
const PORT = process.env.PORT || 8000
app.set('view engine', 'hbs')
app.engine('hbs', handlebars({
layoutsDir: path.join(__dirname, 'views', 'layouts'),
extname: 'hbs',
defaultLayout: 'index',
partialsDir: path.join(__dirname, 'views', 'partials'),
}))
app.use(express.json())
app.use(express.urlencoded({extended: false}))
app.use((err, req, res, next) => {
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
return res.status(400).send({ status: 404, message: err.message })
}
next()
})
app.get('/', (req, res) => {
res.render('main', {
title: 'Main Page'
})
})
app.get('/recordings', (req, res) => {
var database = db.get().db('AudioJungle')
database.collection('recordings').find().sort({ "date": -1 }).toArray(function(err, docs) {
res.render('recordings', {
title: 'Recordings',
recordings: docs
})
})
})
// CRITICAL
app.use('/recordingsDirectory', require('./recordings/recordings'))
app.use(express.static('public'))
app.use('/scripts', express.static(path.join(__dirname, 'node_modules', 'vmsg')))
db.connect(function(err) {
if (err) {
console.log('Unable to connect to Mongo.')
process.exit(1)
} else {
app.listen(PORT, () => console.log(`Listening on Port: ${PORT}`))
}
})
process.on('SIGINT', function() {
db.close(function () {
console.log('Disconnected on app termination');
process.exit(0);
});
});
app.use((req, res, next) => {
res.status(404).send({
status: 404,
error: 'Not found'
})
})
recordings.js (Aka the /recordingsDirectory endpoint for a fetch POST)
const express = require('express')
const router = express.Router()
const multer = require('multer')
const fs = require('fs-extra')
const db = require('../db')
const { ObjectId } = require('bson')
const moment = require('moment')
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, callback) => {
let path = './public/uploads'
fs.mkdirsSync(path)
callback(null, path)
},
filename: (req, file, callback) => {
createRecording(req).then((id) => {
var file_name = id + '.mp3'
callback(null, file_name)
})
}
})
})
var type = upload.single('audio-file')
// CRITICAL
router.post('/', type, (req, res) => {
console.log('made it')
res.status(200)
res.send('OK')
})
router.delete('/delete', (req, res) => {
deleteRecording(req.body._id).then((dbResponse) => {
if (dbResponse == null || dbResponse == undefined) {
res.status(400).json({ msg: 'ID already deleted' })
} else {
res.status(200)
}
})
})
router.get('/', (req, res) => {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
recordings.findOne({"_id": ObjectId(req.query.id)}, function(err, result) {
if (err) throw err
if (result == null || result == undefined) {
return res.status(400).json({
status: 404,
error: 'Recording no longer in the database'
})
}
res.status(200)
res.json({
name: result.name,
date: result.date
})
})
})
async function createRecording(req) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioObject = {
name: req.body.name,
date: moment().format('MMMM Do YYYY, h:mm:ss a')
}
var dbResponse = await recordings.insertOne(audioObject)
return dbResponse.insertedId
}
async function deleteRecording(id) {
var database = db.get().db('AudioJungle')
var recordings = database.collection('recordings')
var audioToDelete = {
_id: ObjectId(id)
}
var deleteResult = await recordings.deleteOne(audioToDelete)
return deleteResult
}
module.exports = router
And below is the Script the audio and name and tries to Fetch (where I need the alert for it to actually process into the /recordingsdirectory)
recorder.js
import { record } from "/scripts/vmsg.js";
let recordButton = document.getElementById("record");
var blobObj = null
recordButton.onclick = function() {
record({wasmURL: "/scripts/vmsg.wasm"}).then(blob => {
blobObj = blob
var tag = document.createElement("p")
tag.id="finishedRecording"
var text = document.createTextNode("Audio File Recorded")
tag.appendChild(text)
var element = document.getElementById("box")
element.appendChild(tag)
document.getElementById('box').appendChild(a)
})
}
let form = document.getElementById('mp3Form');
form.addEventListener("submit", submitAudio)
function submitAudio() {
var fileName = form.elements[0].value
if (fileName == "") {
alert('Please enter a name for your file')
} else if (blobObj != null) {
// CRITICAL
// SUPER CRITICAL WHERE FETCH DOESN'T WORK UNLESS I PUT AN ALERT AT THE END
const formData = new FormData()
formData.append('name', fileName)
formData.append('audio-file', blobObj)
const options = {
method: 'POST',
body: formData
}
fetch('/recordingsDirectory', options);
// If I comment an alert here, /recordingsDirectory will process the post since it console.logs 'made it'
} else {
alert('Record some Audio to upload')
}
}
Here's my file system.
Also, I'd like to mention that the fetch works properly on my Windows PC without having to add the alert, but it doesn't work without the alert on my macbook. If any one figures out a fix or an error in how I'm doing things to allow this please let me know. I've been stuck on this problem for a day now. Thanks a bunch!

How to increment property in MongoDB collection using $inc

I have a list of articles that have a property views and I want to increment that property in the database each time a user clicks on an article title. Currently nothing happens when I do it. Why isn't it working and how can I increment that property each time on click? Here is my React part:
const incrementViews = (id) => {
var item = posts.find(x => x._id === id);
item.views += 1;
}
<div className="post-title">
<Link to={`/post/${post._id}`}>
<h2><a href="#" onClick={() => incrementViews(post._id)}>{post.title}</a>
</h2>
</Link>
</div>
and my server.js:
// Requiring the dependencies
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
require('dotenv').config();
const mongoose = require('mongoose');
const PORT = process.env.PORT || 3001;
const BASE_URL = process.env.REACT_APP_BASE_URL;
console.log(BASE_URL)
const itemRoutes = express.Router();
let Comment = require('./comment.model');
app.use(cors());
app.use(bodyParser.json());
mongoose.connect(BASE_URL, { useNewUrlParser: true })
const connection = mongoose.connection;
connection.once('open', function () {
console.log('Connection to MongoDB established succesfully!');
});
let collection = connection.collection("posts_with_tags_test");
collection.createIndex(
{
postContent: 'text',
title: 'text'
}
);
// Serve static assets
if (process.env.NODE_ENV === 'production') {
app.use(express.static('build'));
}
itemRoutes.route('/').get(async (req, res) => {
let collection = connection.collection("posts_with_tags_test");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/search').post(async (req, res) => {
let result = await connection.collection("posts_with_tags_test").find({
$text: {
$search: req.body.searchString
}
}).toArray();
res.send(result);
});
itemRoutes.route("increment/:id"").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
itemRoutes.route('/comments').get(async (req, res) => {
let collection = connection.collection("comments");
let response = await collection.find({})
.toArray();
res.send(response);
});
itemRoutes.route('/comments')
.post((req, res) => {
res.setHeader('Content-Type', 'application/json');
let comment = new Comment(req.body);
comment.save()
.then(comment => {
res.status(200).json({ comment })
})
.catch(err => {
res.status(400).send('failed')
})
});
app.use('/', itemRoutes);
app.use('/comments', itemRoutes);
app.use('/search', itemRoutes);
app.use('/increment', itemRoutes);
app.listen(PORT, function () {
console.log('Server is running on' + ' ' + PORT);
})
I think there are two problems in frontend and backend respectively.
Front-end
You should use post variable as a state variable so as to re-render then component when changes are made on post.
Back-end
There is no issue with increasing view in your code.
Here, you need to return success status.
The function incrementViews only increments views on the frontend and never sends any data to the API. One way you can make it work is as follows:
server.js
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
collection.updateOne({ _id: id }, { $inc: { views: 1 } });
return res.status(200).json({ msg: "OK" });
});
React
const incrementViews = (id) => {
// Assuming your API server is running on port 5000.
fetch(`http://localhost:5000/increment/${id}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
})
.then((res) => res.json())
.then(console.log)
.catch(console.error);
};
Update
The reason you're getting 404 is a missing colon : in the route parameters.
// Notice the :id, colon is important.
itemRoutes.route("/increment/:id").post(async (req, res) => {
const { id } = req.params;
// ...
});
Here is a demo reproduced on Glitch. Removed database logic and just added a response messages.
I tested the demo using Postman and it works fine.
On a POST request to https://adaptive-sassy-legal.glitch.me/increment/123, should return a response as shown below.
{
msg: "itemsRoute increment.",
id: "123"
}
Update 2
Another thing which I forgot to mention in the previous update was to update the middleware.
// Use only `/increment` instead of `increment/:id`.
app.use("/increment", itemRoutes);
Here is an updated demo.

"Sending request" loading on Get request on Postman

My /chat route works well through Post method with validation with Joi schema but when I send request through Get method, it show Sending Request and continue loading...
My index.js file:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const chat = require('./db/ChatModel');
const app = express();
app.use(bodyParser.json());
app.get('/chat', (req, res) => {
chat.getAllMessages().then( (messages) => {
res.json(messages);
});
});
app.post('/chat', (req, res) => {
console.log(req.dody);
chat.createMessages(req.body).then((message) => {
res.json(message);
}).catch( (error) => {
res.status(500);
res.json(error);
});
});
const port = process.env.PORT || 8888;
app.listen(port, () => {
console.log(`Listening on port ${port}...`);
});
In connection.js I coded this
const monk = require('monk');
const connectionString = 'localhost/chatboard';
const db = monk(connectionString);
module.exports = db;
And ChatModal.js has the following code
const Joi = require('joi');
const db = require('./connection');
const schema = Joi.object().keys({
username: Joi.string().alphanum().min(4).max(16).required(),
subject: Joi.string().required(),
message:Joi.string().max(300).required(),
imgUrl: Joi.string().uri({
scheme: [ // https://github.com/hapijs/joi/blob/v14.3.1/API.md#stringurioptions
/https?/
]
})
});
const chat = db.get('chat');
function getAllMessages() {
return chat.find();
};
function createMessages(message) {
const result = Joi.validate(message, schema);
if (result.error == null) {
message.created = new Date();
return chat.insert(message);
} else {
return Promise.reject(result.error);
}
}
module.exports = {
createMessages,
getAllMessages
};
I can't understand why getAllMessages() doesn't work and postman continue loading when Get request applied like this http://prntscr.com/s0d9c5
ChatModal.js
function getAllMessages() {
try {
return chat.find();
} catch (err) {
return next(err);
}
index.js
app.get('/chat', (req, res, next) => {
try{
data = chat.getAllMessages()
} catch (err) {
return next(error);
}
res.json(data);
});
User try-catch in the ChatModal.js and also index.js then you can understand what is actual error, like bellow:
ChatModal.js
function getAllMessages() {
try {
chat.find();
} catch (err) {
return next(err);
}
I think, may be your data, i mean message list data so weight, in this case you get all message,res.json(messages); json method have long time to parse messages data

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client in Node JS

I am trying to update a data using a specific ID which is not working but gives above error.
When I update, first I search the database for that specific id and then save the data to MongoDB
here is my server.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
//importing MongoDB model schema
let ToDo = require('./todo.model');
const app = express();
const todoRoutes = express.Router();
const PORT = 4000;
//middlewares
app.use(bodyParser.json());
app.use(cors());
app.use('/todos', todoRoutes);
//connection to the MongoDB database
mongoose.connect('mongodb://127.0.0.1:27017/todos', {useNewUrlParser: true});
const connection = mongoose.connection;
connection.once('open', () =>{
console.log("Connected to the MongoDB through port: 27017");
});
app.listen(PORT, () => {
console.log(`Listening to port: ${PORT}`);
});
//get all data - removed to show only the code snippet I am getting errors
//get data by an ID - removed to show only the code snippet I am getting errors
//add items to database -removed to show only the code snippet I am getting errors
//update items
todoRoutes.route('/update/:id').post((req, res) => {
let id = req.params.id;
ToDo.findById(id, (err, todo) => {
if(err) throw err;
if(!todo) res.status(400).send("No data found");
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
res.end();
todo.save().then(todo => {
res.json(200).send("Data Updated! " + todo);
res.end();
}).catch(err => {
res.status(400).send("Error occured! " + err);
});
});
});
This is the error I am getting...
Can someone please help me?
This error usually means that you send a response more than once.
Notice that you send two responses one after the other res.json() and res.end()
If you want for some reason to just end the response, use res.end(), otherwise use res.status(200).json({ result: ‘Data updated’ + todo })
If you send both, it will complain about trying to modify the response (via res.end()) after sending it (via res.status().json())
In the '/update/:id' route, you're sending a res.end() then doing it again 3 lines later. If you remove the first res.end(), it should work.
You should also return if todo is missing:
todoRoutes.route('/update/:id').post((req, res) => {
let id = req.params.id;
ToDo.findById(id, (err, todo) => {
if(err) throw err;
if(!todo) return res.status(400).send("No data found");
todo.todo_description = req.body.todo_description;
todo.todo_responsible = req.body.todo_responsible;
todo.todo_priority = req.body.todo_priority;
todo.todo_completed = req.body.todo_completed;
todo.save().then(todo => {
res.status(200).send("Data Updated! " + todo);
}).catch(err => {
res.status(400).send("Error occured! " + err);
});
});
});

Categories