0

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

Postman output

console output

console output

stack trace

console 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();
})
NewForOlly
  • 35
  • 5

1 Answers1

1

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


abhi patil
  • 504
  • 2
  • 11
  • Thank you for your response. I've followed your advice and am no longer getting the syntax error message but I am still getting 500. POST https://zapp.ogs17.brighton.domains/ net::ERR_ABORTED 500 – NewForOlly Dec 17 '21 at 14:34
  • Yes, it is removed – NewForOlly Dec 17 '21 at 14:58
  • 1
    can you tell me `zapp.ogs17.brighton.domains ` which api does the url represent in express application? – abhi patil Dec 17 '21 at 14:58
  • It is for the following code app.post('/', async (req, res) => { const { status, data } = await postSighting(req); res.status(status); if (data) res.json(data); else res.end(); }) – NewForOlly Dec 17 '21 at 15:01
  • It works fine with postman which is what confuses me a lot – NewForOlly Dec 17 '21 at 15:04
  • 1
    because postman and curl doesn't bind any preflight headers. and browsers does. ive made few changes if it works mark it as answered. – abhi patil Dec 17 '21 at 15:48
  • I've made the changes but I am still receiving the same error net::ERR_ABORTED 500 – NewForOlly Dec 17 '21 at 16:23