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