Accessing and updating In-Memory data via URL - javascript

I'm currently learning about creating a server using the node.js framework Express.
Using the code below I understand how to access or GET the list of students or GET 1 student by name, however I'm not sure how to DELETE a student by id. I tried the localhost:4000/students/1 and it doesn't work, what should the URL it look like?
Same questions for the app.put method. If I type localhost:4000/students/James/art/50 I simply get an error saying "cannot get localhost:4000/students/James/art/50" so it never actually uses the put method.
const express = require('express');
const bodyParser = require('body-parser');
app.use(bodyParser.json());
let students = [
{
id: 1,
name: 'James',
classes: {
computer: 95,
art: 92
}
},
{
id: 2,
name: 'Leeroy',
classes: {
computer: 95,
art: 92
}
}
];
app.get('/students', (req, res) => {
res.json(students);
});
app.get('/students/:name', (req, res) => {
res.json(students.find((student) => student.name === req.params.name));
});
app.put('/students/:name/:class/:grade', (req, res) => {
const student = students.find((student) => student.name === req.params.name);
if (student) {
student.classes[req.params.class] = parseInt(req.params.grade);
res.status(201).send(`Student ${req.params.name} was assigned a grade of ${req.params.grade} in ${req.params.class}`);
} else {
res.status(404).send('Student not foun');
}
});
app.delete('/students/:id', (req, res) => {
const student = students.find((student) => student.id === req.params.id);
if (student) {
students = students.filter((obj) => obj.id !== req.params.id);
res.status(201).send('Student was deleted.');
}
});
app.listen(4000, () => {
console.log('Your app is listening on port 4000');
});

You'll need to use a tool in order to test requests different to GET, I recommend Postman there you'll be able to test the DELETE, PUT and POST requests

Related

Setting cookies in Nodejs

I'm working on a small petition project, I would like to set a cookie when a user signs it, so if he tries to access the page again, it should redirect him already to a "thanks page". If the user didn't, then he can proceed to sign it.
I'm getting the Cannot set headers after they are sent to the client error, maybe someone understands what I'm doing wrong. Down below the js code.
const express = require("express");
const { getSignatures, addSignature } = require("./db.js");
const app = express();
const cookieParser = require("cookie-parser");
app.use(express.urlencoded({ extended: false }));
app.use(express.static("./public"));
const hb = require("express-handlebars");
app.engine("handlebars", hb());
app.set("view engine", "handlebars");
app.use(cookieParser());
app.get("/", (req, res) => {
res.redirect("/petition");
});
app.get("/petition", (req, res) => {
let hasSigned = req.cookies.petition_signed;
if (!hasSigned) {
res.render("petition", {
layout: "main",
});
} else {
res.redirect("/thanks");
}
});
app.post("/petition", (req, res) => {
const { firstName, lastName, signature } = req.body;
//console.log("req.body: ", req.body);
if (firstName === "" || lastName === "" || signature === "") {
res.render("petitionerror", {
error: "Please fill out all the elements before submitting",
});
} else {
addSignature(firstName, lastName, signature).then((data) => {
res.cookie("petition_signed", "yes");
console.log(data);
});
res.redirect("/thanks");
}
});
app.get("/thanks", (req, res) => {
res.render("thankyou", {
layout: "main",
});
});
app.get("/signers", (req, res) => {
getSignatures().then((data) => {
res.render("signers", { success: true, rows: data.rows });
});
});
app.listen(8080, () => console.log("listening on 8080"));
The way you have it structured, it is trying to do res.cookie after res.redirect because addSignature is an asynchronous function.
addSignature(firstName, lastName, signature).then((data) => {
res.cookie("petition_signed", "yes");
console.log(data);
res.redirect("/thanks");
});
You need to ensure res.cookie is called prior to redirect (which returns headers to the client)

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.

API get call returns status 400

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);
`

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
});
});

Connection terminated when I do POST request?

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)})
});

Categories