fetch POST returns only _id object in Express API server React FrontEnd

Viewed 2289

I'm trying to make a React-Node.js application for practice. I encountered an problem in sending POST request. When I fetch POST request in App.js, it returns only id. I expected it to return 3 more values.

Current object

{ _id: 5a046d52bb5d37063b3c8b21 }

Ideal object

{_id: "59e9fed60fe8bf0d7fd4ac6e", name: "recipe1", ingredients: "apple", descriptions: "cut an apple"}

How should I add values to req.body correctly? I referred this solution Post an object with fetch using react js and express API server but it didn't work with my app.

index.js (node.js)

const express = require('express');
const path = require('path');
const bodyParser = require('body-parser');
const app = express();

 // Serve static files from the React app
app.use(express.static(path.join(__dirname, 'client/build')));

app.use(bodyParser.urlencoded({ extended: true}));

var db

const MongoClient = require('mongodb').MongoClient

MongoClient.connect
('mongodb://Recipe:recipebox@ds125914.mlab.com:25914/ayumi', (err, database) => {
 if (err) return console.log(err)
 db = database
 app.listen(8080, () => {
    console.log('listening on 8080')
 })
 })

 app.get('/api', (req,res)=> {
     db.collection('recipe').find().toArray((err, results) => {
     if(err) return console.log("no recipe");
         res.json(results);
     })
 })

 app.post('/recipe', (req,res)=>{
     db.collection('recipe').save(req.body, (err, result) => {
     if(err) return console.log(err);
          console.log(req.body)
    console.log('save to database');
    res.redirect('/');
})
})

App.js (react)

class App extends Component {
constructor(props){
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
}

handleSubmit(e){
  e.preventDefault();
  fetch('/recipe', {
       method: 'POST',
       body: JSON.stringify({
           name: this.refs.name.value,
           ingredients: this.refs.ingredients.value,
           descriptions: this.refs.descriptions.value
       }),
       headers: {"Content-Type" : "application/json"}
       })
  .then((res)=> {
      return res.json()
  })
  .then((body)=>{
      console.log("body" + body)
      console.log("result" + this.refs.name.value)
  })
}

render() {

return (
  <div className="App">
  <h1>Recipe List</h1>
  <form onSubmit={this.handleSubmit}>
  <input type="text" placeholder="name" ref="name" />
  <input type="text" placeholder="ingredients" ref="ingredients" />
  <input type="text" placeholder="descriptions" ref="descriptions" />
  <input type="submit"/>
  </form>
  </div>
  )
}

}

export default App;

1 Answers

Server-side changes:

app.post('/recipe', (req, res) => {
  // log the body of the request, to make sure data is properly passed
  console.log(req.body);
  // use mongodb's insertOne instead of the deprecated save
  db.collection('recipe').insertOne(req.body, (err, result) => {
    if (err) return console.log(err);
    // log the result of db insertion
    console.log(result);
    console.log('saved to database');
    // send the freshly saved record back to the front-end
    res.json(result);
  });
});

Front-end changes:

class App extends Component {
  constructor(props){
    super(props);
    // add state to hold recipe returned from POST call
    this.state = {
      recipe: null,
      name: '',
      ingredients: '',
      descriptions: ''
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleSubmit(e) {
    e.preventDefault();
    const { name, ingredients, descriptions } = this.state;
    fetch('/recipe', {
      method: 'POST',
      body: JSON.stringify({
        name,
        ingredients,
        descriptions
      }),
      headers: {"Content-Type" : "application/json"}
    })
    // when call completes, it should return the newly created recipe object
    // as it was saved in the DB - just store it into state
    .then((recipe)=> {
      this.setState({recipe});
    });
    // TODO: handle error case
  }

  render() {
    // added a paragraph tag to display the ID of the freshly created recipe
    // it's only displayed if recipe is not null or undefined
    // further changes: turning inputs into controlled inputs
    const { name, ingredients, descriptions } = this.state;
    return (
      <div className="App">
        <h1>Recipe List</h1>
        <form onSubmit={this.handleSubmit}>
          <input
            value={name}
            type="text"
            onChange={e => this.setState({ name: e.target.value })}
            placeholder="name" />
          <input
            value={ingredients}
            type="text"
            onChange={e => this.setState({ ingredients: e.target.value })}                
            placeholder="ingredients" />
          <input
            value={descriptions}
            type="text"
            onChange={e => this.setState({ descriptions: e.target.value })}
            placeholder="descriptions" />
          <input type="submit"/>
          { recipe &&
            <p>Saved ID: {this.state.recipe._id}</p>
          }
        </form>
      </div>
    );
  }
}

export default App;

Further changes: turned all three text inputs into controlled inputs (values of all 3 fields are tracked in state, and passed to fetch when the form is submitted).

Related