Request is not finished yet error with mongoDB and Express - javascript

I get a message Request is not finished yet and no data will be sent, if I do patch and delete requests in my app ( the post and get request work well)
Here are my requests
In service (front, Angular 4) I create the requests
api = 'http://localhost:3000/tasks';
deleteData(id) {
return this.http.delete( this.api, id);
}
patch(data) {
return this.http.patch( this.api, data);
}
And then call them in component
this.deleteData(this.checkedItems);
this.service.patch(result.data).subscribe(d => {
this.tasks = d;
});
The service
The PATCH request get req.body via console.log - so it should works, but it doesn't
The DELETE request doesn't get any data! The req.body is empty! But I need to pass the array of ids, so I can't do it via params.
Could you please help me or give a hint? Here is my service
const express = require('express');
const path = require('path');
const http = require('http');
const bodyParser = require('body-parser');
const mongoose = require('mongoose');
const cors = require('cors');
var Schema = mongoose.Schema;
const app = express();
//Middleware for CORS
app.use(cors());
app.use(express.json());
// Parsers for POST data
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// Point static path to dist
app.use(express.static(path.join(__dirname, 'dist')));
var todoSchema = new Schema({
taskName: String,
createdAt: Date,
isDone: Boolean,
prioraty: String
}, {
collection: 'tasks'
});
var Model = mongoose.model('Model', todoSchema);
//replace when DB is online
mongoose.connect('mongodb://localhost:27017/admin').then(() => {
console.log("connected");
}).catch (() => {
console.log("not connected");
});
mongoose.connection.once('open', function () {
console.log('mongodb connected.');
});
app.patch('/tasks', function (req, res) {
console.log(req.body);
var updateObject = {
'taskName': req.body.taskName,
'isDone': req.body.isDone,
'prioraty': req.body.prioraty
}
var id = req.body._id;
Model.collection.update({_id : id}, {$set: updateObject});
});
app.delete('/tasks', function(req,res){
console.log('Delete', req.body);
var ids = [];
for (let i = 0; i < req.body.length; i ++) {
ids.push(req.body[i]._id);
}
var myquery = { _id: { $in: ids } };
Model.collection.deleteMany(myquery, function(err, obj) {
if (err) throw err;
});
});
const port = process.env.PORT || '3000';
app.set('port', port);
/**
* Create HTTP server.
*/
const server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port, () => console.log(`API running on localhost:${port}`));

