Connection terminated when I do POST request? - javascript

I am wondering if anyone can help me out here. I'm currently taking a Udemy course on learning how to connect a database with my server. I have been going word by word on the teacher's code, frequently checking if my code has any minor errors. I didn't have any trouble with code thus far on my server.js file. Port is running smoothly. However, when I run the POST request from Postman, I get
Unhandled rejection Error: Connection terminated unexpectedly
Through Postman, I am following exactly what the teacher did. The POST request contains email, password, name and the localhost:3000/register path is fine. That is what my server is connected to. I'm wondering what's going on since my code runs smoothly until I do a POST request. Also noting that I get 200 OK response on Postman but on server, I get that unhandled rejection error message.
Yeah that console log is intentional, I am going along exactly what he is doing in the video so code is bound to change over time on the next video.
const express = require('express');
const bodyParser = require('body-parser');
const bcrypt = require ('bcrypt-nodejs');
const cors = require('cors');
const knex = require('knex');
const pg = require('pg');
const db = knex({
client: 'pg',
connection: {
host : '127.0.0.1',
user : 'postgres',
port: '3000',
password : '',
database : 'smart-brain'
}
});
const app = express();
const database = {
users: [
{
id: '123',
name: 'Jess',
email: 'jess#gmail.com',
password: 'cookies',
entries: 0,
joined: new Date()
},
{
id: '124',
name: 'Sally',
email: 'sally#gmail.com',
password: 'bananas',
entries: 0,
joined: new Date()
}
]
}
app.use(bodyParser.json());
//body parser is basically json.parse. we want to always parse json so our code is readable in string form. POST > Raw > JSON
app.use(cors())
app.get('/', (req, res)=> {
res.send(database.users);
})
app.post('/signin', (req, res) => {
if(req.body.email === database.users[0].email && req.body.password === database.users[0].password) {
res.json(database.users[0])
} else {
res.status(400).json('error logging in')
}
})
app.post('/register', (req, res) => {
const { email, name, password } = req.body;
db('users').insert({
email: email,
name: name,
joined: new Date()
}).then(() => console.log())
res.json(database.users[database.users.length-1])
});
app.get('/profile/:id', (req, res) => {
const { id } = req.params;
let found = false;
database.users.forEach(user => {
if (user.id === id) {
found = true;
return res.json(user);
}
})
if (!found) {
res.status(400).json('not found...')
}
})
//now we are creating route for entries count. everytime they submit image, they will get a count for it
app.put('/image', (req, res) => {
const { id } = req.body;
let found = false;
database.users.forEach(user => {
if (user.id === id) {
found = true;
user.entries++
return res.json(user.entries);
}
})
if (!found) {
res.status(400).json('not found...')
}
})
app.listen(3000, ()=> {
console.log('app is running on port 3000');
})

try to add catch function after then block.
app.post('/register', (req, res) => {
const { email, name, password } = req.body;
db('users').insert({
email: email,
name: name,
joined: new Date()
}).then(() => {
res.json(database.users[database.users.length-1])
}).catch((err)=>{console.log(err)})
});

Related

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.

Use Express JS to block unwanted requests from the Client SIde

Consider the Express router :
const express = require("express");
const router = express.Router();
const DUMMY_PLACES = [
{
id: "p1",
title: "Empire State Building",
description: "One of the most famous sky scrapers in the world!",
location: {
lat: 40.7484474,
lng: -73.9871516
},
address: "20 W 34th St, New York, NY 10001",
creator: "u1"
}
];
// # http://localhost:5000/api/places/user/u1
router.get("/user/:uid", (req, res, next) => {
const user_id = req.params.uid;
const place = DUMMY_PLACES.find(p => {
return p.creator === user_id;
});
return res.status(200).json({
place
});
});
module.exports = router;
And the Server :
const express = require("express");
const bodyParser = require("body-parser");
const app = express();
const placesRoutes = require("./routes/places-routes");
app.use("/api/places", placesRoutes);
const PORT = 5000;
app.listen(PORT, () => {
console.log(`Listening on port ${PORT}`);
});
When clients hit the request http://localhost:5000/api/places/user/u1 , they get the dummy object ... however when hitting the request
http://localhost:5000/api/places/user
... it produces an empty object.
How can I return something like NOT ALLOWED instead of the empty object ?
Maybe you could check for the existence of a user_id and send an error response if there isn't one?
router.get('/user/:uid', (req, res, next) => {
const user_id = req.params.uid
if (!user_id) {
return res.status(400).json({
error: 'User ID required'
})
}
const place = DUMMY_PLACES.find((p) => {
return p.creator === user_id
})
return res.status(200).json({
place
})
})
The HTTP status codes are born to handle a lot of situation. In your case, there is a client error: the resource requested has not been found on the server (error 404).
In this case, your API can change in this way:
router.get("/user/:uid", (req, res, next) => {
const user_id = req.params.uid;
const place = DUMMY_PLACES.find(p => {
return p.creator === user_id;
});
if (!place) { // if the place does not exist
return res.status(404).json({
message: 'The requested resource has not been found in the server.'
});
}
return res.status(200).json({
place
});
});

Express returns servers' automatic HTML error page, instead of my res.json error message

