Sending Express back-end data from a database to React front end - javascript

I'm trying to show some information about a user in front-end, but I don't know why is not showing anything. If I access the server using localhost:8080/api/user I can see the information in an empty page (is just a dummy database that I had), but I can't print them in my content page, my code recognizes the list as being empty.
I'm a beginner and I just started using React, Node.js, and Express.
UserStatus.js
import React from 'react';
class UserStatus extends React.Component {
constructor(props){
super(props);
this.state = {
list: []
}
}
// Fetch the list on first mount
componentDidMount() {
}
// Retrieves the list of items from the Express app
getList = () => {
fetch('/api/user')
.then(res => res.json())
.then(list => this.setState({ list })
)
}
render() {
const { list } = this.state;
return (
<div className="App">
<h1>User Status</h1>
{/* Check to see if any items are found*/}
{list.length ? (
<div>
{/* Render the list of items */}
{list.map((item) => {
return(
<div>
{item}
</div>
);
})}
</div>
) : (
<div>
<h2>No List Users Found</h2>
</div>
)
}
</div>
);
}
}
export default UserStatus
server.js
//Initiallising node modules
var express = require("express");
var bodyParser = require("body-parser");
var sql = require("mssql");
var app = express();
// Body Parser Middleware
app.use(bodyParser.json());
//CORS Middleware
app.use(function (req, res, next) {
//Enabling CORS
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, contentType,Content-Type, Accept, Authorization");
next();
});
//Setting up server
var server = app.listen(process.env.PORT || 8080, function () {
var port = server.address().port;
console.log("App now running on port", port);
});
//Initiallising connection string
var dbConfig = {
user: '---',
password: '---',
server: '----',
database: '---'
};
//Function to connect to database and execute query
var executeQuery = function (query, res) {
sql.connect(dbConfig, function (err) {
if (err) {
console.log("Error while connecting database :- " + err);
res.send(err);
} else {
// create Request object
var request = new sql.Request();
// query to the database
request.query(query, function (err, ress) {
if (err) {
console.log("Error while querying database :- " + err);
res.send(err);
} else {
res.json(ress);
}
});
}
});
}
//GET API
app.get("/api/user", function (req, res) {
var query = "select * from Machine";
executeQuery(query, res);
});
//POST API
app.post("/api/user", function (req, res) {
var query = "INSERT INTO [user] (Name,Email,Password) VALUES (req.body.Name,req.body.Email,req.body.Password)";
executeQuery(res, query);
});
//PUT API
app.put("/api/user/:id", function (req, res) {
var query = "UPDATE [user] SET Name= " + req.body.Name + " , Email= " + req.body.Email + " WHERE Id= " + req.params.id;
executeQuery(res, query);
});
// DELETE API
app.delete("/api/user /:id", function (req, res) {
var query = "DELETE FROM [user] WHERE Id=" + req.params.id;
executeQuery(res, query);
});

To debug you can set breakpoints or use console.log or some other console method. First you can check your fetch response:
getList = () => {
fetch('/api/user')
.then(res => {
console.log(res) // is it a string or is it already json? What is the data structure?
return res.json()
})
.then(list => this.setState({ list }))
.catch(err => console.error(err)) // you should handle errors
}
Also you should catch errors. It seems to me you are returning a dataset with a toplevel 'recordset' attribute. So you probably have to do: return res.json().recordset

First of all, list.length is just a number you need to check if the array is greater than zero
list.length > 0
Secondly, Check to see if the data is being received from the backend
getList = async () => {
try{
let res = await fetch('/api/user');
console.log(res);
} catch (err) {
console.log(err);
}
}

Related

Data retreived by ID from NodeJS server showing NULL in react

