I'm in a web programming class and we are writing web server API's. I've currently implemented POST and GET, but I'm having some trouble implementing DELETE. What am I doing wrong here? I would appreciate a thorough explanation of what I'm doing wrong because I'm really trying to learn the material(unless of course it is simply formatting errors).
There are lots of portions that have not been implemented yet, so I'm sure there are many errors. Mostly I'm concerned with DELETE, and anything else apparent from my code that I don't understand.
I've replaced my domain name with fakeDomainName.com
index.html and script.js are in a public folder. start.js in my main directory.
We are creating a simple voting type api to practice, using a Node.js server and MongoDB database to store the information. Currently, POST and GET are working as expected, but DELETE is giving me these errors:
spread.js:25 DELETE http://fakeDomainName.com:3010/api/candidates/undefined 500 (Internal Server Error)
(anonymous) # spread.js:25
e.exports # spread.js:25
e.exports # spread.js:25
Promise.then (async)
r.request # spread.js:25
r.<computed> # spread.js:25
(anonymous) # axios.min.js:477
deleteItem # script.js:65
invokeWithErrorHandling # vue.js:1855
invoker # vue.js:2173
original._wrapper # vue.js:7416
spread.js:25 Uncaught (in promise) Error: Request failed with status code 500
at e.exports (spread.js:25)
at e.exports (spread.js:25)
at XMLHttpRequest.d.onreadystatechange (spread.js:25)
e.exports # spread.js:25
e.exports # spread.js:25
d.onreadystatechange # spread.js:25
XMLHttpRequest.send (async)
(anonymous) # spread.js:25
e.exports # spread.js:25
e.exports # spread.js:25
Promise.then (async)
r.request # spread.js:25
r.<computed> # spread.js:25
(anonymous) # axios.min.js:477
deleteItem # script.js:65
invokeWithErrorHandling # vue.js:1855
invoker # vue.js:2173
original._wrapper # vue.js:7416
Here is my code in "start.js", which is what I'm using to start the server.
const express = require('express');
const bodyParser = require("body-parser");
const multer = require('multer');
const upload = multer({
dest: './public/images/',
limits: {
fileSize: 10000000
}
});
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(express.static('public'));
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/voting', {
useNewUrlParser: true
});
var candidateSchema = new mongoose.Schema({
name: String,
bio: String,
numVotes: String,
});
var Candidate = mongoose.model('Candidate', candidateSchema);
//add a candidate to the list
app.post('/api/candidates', async(req, res) => {
console.log("initiated post request");
const candidate = new Candidate({
name: req.body.name,
bio: req.body.bio,
numVotes: req.body.numVotes,
});
this.addItem = candidate.data;
try {
await candidate.save();
}
catch (error) {
console.log(error);
res.sendStatus(500);
}
});
// Get a list of all of the candidates.
app.get('/api/candidates', async(req, res) => {
console.log("initiated get request");
try {
let candidate = await Candidate.find();
res.send(candidate);
}
catch (error) {
console.log(error);
res.sendStatus(500);
}
});
//delete a candidate from the list
app.delete('/api/candidates/:id', async(req, res) => {
console.log("initiated delete request");
Candidate.deleteOne({ _id: req.params.id }, function(err) {
if (err) res.sendStatus(500);
else {
console.log(req.params.id, "deleted successfully");
res.sendStatus(200);
}
});
});
//edit a candidate
app.put('/api/candidates/:id', async(req, res) => {
console.log("initiated put(edit) request");
try {
let candidate = await Candidate.findOne({ _id: req.params.id });
candidate.name = req.body.name;
candidate.bio = req.body.bio;
candidate.numVotes = req.body.numVotes;
candidate.save();
res.sendStatus(200);
}
catch (error) {
console.log(error);
res.sendStatus(500);
}
});
app.listen(3010, () => console.log('Server listening on port 3010!'));
and here is my code in script.js, which is linked to my index.html page:
var app = new Vue({
el: '#app',
data: {
name: "",
bio: "",
numVotes: "",
file: null,
addItem: null,
items: [],
findName: "",
findItem: null,
},
created() {
this.getItems();
},
computed: {
suggestions() {
return this.items.filter(item => item.title.toLowerCase().startsWith(this.findTitle.toLowerCase()));
}
},
methods: {
async postItem(item) {
console.log("initiated");
try {
let response = await axios.post('/api/candidates', {
name: this.name,
bio: this.bio,
numVotes: this.numVotes,
});
this.addItem = response.data;
}
catch (error) {
console.log(error);
}
},
async getItems() {
try {
let response = await axios.get("/api/candidates");
this.items = response.data;
return true;
}
catch (error) {
console.log(error);
}
},
selectItem(item) {
this.findName = "";
this.findItem = item;
},
async deleteItem(item) {
try {
let response = axios.delete("/api/candidates/" + item._id);
this.findItem = null;
this.getItems();
return true;
} catch (error) {
console.log(error);
}
},
async editItem(item) {
try {
let response = await axios.put("/api/candidates/" + item._id, {
name: this.findItem.name,
bio: this.findItem.bio,
numVotes: this.findItem.numVotes,
});
this.findItem = null;
this.getItems();
return true;
} catch (error) {
console.log(error);
}
},
},
});
and finally, here is the code I'm using in index.html:
<!DOCTYPE HTML>
<html>
<head>
<title></title>
</head>
<body>
<h2>Hello World</h2>
<div id="app">
<div>
<div>
<input v-model="name" placeholder="Name">
<p></p>
<input v-model="bio" placeholder="Bio">
<p></p>
<input v-model="numVotes" placeholder="Number of votes">
<button #click="postItem">Upload</button>
<button #click="deleteItem">Delete</button>
</div>
<div v-if="addItem">
<h2>{{addItem.name}}</h2>
<h2>{{addItem.bio}}</h2>
<h2>{{addItem.NumVotes}}</h2>
</div>
</div>
<h2>Candidates:</h2>
<div v-for="item in items">
<div #click="selectItem">
<h2>Name: {{item.name}}</h2>
<h2>Bio: {{item.bio}}</h2>
<h2>Number of Votes: {{item.numVotes}}</h2>
</div>
</div>
</div>
<!--Vue and axios-->
<script src="https://cdn.jsdelivr.net/npm/vue#2.6.2/dist/vue.js"></script>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script src="/script.js"></script>
</body>
</html>
As you can see in url you are not getting params id its showing as undefined
//fakeDomainName.com:3010/api/candidates/undefined
due to which it is giving error if you will pass with as id it will work as well
//delete a candidate from the list
app.delete('/api/candidates/:id', async(req, res) => {
console.log("initiated delete request");
Candidate.findOneAndDelete({ _id: req.params.id }, function(err) {
if (err) res.sendStatus(500);
else {
console.log(req.params.id, "deleted successfully");
res.sendStatus(200);
}
});
});```
I tested your node side with postman. "Delete" is fine. But as you see in the error itself
DELETE http://fakeDomainName.com:3010/api/candidates/undefined the parameter item id is being sent undefined, hence cannot delete.
In you POST request (on server/node side), you can perhaps add a sendStatus(200) to avoid post request being hung up.Also, You can get id of the saved item while saving the Candidate
Like this:
app.post('/api/candidates', async(req, res) => {
console.log("initiated post request");
const candidate = new Candidate({
name: req.body.name,
bio: req.body.bio,
numVotes: req.body.numVotes,
});
this.addItem = candidate.data;
try {
console.log("Await save...");
let data = await candidate.save();
console.log("Done");
res.statusCode = 200;
res.setHeader('content-type','application/json');
res.end(JSON.stringify(data));
}
catch (error) {
console.log(error);
res.sendStatus(500);
}
});
The response would be like :
{
"_id": "5dd38cb16f47912b40a1deef",
"name": "Ken Adams",
"bio": "bio",
"numVotes": "10",
"__v": 0
}
Related
I am using axios for the request of my own api, everything works correctly except the DELETE request, I looked for information about the error but I have not found the solution. when I send the request to the server I get this error: "xhr.js:210 DELETE http://localhost:3000/posts/62575cb61cb27c6417732193 403 (Forbidden)".
I put this line of code in Package.Json to avoid problems with CORS:
"proxy": "http://localhost:8080/api"
This would be my api, for the operation I pass the post id by url and the user id:
(I tried it in postman and it works without any problem)
router.delete("/:id", async (req, res) => {
try {
const post = await Post.findById(req.params.id);
if (post.userId === req.body.userId) {
await post.deleteOne();
res.status(200).json("the post has been deleted");
} else {
res.status(403).json("you can delete only your post");
}
} catch (err) {
res.status(500).json(err);
}
});
and this is where I consume my api:
const Post = ({ date, _id,userId, description }) => {
const handleDelete = async () => {
try {
await axios.delete('posts/' + _id, { userId: currentUser._id })
} catch (error) {
console.log(error)
}
}
return(
<div onClick={handleDelete }>
//component content
</div>
)
}
export default Post
I solved it, sending a data object to my api (inside that object I put the user id):
const handleDelete = async () => {
try {
await axios.delete('/posts/' + _id, {
data: {
userId: currentUser._id
}
}
)
} catch (error) {
console.log(error)
}
}
I'm building a todo list using express handlebars, mongoose, mongodb, google oauth. I'm having trouble with rendering using handlebars. A todo has a mongoose attribute of done. If done true, then a class of complete is applied, which is text-decoration: line-through. The problem is that done is always rendered as true. When I click on the todo, it toggles between true/false in mongodb but doesn't show in hbs.
hbs:
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="css/style.css" />
<title>To Do</title>
</head>
<body>
<div class="container">
<header class="flexContainer">
<h1 class="title main-font center">Welcome {{name}}</h1>
</header>
<form class="center" action="/todos/addTodo" method="POST">
<input type="text" placeholder="Add a To Do" name="todoItem" />
<button type="submit" class="submitButton">
<span>Add Todo</span>
</button>
</form>
<div class="to-do-list flexContainer">
<ul class="task-list center">
{{#each todo}}
<li data-id="{{_id}}">
// Problem is this block of code here.
{{done}} // Added this line just to show the done attribute is toggling between true/false. Status renders here correctly.
{{#if done}} // This if statement doesn't work. All todos are rendered with the complete class.
<span class="complete">{{todo}}</span>
{{else}}
<span class="incomplete">{{todo}}</span>
{{/if}}
<span class="fa fa-trash delete-todo"></span>
</li>
{{/each}}
</ul>
</div>
<h4 class="main-font center">Left to do: {{left}}</h4>
</div>
<script type="text/javascript" src="js/main.js"></script>
</body>
</html>
controller todo.js
const Todo = require("../models/todos");
module.exports = {
getTodos: async (req, res) => {
try {
const todoItems = await Todo.find({ googleId: req.user.googleId }).lean();
const itemsLeft = await Todo.countDocuments({
googleId: req.user.googleId,
done: false,
});
res.render("todos.hbs", {
todo: todoItems,
left: itemsLeft,
name: req.user.firstName,
// done: done, <-- I'm not sure if I have to pass in a variable for done. The variable done seems to work in the hbs file anyways.
});
} catch (err) {
console.error(err);
}
},
addTodo: async (req, res) => {
console.log(req.body);
try {
await Todo.create({
todo: req.body.todoItem,
done: false,
googleId: req.user.googleId,
});
console.log("To do has been added!");
res.redirect("/");
} catch (err) {
console.error(err);
}
},
deleteTodo: async (req, res) => {
try {
await Todo.findOneAndDelete({ _id: req.body.todoId });
console.log("Deleted Todo");
res.json("Deleted Todo");
} catch (err) {
console.log(err);
}
},
markComplete: async (req, res) => {
console.log("markComplete");
try {
await Todo.findOneAndUpdate(
{ _id: req.body.todoId },
{
done: true,
}
);
console.log("complete");
res.json("Marked Complete");
} catch {
console.log(err);
}
},
markIncomplete: async (req, res) => {
try {
await Todo.findOneAndUpdate(
{ _id: req.body.todoId },
{
done: false,
}
);
console.log("Marked Incomplete");
res.json("Marked Incomplete");
} catch {
console.log(err);
}
},
};
main.js:
const deleteBtn = document.querySelectorAll(".delete-todo");
const todoIncomplete = document.querySelectorAll(".incomplete");
const todoComplete = document.querySelectorAll(".complete");
Array.from(deleteBtn).forEach((e) => {
e.addEventListener("click", deleteToDo);
});
Array.from(todoIncomplete).forEach((e) => {
e.addEventListener("click", markComplete);
});
Array.from(todoComplete).forEach((e) => {
e.addEventListener("click", markIncomplete);
});
async function deleteToDo() {
const todoId = this.parentNode.dataset.id;
try {
const response = await fetch("todos/deleteTodo", {
method: "delete",
headers: { "Content-type": "application/json" },
body: JSON.stringify({
todoId: todoId,
}),
});
const data = await response.json();
console.log(data);
location.reload();
} catch (err) {
console.log(err);
}
}
async function markComplete() {
const todoId = this.parentNode.dataset.id;
console.log(todoId);
try {
const response = await fetch("todos/markComplete", {
method: "put",
headers: { "Content-type": "application/json" },
body: JSON.stringify({
todoId: todoId,
}),
});
const data = await response.json();
console.log(data);
location.reload();
} catch (err) {
console.log(err);
}
}
async function markIncomplete() {
const todoId = this.parentNode.dataset.id;
try {
const response = await fetch("todos/markIncomplete", {
method: "put",
headers: { "Content-type": "application/json" },
body: JSON.stringify({
todoId: todoId,
}),
});
const data = await response.json();
console.log(data);
location.reload();
} catch (err) {
console.log(err);
}
}
Routes:
const express = require("express");
const router = express.Router();
const todosController = require("../controllers/todos");
const { ensureAuth, EnsureGuest } = require("../middleware/auth");
router.get("/", ensureAuth, todosController.getTodos);
router.post("/addTodo", todosController.addTodo);
router.put("/markComplete", todosController.markComplete);
router.put("/markIncomplete", todosController.markIncomplete);
router.delete("/deleteToDo", todosController.deleteTodo);
module.exports = router;
According to #76484, it is a string and not boolean. I have fixed it.
Mongoose model schema:
const toDoSchema = new mongoose.Schema({
todo: {
type: String,
required: true,
},
done: {
type: Boolean, // previously string here
required: true,
},
googleId: {
type: String,
required: true,
},
});
I need to pass author's email in my posts. I though I can do it by joining tables in my posts route, but it doesn't really work.
Here is my route :
router.get("/posts", async (req, res) => {
const { id } = req.session.user;
//const usersPosts = await user.$relatedQuery("posts");
try {
const user = await User.query().findById(id);
if (!user) {
return res.status(401).send("User was not found");
}
const posts = await Post.query()
.select([
"users.email",
"images.name",
"posts.category",
"posts.title",
"posts.description",
"posts.created_at"
])
.join("images", { "posts.image_id": "images.id" });
.join("users", { "posts.user_email": "users.email" });
console.log("it worked");
return res.status(200).send({ posts: posts });
} catch (error) {
console.log(error);
return res.status(404).send({ response: "No posts found" });
}
});
Here is code with my axios fetching the route :
function Home(props) {
const [posts, setPosts] = useState([]);
const getPosts = async () => {
try {
let response = await axios.get("http://localhost:9090/posts", {
withCredentials: true
});
console.log(response.data.posts);
setPosts(response.data.posts);
} catch (error) {
console.log(error.data);
}
};
useEffect(() => {
getPosts();
}, []);
And this is how I tried to return it:
{posts.map((post, index) => {
return (
<>
Author:<br></br>
<small>{post.user_email}</small>
</p>
<p>
Category:<br></br>
<small>{post.category}</small>
</p>
<p>
Description:<br></br>
<small>{post.description}</small>
</p>
<p>
Created: <br></br>
<small>{post.created_at}</small>
Everything works except the fetching Author.
a typo its user_email not users_email
your sending email in the value assingned to user_email and in front end using users_email
I have two directories where vue, node exist. And I have the vue build file in the node folder.
I am currently processing requests from nodes in the vue. However, the event occurs but the data does not cross.
I have the following code, I sent form data via create, but the return data is empty. Also, in mongodb, title and content are require: true, so I get an error like the title.
Please help me.
node/routes/api
...
const Post = require('../db/post');
router.post('/new', (req, res) => {
const post = new Post({
title: req.body.title,
content: req.body.content
});
post.save((err) => {
if (err) {
console.error(err);
res.json({ result: 0 });
return;
}
res.json({ result: 1 });
});
});
...
vue/src/component/new
<template>
<div id="form-group">
name : <input v-model="post.title">
content : <input v-model="post.content">
<button v-on:click="new" >New</button>
</div>
</template>
<script>
export default {
data: function () {
return {
post: {}
}
},
methods: {
new: function (evt) {
this.$http.post('/api/post/new', {
post: this.post
})
.then((response) => {
if (response.data.result === 0) {
alert('Error')
}
if (response.data.result === 1) {
alert('Success')
this.$router.push('/')
}
})
.catch(function (error) {
alert('error')
})
}
}
}
</script>
I am trying to do a PUT request with NodeJS, Express and MongoDB. The issue that I am currently having is that I keep receiving an error **400** and I am not sure exactly why.
What I am exactly trying to do is upload edit a field in my USER collection, after a certain user has been registered. This is supposed to happen on a specific /user/edit/:id route.
My application is structured with a standard MVC pattern.
Here is how my Mongo Schema is structured:
let UserSchema = new mongoose.Schema({
username: String,
password: String,
email: String,
avatar: String,
firstName: String,
lastName: String,
laps:[{ type: Schema.Types.ObjectId, ref: 'Stats' }]
});
This is my service:
exports.updateUser = async function(user) {
let id = user.id;
let oldUser;
try {
//Find the old User Object by the Id
oldUser = await User.findById(id);
} catch(e) {
throw Error("Error occured while Finding the User");
}
// If no old User Object exists return false
if (!oldUser) {
return false;
}
//Edit the User Object
oldUser.firstName = user.firstName || oldUser.firstName;
oldUser.lastName = user.lastName || oldUser.lastName;
oldUser.avatar = user.avatar || oldUser.avatar;
try {
let savedUser = await oldUser.save();
return savedUser;
} catch(e) {
throw Error("And Error occured while updating the User");
}
};
The Controller that I am using:
exports.updateUser = async function(req, res, next) {
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
let id = req.body._id;
let user = {
id,
firstName: req.body.firstName || null,
lastName: req.body.lastName || null,
avatar: req.body.avatar || null
};
try {
let updatedUser = await UserService.updateUser(user);
return res.status(200).json({status: 200, data: updatedUser, message: "Successfully Updated User"})
} catch(e) {
return res.status(400).json({status: 400, message: e.message})
}
};
Route path in router file:
router.post('/edit/:id', UserController.updateUser);
Route path for users inside server file:
app.use('/user', require('./api/routes/user.route'));
I know that most 4** errors come from the front end of the application, so I will also post my form and the constructor behind it. I am using ReactJS as a framework.
Front end Form:
class UserProfile extends Component {
constructor(props) {
super(props);
this.state = {
avatar: '',
resultsSubmitted: false
};
this.formChange = this.formChange.bind(this);
this.resultsSubmit = this.resultsSubmit.bind(this);
}
formChange(e) {
console.log("form changed" + e.target);
const { name, value } = e.target;
this.setState({ [name]: value });
}
resultsSubmit(e) {
e.preventDefault();
const accessToken = JSON.parse(localStorage.getItem('auth_user')).data.access_token;
const { avatar } = this.state;
const { dispatch } = this.props;
if (avatar) {
console.log("submitting results: " + avatar);
dispatch(userActions.addAvatar(avatar, accessToken));
}
}
render(){
const { avatar, resultsSubmitted} = this.state;
return (
<div className="container-fluid no-gutters page-login">
<div className="row">
<div className="login-wrapper">
<h2> Edit User Profile </h2>
<form onSubmit={this.resultsSubmit}>
<div className="form-group">
Paste Avatar URL: <input type="text" value={avatar} name="avatar" id="" onChange={this.formChange} />
</div>
<input type="submit" className="btn btn-primary btn-lg btn-block" value="submit"/>
</form>
</div>
</div>
</div>
)
}
}
function mapStateToProps(state) {
const { layout } = state;
return {
layout
};
}
export default connect(mapStateToProps)(UserProfile);
My dispatch:
function addAvatar(avatar, token) {
return dispatch => {
dispatch(request());
userService.addAvatar(avatar, token)
.then(
user => {
dispatch(success(user));
history.push(`${process.env.PUBLIC_URL}/`);
},
error => {
dispatch(failure(error));
dispatch(alertActions.error(error));
}
);
};
function request() { return { type: userConstants.AVATAR_REQUEST } }
function success(user) { return { type: userConstants.AVATAR_SUCCESS, user } }
function failure(error) { return { type: userConstants.AVATAR_FAILURE, error } }
}
HTTP Post service:
function addAvatar(avatar){
const requestOptions = {
method: 'POST',
headers: authHeader(),
body: avatar
};
return fetch('http://localhost:3003/user/edit/:id', requestOptions)
.then(response => {
if (!response.ok) {
console.log("+",response,"+");
return Promise.reject(response.statusText);
}else{
console.log(response, "the user service response was gooooooooooood");
}
return response.json();
})
.then(data => console.log(data,"WHT DO WE HAVE HERE?"));
}
Apologies for the huge code wall but I wanted to include all the bits.
I am getting an error 400 (Bad Request) on the route POST
http://localhost:3003/user/edit/:id
In your fetch request you are sending only avatar as a body and on your updateUser function you have the following if statement:
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
so obviously you have not _id on your body request but an avatar instead, in fact you're sending your id as a param
'http://localhost:3003/user/edit/:id'
So you could change this line as a workaround
if (!req.params.id){
Hope it helps.
The below snippet shows that you are trying to get the ID parameter from the body of the request.
if (!req.body._id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
Whereas, the route /user/edit/:id , shows that the ID parameter is actually passed through the URL, and to access it, all you need is to get your ID from the URL using req.params.id. req.params contains all parameters that are passed through the route or URL path.
The above snippet should be corrected to;
if (!req.params.id){
return res.status(400).json({status: 400, message: "Id must be present"})
}
Check https://expressjs.com/en/guide/routing.html#route-parameters for proper guide on how to deal with route parameter.