I have a REST api on the express server and React app for front-end. I have designed it to send JSON to front-end when faces an error, it sends it and I can use it to print errors on the client side as a modal etc. this is my route file for user/login(I also use JWT and bcrypt for password issues):
router.post("/login", (req, res) => {
const { email, password } = req.body;
//simple validation
if (!email || !password) {
return res.status(400).json({ general: "Email and Password can not be empty" });
}
//check for existing user
User.findOne({ email }).then((err, user) => {
if (!user)
return res.status(400).json({ email: "This user doesn't exist"});
if (err) console.log(err);
//Validate password
bcrypt.compare(password, user.password).then(isMatch => {
if (!isMatch)
return res
.status(400)
.json({ password: "Password and User name are not match!" });
jwt.sign(
{ id: user.id },
config.get("jwtSecret"),
{ expiresIn: 3600 },
(err, token) => {
if (err) throw err;
res.json({
token,
user: {
id: user.id,
name: user.name,
email: user.email,
sort: user.sort
}
});
}
);
});
});
});
and my app.js:
const express = require("express");
const mongoose = require("mongoose");
const bodyParser = require("body-parser");
const config = require("config");
const app = express();
//Bodyparser Middleware
app.use(bodyParser.json());
// DB config
const db = config.get("mongoURI");
//connect to Mongo
mongoose
.connect(db)
.then(() => console.log("MongoDB Connected..."))
.catch(err => console.log(err));
//Use routes
app.use("/api/test", require("./routes/api/test"));
app.use("/api/users", require("./routes/api/users"));
app.use("/api/tickets", require("./routes/api/tickets"));
const port = process.env.PORT || 5000;
app.listen(port, () => console.log(`Server started on port ${port}`));
On Localhost there is no problem. But after uploading to the server, when I left empty spaces or submit false password etc, it never sends my JSON responses, instead, it returns servers Html Page. I console.logged returning error response and it is something like this:
How can I replace the Html response with my own JSON error message?
The default error handler shows the error as an HTML page.
See https://expressjs.com/en/guide/error-handling.html
You can override the default error handler. Try adding this to the end of your app.js.
// catch 404 and forward to error handler
app.use(function (req: express.Request, res: express.Response, next) {
next({ status: 404 });
});
app.use(function (err: any, req: express.Request, res: express.Response, next: express.NextFunction) {
console.error(err);
res.status(err.status || 500).json();
});
i guess you are using axios?
did you try it with:
.then(err => {
err.response.data
})
Your response is actually in err.response

.insertOne is not a function

I want to preface this by saying I have read several posts here regarding this issue.
I have a node/express/mongo app with the following:
app.js:
var express = require('express')
var bodyParser = require('body-parser')
var cors = require('cors')
var morgan = require('morgan')
var mongoose = require('mongoose')
var passport = require('passport')
var app = express()
// MongoDB Setup
var configDB = require('./config/database.js')
mongoose.connect(configDB.url)
app.use(morgan('combined'))
app.use(bodyParser.json())
// Check security with this
app.use(cors())
// load our routes and pass in our app and fully configured passport
require('./routes')(app)
app.listen(process.env.PORT || 8081)
console.log('We are up and running, captain.')
routes.js
const AuthenticationController = require('./controllers/AuthenticationController')
module.exports = (app) => {
app.post('/register', AuthenticationController.register)
}
My mongo schema file Account.js:
const mongoose = require('mongoose')
const bcrypt = require('bcrypt-nodejs')
const Schema = mongoose.Schema
var accountSchema = new Schema({
email: String,
password: String,
likesPerDay: { type: Number, min: 0, max: 250 },
followPerDay: { type: Number, min: 0, max: 250 },
unfollowPerDay: { type: Number, min: 0, max: 250 },
commentsPerDay: { type: Number, min: 0, max: 250 },
comment: String,
hashtags: [String]
})
// methods ======================
// generating a hash. We hash password within user model, before it saves to DB.
accountSchema.methods.generateHash = function (password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null)
}
// checking if password is valid
accountSchema.methods.validPassword = function (password) {
return bcrypt.compareSync(password, this.local.password)
}
// create the model for users and expose it to our app
module.exports = mongoose.model('Account', accountSchema)
And finally my controller file AuthenticationController.js
const Account = require('../models/Account.js')
// var bodyParser = require('body-parser')
module.exports = {
register (req, res) {
Account.findOne({email: req.body.id}, function (err, account) {
if (err) {
console.log('Could not regster user')
throw err
}
if (account) {
console.log('account already exists')
} else {
Account.insertOne({email: req.body.email, password: req.body.password}, function (err, res) {
if (err) {
console.log('could not insert')
throw err
}
console.log('inserted account')
Account.close()
})
}
})
}
}
I am getting an error in my AuthenticationController file when I call Account.insertOne function.
I get the error that
TypeError: Account.insertOne is not a function
Now several of the posts here on stack have advised that I make sure that I am exporting the model from my model class, which I am doing, and that would fix this issue. Its weird because the findOne method seems to be fine, but when I call the insertOne i get an issue.
Am I missing something here?
A Mongoose model doesn't have an insertOne method. Use the create method instead:
Account.create({email: req.body.email, password: req.body.password}, function (err, doc) {
The Mongoose docs show how to create documents:
Either via Account.create():
Account.create({email: req.body.email, password: req.body.password}, function (err, res) {
// ...
})
Or by instantiating and save()ing the account:
new Account({email: req.body.email, password: req.body.password}).save(function (err, res) {
// ...
})
edit
as of mongoose documentation, try using
Account.create({ ...params ... }, function (err, small) {
if (err) return handleError(err);
// saved!
})
insertOne command is not available in mongoose directly as mentioned in Mongoose Documentation. If you want to use insertOne command then you need to use bulk command in order to send this command to MongoDB server. Something like below. I hope this works.
Account.bulkWrite([
{
insertOne: {
document: {email: req.body.email, password: req.body.password}
}
}
}]

Categories