I am using axios to make api calls using a proxy server with nodeJS as backend and reactJS as frontend.
node js file
const express = require('express');
const axios = require('axios').default;
const app = express();
require('dotenv').config();
const PORT = process.env.PORT || 8000;
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.get('/', (req, res) => {
res.redirect('/api')
})
// get anime on front page
app.get(`/api`, async (req, res) => {
try {
const response = await axios.get('https://api.jikan.moe/v4/anime');
const data = response.data
res.json(data.data)
}
catch (err) {
console.log(err.message)
}
})
// get anime by id
app.get('/api/anime/:id' ,async (req,res)=>{
try {
const id = req.params.id
const response = await axios.get(`https://api.jikan.moe/v4/anime/${id}`)
const data = response.data
res.json(data);
console.log(data)
}
catch (err) {
console.log(err)
}
})
app.listen(PORT, () => {
console.log('connected')
})
Here I am using Two routes, one for the main homepage to show all the data, and then one route to retrieve data by ID, but when i try to retrieve the data using ID it shows null in console.
import useFetch from "./usefetch";
import { useParams } from "react-router-dom";
import './style/animeDetails.css'
const AnimeDetails = () => {
const play = (e) => {
console.log(e.target)
}
const {id} = useParams()
const { data: animeDet, isLoading, error } = useFetch('/api/anime/' + id);
console.log(animeDet.data)
return (
<div className="details">
</div>
);
}
export default AnimeDetails;
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Here i am making a call to the proxy server to retrieve the data by ID.
try to check isLoading first, like:
const { data: animeDet, isLoading, error } = useFetch('/api/anime/' + id);
if (isLoading) {
return null
}
console.log(animeDet.data)
return (
<div className="details">
</div>
);

Stringify causing SyntaxError: Unexpected end of JSON input React Express