You need to close the connection when you're done handling the request, otherwise the client will wait for the server to send a response until the request timeout is reached.
app.patch('/tasks', function (req, res) {
...
Model.collection.update({_id : id}, {$set: updateObject}, function (err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
app.delete('/tasks', function(req,res){
...
Model.collection.deleteMany(myquery, function(err) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
res.sendStatus(200);
});
});
As for the DELETE request not having a req.body, that's because Angular 4's http client doesn't allow a body for DELETE requests. Its API for DELETE requests looks like this: this.http.delete(url, httpOptions), with no body support. You'll have to use query parameters if you need to send an array of ids. Query params does support arrays, they look something like this: https://myurl.xyz/tasks?ids[]=1&ids[]=2&ids[]=3
See https://angular.io/guide/http#url-parameters

Related

SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data on React app

I have a React app that uses express in the backend. I try to get from my db a list of messages through a fetch API call.
The code in the Frontend:
App.js
getMessages = () => {
fetch('/api/mess')
.then(res => res.json())
.then(res => {
var Messages = res.map(r => r.messages);
this.setState({ Messages });
});
};
The code in the backend:
api/mess.js
var express = require('express');
var Mess = require('../queries/mess');
var router = express.Router();
router.get('/', (req, res) => {
Mess.retrieveAll((err, messages) => {
if (err)
return res.json(err);
return res.json(messages);
})
});
router.post('/', (req, res) => {
var message = req.body.message;
Mess.insert(message, (err, result) => {
if (err)
return res.json(err);
return res.json(result);
});
});
module.exports = router;
queries/mess.js
const db = require('../database');
class Mess {
static retrieveAll(callback) {
db.query('SELECT * FROM mess;', (err, res) => {
if (err.error)
return callback(err);
callback(res);
});
}
static insert(mess, callback) {
db.query('INSERT INTO mess (messages) VALUES ($1)', [mess], (err, res) => {
if (err.error)
return callback(err);
callback(res);
});
}
}
module.exports = Mess;
index.js
const path = require('path');
const express = require('express');
const bodyParser = require('body-parser');
var db = require('./database');
const ENV = process.env.NODE_ENV;
const PORT = process.env.PORT || 3011;
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/api/mess', require('./api/mess'));
app.use('/api/roles', require('./api/roles'));
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}...`);
});
module.exports = app;
I get this error on my Frontend:
SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data
I have tried and changed every response and request to use either JSON.parse or .json and I get the same message no matter what.
When I use my browser and go to the api channel I get a correctly formatted JSON with the contents of the db.
Did I miss something?
EDIT:
The stack trace is super minimal:
When I try:
getMessages = () => {
fetch('/api/mess')
.then(res => console.log(res));
};
I get this object:
The problem is that the backend is at PORT 3011 and the api call is made to PORT 3000 from the frontend.
I need to have a proxy to point from the forntend to the backend port.
I need to add inside client/package.json (Frontend) this line:
"proxy": "http://localhost/3011"

Never resolving HTTP Requests from Node+Express API to MongoDB

I'm building a pretty simple API to do a basic CRUD operations on a local mongo database. The code looks fine for me but somehow the CRUD operations results on a pending request which never ends.
Here the parts of the code:
spawn.model.js (Model corresponding to database collection)
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var SpawnSchema = Schema({
Name: {
type: String,
unique: false,
required: true
}
}, { timestamps: true });
module.exports = mongoose.model('spawns', SpawnSchema);
spawn.controller.js
var Spawn = require('../models/Spawn/spawn.model');
exports.getSpawns = function(req, res){
Spawn.find({}, function(spawns){
res.send(spawns);
});
}
Here the spawn.routes.js file:
var Spawns = require('../controllers/spawn.controller');
module.exports = function(app){
app.get('/list', Spawns.getSpawns);
}
And then finally the server.js file:
var express = require('express');
var bodyParser = require('body-parser');
var properties = require('./config/properties');
var db = require('./config/database');
var app = express();
//configure bodyparser
var bodyParserJSON = bodyParser.json();
var bodyParserURLEncoded = bodyParser.urlencoded({ extended: true });
// call the database connectivity function
db();
// configure app.use()
app.use(bodyParserJSON);
app.use(bodyParserURLEncoded);
// Routes
app.get('/', function(req, res){
res.json({ message: 'Spawns API' });
});
require('./app/routes/spawn.routes')(app);
// intialise server
app.listen(properties.PORT, () => {
console.log(`Server is running on ${properties.PORT} port.`);
})
The database file on ./config is the following:
var mongoose = require('mongoose');
var dbURL = require('./properties').DB;
mongoose.Promise = global.Promise;
module.exports = function(){
mongoose.connect(dbURL, { useNewUrlParser: true }, function(){
console.log('Successfully connected to database');
});
}
And the properties.js on /config is simply an object with the database URL and the port for the express server.
When I try to to a request through Postman to the URL: http://localhost:4000/list the request gets hanged and never resolves. What am I missing?
PD: SOLVED!
===========
I needed to update mongoose version on npm cause it was 3.x and needed to be 5.x in order to work well with the new methods.
Update your code little bit, Like this and check
spwanRoute.js
const express = require('express');
const router = express.Router();
const spawnCntr = require('./speanControllers');
router.get('/list', spawnCntr.getSpawns);
module.exports = router;
spwanUtils.js
const Spawns = require('../models/Spawn/spawn.dao');
const spawnUtils = {};
spawnUtils.getSpawns = (req, res) => {
try {
Spawns.get({}, (err, spawns) => {
if(err){
return res.status(400).json({ error: err });
}
return res.status(200).json({ spawns });
});
} catch (err) {
console.log(err);
return res.status(500).json({ error: 'INTERNAL_EROR' });
}
}
module.exports = spawnUtils;

Save data to MongoDB using NodeJS

I am trying to pass data to a MongoDB collection and it returns Cannot POST /courseweb/course/add
Before passing values through axios I tried postman (a google extension) to send data.
This is my server.js which is implemented with expressjs
const express = require("express");
const app = express();
const bodyParser = require("body-parser");
const Bundler = require("parcel-bundler");
const cors = require("cors");
const mongoose = require("mongoose");
const InstructorDB = require('./public/DBModels/InstructorDB');
const router = express.Router();
const bundler = new Bundler("./src/index.html");
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(bundler.middleware());
// app.use(express.static('./src'));
app.use("/courseweb", router);
mongoose.connect("mongodb://127.0.0.1:27017/courseweb", {
useNewUrlParser: true
});
const connection = mongoose.connection;
connection.once("open", () => {
console.log("Connected to MongoDB via 27017");
});
app.listen(3000, err => {
if (err) {
console.error(err);
process.exit(-1);
}
console.log("Application is running on port 3000");
});
app.get("/", function(req, res) {
res.sendFile("./dist/index.html");
});
router.route('/course/add').post((req, res) => {
let instructorDB = new InstructorDB(req.body);
instructorDB.save().then(bookDB => {
res.status(200).send(`${bookDB} Added`);
}).catch((err) => {
res.status(400).send({message: err});
});
});
router.route('/courses').get((req, res) => {
// name of the course database model here
InstructorDB.find().count(function(err, count){
res.status(200).send(count);
});
});
And this is my InstructorDB.js which is a schema model by mongoose
const mongoose= require('mongoose');
const Schema = mongoose.Schema;
let InstructorDB = new Schema({
firstName: String,
lastName: String,
designation: String,
faculty: String,
contactNumber: Number,
email: String,
password: String,
isEnabaled: Boolean,
courses: [{courseID: String}]
});
module.exports = mongoose.model('InstructorDB', InstructorDB, 'InstructorDB');
And this is a screenshot and the response I get when I pass the values through postman. I have set header as content-type and application/json too
Can anyone tell me where I have gone wrong?
Make sure you send the right data via your post request and change the verb to post :
app.post('/course/add', (req, res) => {
if(req.body == null){
return res.status(400).send({message: 'bad request'});
}
let instructorDB = new InstructorDB(req.body);
instructorDB.save((err ,doc ) => {
if(err){
res.status(400).send({message: err});
}
res.status(200).send(`Added`);
});
});
You don't need router if you're going to put it in the same file.
try this syntax instead:
app.post('/coureweb/course/add',((req, res) => {
let instructorDB = new InstructorDB(req.body);
instructorDB.save().then(bookDB => {
res.status(200).send(`${bookDB} Added`);
}).catch((err) => {
res.status(400).send({message: err});
});
}));
then take out
app.use("/courseweb")

How to call POST method in express js?

I am trying to learn REST API. I created POST method but it is not working
get method is working fine in postman but post method is not working. Can anyone help me where I am missing?
I am stuck in it.
here is my code
app.js
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
//connect to mongoose
Genre =require('./models/genre');
Book =require('./models/book');
// Connect to Mongoose
mongoose.connect('mongodb://localhost/bookstore',{ useNewUrlParser: true });
var db = mongoose.connection;
app.get('/', (req, res) => {
res.send('Please use /api/book or /api/genres');
});
app.get('/api/genres', (req, res) => {
Genre.getGenres((err, genres) => {
if(err){
throw err;
}
res.json(genres);
});
});
app.post('/api/genres', (req, res) => {
var genre = req.body;
Genre.addGenre(genre, (err, genre) => {
if(err){
throw err;
console.log(err);
}
res.json(genre);
});
});
app.listen(3000);
console.log("running on port 3000..");
models/genre.js
const mongoose = require('mongoose');
// Genre Schema
const genreSchema = mongoose.Schema({
name:{
type: String,
required: true
},
create_date:{
type: Date,
default: Date.now
}
});
const Genre = module.exports = mongoose.model('Genre', genreSchema);
// Get Genres
module.exports.getGenres = (callback /* we can access through routes*/, limit) => {
Genre.find(callback).limit(limit);
}
//add genre
module.exports.addGenre = (genre, callback) => {
Genre.create(genre, callback);
}
get method is working fine in postman but post method is not working. Can anyone help me where I am missing?
You need to update your code accordingly:
Add in app.js:
// BodyParser middleware
const BodyParser = require(`body-parser`);
// Create application/json parser
App.use(BodyParser.json({ limit: `50mb` })); // Set request size
// create application/x-www-form-urlencoded parser
App.use(BodyParser.urlencoded({ limit: `50mb`, extended: true }));
Update API call:
app.post('/api/genres', (req, res) => {
var genre = req.body;
Genre.addGenre(genre, (err, genreDB) => {
if(err){
throw err;
console.log(err);
}
res.status(200).send(genreDB);
});
});
Hope this works for you.
If you want to access req.body in your POST handler, you'll need to use some express middleware to actually parse the request body. For example, for parsing JSON bodies, you'll need express.json middleware.

Passing Mongo DB Object DB to Express Middleware

I am having problems trying to access the "DB" database object that is created when the MongoDB client module connects to my MongoDB database.
At the moment I am getting an error stating that, within data.js, 'db' is not defined. I understand why this is - the db object is not being "passed" through to the router and then subsequently through to the controller.
What is the best way to do this?
I have tried to pass the "db" object through to the router (dataRoutes.js) but I cannot figure how to make this accessible to the controller (data.js). Could someone please help?
Please note I have not included the other routes and controllers but they simply submit a Form via the POST method to /data/submit . The controller below is meant to write this form data to the MongoDB database.
Here is the relevant code:
app.js
var express = require('express');
var path = require('path')
var MongoClient = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var app = express();
var routes = require('./routes/index');
var dataRoutes = require('./routes/dataRoutes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
if(err) throw err;
console.log("Successfully connected to MongoDB.");
app.use('/', routes); // Use normal routes for wesbite
app.use('/data', dataRoutes);
app.get('/favicon.ico', function(req, res) {
res.send(204);
});
app.use(function(req, res, next) {
var err = new Error('Oops Page/Resource Not Found!');
err.status = 404;
next(err); //Proceed to next middleware
});
if (app.get('env') === 'development') {
app.use(function(err, req, res, next) {
// update the error responce, either with the error status
// or if that is falsey use error code 500
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
}
app.use(function(err, req, res, next) {
console.log('Error');
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: {}
});
});
var server = app.listen(3000, function() {
var port = server.address().port;
console.log("Express server listening on port %s.", port);
});
});
dataRoutes.js
// router
var express = require('express');
var router = express.Router();
// controller references
var ctrlsData = require('../controllers/data');
router.post('/submit', ctrlsData.submit);
module.exports = router;
data.js
var MongoClient = require('mongodb').MongoClient;
var sendJsonResponse = function(res, status, content) {
res.status(status);
res.json(content);
};
module.exports.submit = function(req, res) {
var title = req.body.title;
var year = req.body.year;
var imdb = req.body.imdb;
/*
console.log('submitted');
console.log(req.body);
sendJsonResponse(res, 201, {title,year,imdb});
*/
var title = req.body.title;
var year = req.body.year;
var imdb = req.body.imdb;
if ((title == '') || (year == '') || (imdb == '')) {
sendJsonResponse(res, 404, {
"message": "Title, Year and IMDB Reference are all required."
});
} else {
db.collection('movies').insertOne(
{ 'title': title, 'year': year, 'imdb': imdb },
function (err, r) {
if (err) {
sendJsonResponse(res, 400, err);
} else {
sendJsonResponse(res, 201, "Document inserted with _id: " + r.insertedId + {title,year,imdb});
}
}
);
}
};
Create a db variable that reference mongodb in app.js :
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
app.db = db;
//.....
});
In data.js, access db from req.app :
module.exports.submit = function(req, res) {
req.app.db.collection('movies').insertOne({ 'title': title, 'year': year, 'imdb': imdb },
function(err, r) {}
)
};
The accepted answer isn't quite correct. You shouldn't attach custom objects to the app object. That's what app.locals is for. Plus, the accepted answer will fail when using Typescript.
app.locals.db = db;
router.get('/foo', (req) => {
req.app.locals.db.insert('bar');
});
Sure, it's longer. But you get the assurance that future updates to ExpressJS will not interfere with your object.
I understand that the answer of #Bertrand is functional, but it is not usually recommended. The reason being that, from a software point of view, you should have a better separation in your software.
app.js
var express = require('express');
var path = require('path')
var MongoClient = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var app = express();
var routes = require('./routes/index');
var dataRoutes = require('./routes/dataRoutes');
var DB = require('./db.js');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, 'public')));
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');
DB.Init("mongodb://localhost:27017/m101")
.then(() => {
console.log("Successfully connected to MongoDB.");
app.use('/', routes); // Use normal routes for wesbite
app.use('/data', dataRoutes);
app.get('/favicon.ico', function(req, res) {
res.send(204);
});
var server = app.listen(3000, function() {
var port = server.address().port;
console.log("Express server listening on port %s.", port);
});
})
.catch((e) => {
console.log("Error initializing db");
});
db.js
var _db = null;
module.exports = {
Init: (url) => {
return new Promise((resolve, reject) => {
if (!url)
reject("You should provide a URL");
MongoClient.connect("mongodb://localhost:27017/m101", function(err, db) {
if(err) reject(err);
_db = db;
resolve(); // Or resolve(db) if you wanna return the db object
});
});
},
Submit: (req, res, next) => {
// Whatever goes. You have access to _db here, too!
}
};
in data.js
var DB = require('../db.js');
router.post('/submit', DB.submit);
Finally, even this answer can be improved as you are not usually advised to wait for the DB to connect, otherwise, you are losing the advantage of using ASync procs.
Consider something similar to here in app.js:
Promise.resolve()
.then(() => {
// Whatever DB stuff are
// DB.Init ?
})
.then(() => {
// Someone needs routing?
})
...
.catch((e) => {
console.error("Ther app failed to start");
console.error(e);
});
I understand that in the last sample, you can not instantly query DB as it may not have connected yet, but this is a server, and users are usually expected to wait for your DB to init. However, if you wanna more proof solution, consider implementing something yourself in DB.submit to wait for the connect. Or, you can also use something like mongoose.

Categories