I am building a React app that logs user input and saves it to a database. I have created a REST API using node.js Express to connect the app to the database.
I have succesfuly made a post request using Postman but cannot get it working with the react app because I recieve SyntaxError: Unexpected end of JSON input error.
I've tried solutions described on similar Stack Overflow posts but nothing has worked for me. As far as I can tell, I have formatted my json input correctly. One post indicated it could be to do with my Express API not returning correctly formatted JSON but this cannot be the case because postman receives JSON output after successful post.
Postman output
console output
stack trace
React App code
import logo from './logo.svg';
import './App.css';
import Submit from './Submit';
import React from 'react';
import ReactDOM from 'react-dom';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
pic: "",
alive: "",
typeOfDeath: "",
comment: "",
geoLocation: ""
};
}
// code credit - https://github.com/lyhd/reactjs/blob/base64_encode/src/index.js
encodeFileBase64 = (file) => {
var reader = new FileReader();
if (file) {
reader.readAsDataURL(file);
reader.onload = () => {
var Base64 = reader.result;
console.log(Base64);
//setFileBase64String(Base64);
};
reader.onerror = (error) => {
console.log("error: ", error);
};
}
};
handleSubmit = async (evt) => {
evt.preventDefault();
const input = document.querySelector('input[type=file]');
const pic = input.files[0];
const pic_base64 = this.encodeFileBase64(pic);
const rbs = document.querySelectorAll('input[name="mortality"]');
let alive = false;
//code belongs to javascripttutorial.net/javascript-dom/javascript-radio-button/
for (const rb of rbs) {
if (rb.checked) {
alive = rb.value;
break;
}
}
const typeOfDeathDropDown = document.querySelector('#typeOfDeath');
const typeOfDeath = typeOfDeathDropDown.options[typeOfDeathDropDown.selectedIndex].value;
const comment = document.querySelector('#comment').value.trim();
const geoLocation = "placeholder";
//pleaceholder validation if statement, replace at a later date
if (1 > 0) {
console.log(alive,typeOfDeath,comment,geoLocation);
this.setState({
pic: pic_base64,
alive: alive,
typeOfDeath: typeOfDeath,
comment: comment,
geoLocation: geoLocation
});
const url = 'https://zapp.ogs17.brighton.domains/';
let jsonBody = JSON.stringify({
pic: pic_base64,
alive: alive,
typeOfDeath: typeOfDeath,
comment: comment,
geoLocation: geoLocation
});
console.log(jsonBody);
try {
const response = await fetch(url, {
method: 'POST',
headers: {'Content-Type':'application/x-www-form-urlencoded'},
body: jsonBody
});
await console.log(response);
const jsonData = await response.json();
this.setState({
loading: false,
records: jsonData.records
});
} catch (err) {
console.log(err);
this.setState({
loading: false,
records: []
});
}
}
}
render = () => {
return (
<div>
<h1>Zapp App - Pangolin Sightings</h1>
<form onSubmit={this.handleSubmit}>
<input type="file" accept="image/*" id="pic" />
<input id="alive" type="radio" name="mortality" />
<label htmlFor="alive">Alive</label>
<input id="deceased" type="radio" name="mortality" />
<label htmlFor="deceased">Deceased</label>
<br />
<label htmlFor="typeOfDeath">Type of Death:</label>
<select name="typeOfDeath" id="typeOfDeath">
<option value="fence_electrocution">Fence death: electrocution;</option>
<option value="fence_normal">Fence death: caught on non-electrified fence</option>
<option value="road">Road death</option>
<option value="other">Other</option>
</select>
<br />
<textarea name="comment" id="comment" defaultValue="comment"></textarea>
<br />
<button type="submit">Submit</button>
</form>
<Submit state={this.state} />
</div>
);
}
}
export default App;
Node.js Express Api code
const express = require('express');
const bodyParser = require('body-parser');
const db = require('./db');
const cors = require('cors');
const app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cors());
const port = 3000;
//https://stackoverflow.com/questions/18310394/no-access-control-allow-origin-node-apache-port-issue
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});
async function getSighting(req) {
let status = 500, data = null;
try {
const oid = req.query.oid;
if (oid
&& oid.length > 0 && oid.length <= 32
&& oid.match(/^[a-z0-9]+$/i)) {
const sql = 'SELECT * FROM tSightings WHERE oid=?';
const rows = await db.query(sql, [oid]);
if (rows) {
status = 200;
data = {
'oid': oid,
'data': rows
};
} else {
status = 204;
}
} else {
status = 400;
}
} catch (e) {
console.error(e);
}
return { status, data };
}
async function postSighting(req) {
console.log("postSighting method entered")
let status = 500, data = null;
try {
const pic = req.body.pic;
const alive = req.body.alive;
const typeOfDeath = req.body.typeOfDeath;
const comment = req.body.comment;
const geoLocation = req.body.geoLocation;
//impliment appropriate checks here
if (1 == 1) {
const sql = 'INSERT INTO tSightings (pic, alive, typeOfDeath, comment, geoLocation) '
+ 'VALUES (?, ?, ?, ?, ?)';
const result = await db.query(sql, [pic, alive, typeOfDeath, comment, geoLocation]);
if (result.affectedRows) {
status = 201;
data = { 'id': result.insertId };
}
} else {
status = 400;
}
} catch (e) {
console.error(e);
}
return { status, data };
}
app.get('/', async (req, res) => {
console.log("express get submitted")
const { status, data } = await getSighting(req);
res.status(status);
if (data) res.json(data);
else res.end();
})
app.listen(port, () => {
console.log(`Running at http://localhost:${port}`)
})
app.get('/express_api', async (req, res) => {
console.log("express get submitted")
const { status, data } = await getData(req);
res.status(status);
if (data) res.json(data);
else res.end();
})
app.post('/', async (req, res) => {
const { status, data } = await postSighting(req);
res.status(status);
if (data) res.json(data);
else res.end();
})
app.put('/express_api', async (req, res) => {
res.status(405);
res.end();
})
app.delete('/express_api', async (req, res) => {
res.status(405);
res.end();
})
Front-end
A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side.
your're sending header content type = form-urlencoded and actual body as json
try {
const response = await fetch(url, {
method: 'POST',
//should be application json if your're stringifying it
headers: {'Content-Type':' 'application/json'},
body: jsonBody
});
await console.log(response);
// need to check if the response is valid or not
if(response.ok){
const jsonData = await response.json();
}else{
throw response
}
this.setState({
loading: false,
records: jsonData.records
});
}
Back-end
I don't know why your're setting custom headers while have use cors app.use(cors()); that allows all methods.
remove that
//was missing
app.use(bodyParser.json());
const corsOptions = {
origin: "*" // grant for all origins. can be use as per your ref url
}
app.use(cors(corsOptions));
// don't need this part
app.use(function (req, res, next) {
// Website you wish to allow to connect
res.setHeader('Access-Control-Allow-Origin', '*');
// Request methods you wish to allow
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE');
// Request headers you wish to allow
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
// Set to true if you need the website to include cookies in the requests sent
// to the API (e.g. in case you use sessions)
res.setHeader('Access-Control-Allow-Credentials', true);
// Pass to next layer of middleware
next();
});

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

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