1

I am working on a project in which I need to post a new Course to my API. I tested this with POSTMAN and API works just fine, however when I try to post data using react fetch data is corrupted. While sending single strings like dishName: "pizza" works just fine and is shown in database I cannot manage to send an array of objects. I tried to do it in many ways like:

 [
            {
                "quantity": 1,
                "unit": "",
                "description": "egg"
            },
            {
                "quantity": 0.5,
                "unit": "cup",
                "description": "mozzarella shredded"
            }
]

or:

                {
                "quantity": 1,
                "unit": "",
                "description": "egg"
            },
            {
                "quantity": 0.5,
                "unit": "cup",
                "description": "mozzarella shredded"
            }

and many, many more combinations but I failed.

Here is my React class which is responsible for sending data to API:

import React, { useState } from "react";
import { useHistory } from "react-router-dom";

export default function Login() {
  const [dishName, setdishName] = useState("");
  const [category, setcategory] = useState("");
  const [author, setauthor] = useState("");
  const [ingredients, setingredients] = useState([]);
  const [cookingTime, setcookingTime] = useState("");
  const [sourceUrl, setsourceUrl] = useState("");
  const [imageUrl, setimageUrl] = useState("");
  const [isPublished, setisPublished] = useState("true");
  const [price, setprice] = useState("");
  const [tags, settags] = useState([]);
  const history = useHistory();

  async function login() {
    let item = {
      dishName,
      category,
      author,
      ingredients,
      cookingTime,
      sourceUrl,
      imageUrl,
      isPublished,
      price,
      tags,
    };

    await fetch("http://localhost:1234/api/courses", {
      method: "POST",
      body: JSON.stringify(item),
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json",
      },
    });

    history.push("/addCourse");
    //   window.location.reload();
  }

  return (
    <div className="col-sm-6" style={{ textAlign: "center" }}>
      <h1 className="bigBar">Create new recipe</h1>
      <input
        type="text"
        onChange={e => setdishName(e.target.value)}
        className="form-control"
        placeholder="dishName"
      />
      <br />

      <input
        type="text"
        onChange={e => setcategory(e.target.value)}
        className="form-control"
        placeholder="cathegory"
      />
      <br />

      <input
        type="text"
        onChange={e => setauthor(e.target.value)}
        className="form-control"
        placeholder="author"
      />
      <br />

      <input
        type="text"
        onChange={e => setingredients(e.target.value)}
        className="form-control"
        placeholder='{"quantity": int, "unit": "", "description": ""}'
      />
      <br />

      <input
        type="text"
        onChange={e => setcookingTime(e.target.value)}
        className="form-control"
        placeholder="cooking time"
      />
      <br />

      <input
        type="text"
        onChange={e => setsourceUrl(e.target.value)}
        className="form-control"
        placeholder="source url"
      />
      <br />

      <input
        type="text"
        onChange={e => setimageUrl(e.target.value)}
        className="form-control"
        placeholder="image url"
      />
      <br />

      <input
        type="text"
        onChange={e => setisPublished(e.target.value)}
        className="form-control"
        placeholder="publish status (dafault: true)"
      />
      <br />

      <input
        type="text"
        onChange={e => setprice(e.target.value)}
        className="form-control"
        placeholder="price"
      />
      <br />

      <input
        type="text"
        onChange={e => settags(e.target.value)}
        className="form-control"
        placeholder="tags"
      />
      <br />

      <button onClick={login} className="btn btn-primary">
        Sign up
      </button>
    </div>
  );
}

When I send data I get no error. There is simply an array of Ingredients which is empty when I click ot unfold its elements in MongoDB or in the case of Tags it doesn't even appear in MongoDB.

Could you please tell me how can I fix this or if it is me who is doing something wrong? I searched a lot for an answer but after 2 hours I found nothing useful.

EDIT: I am adding my REST API post method for new Courses:

router.post("/", async (req, res) => {
  const { error } = validateCourse(req.body);

  if (error)
    //   400 Bad request
    return res.status(400).send(error.details[0].message);

  let course = new Course(
    _.pick(req.body, [
      `dishName`,
      `category`,
      `author`,
      `ingredients`,
      `cookingTime`,
      `sourceUrl`,
      `imageUrl`,
      `isPublished`,
      `price`,
      //  `tags`,
    ])
  );

And schema for new Courses:

const mongoose = require(`mongoose`);
const Joi = require(`joi`);

function validateCourse(body) {
  const schema = Joi.object({
    dishName: Joi.string().min(3).required(),
    category: Joi.string().min(3).required(),
    author: Joi.string().min(3).required(),
    ingredients: Joi.required(),
    cookingTime: Joi.number().min(0).required(),
    sourceUrl: Joi.string().required(),
    imageUrl: Joi.string().required(),
    isPublished: Joi.boolean().required(),
    price: Joi.number().min(0).required(),
    tags: Joi.required(),
    date: Joi.allow(),
  });
  return schema.validate(body);
}

const Course = mongoose.model(
  `Course`,
  new mongoose.Schema({
    dishName: { type: String, required: true, minLength: 3, maxLength: 255 },
    category: {
      type: String,
      required: true,
      lowercase: true,
      trim: true,
      dishType: [
        "carrot",
        "broccoli",
        "asparagus",
        "cauliflower",
        "corn",
        "cucumber",
        "green pepper",
        "lettuce",
        "mushrooms",
        "onion",
        "potato",
        "pumpkin",
        "red pepper",
        "tomato",
        "beetroot",
        "brussel sprouts",
        "peas",
        "zucchini",
        "radish",
        "sweet potato",
        "artichoke",
        "leek",
        "cabbage",
        "celery",
        "chili",
        "garlic",
        "basil",
        "coriander",
        "parsley",
        "dill",
        "rosemary",
        "oregano",
        "cinnamon",
        "saffron",
        "green bean",
        "bean",
        "chickpea",
        "lentil",
        "apple",
        "apricot",
        "avocado",
        "banana",
        "blackberry",
        "blackcurrant",
        "blueberry",
        "boysenberry",
        "cherry",
        "coconut",
        "fig",
        "grape",
        "grapefruit",
        "kiwifruit",
        "lemon",
        "lime",
        "lychee",
        "mandarin",
        "mango",
        "melon",
        "nectarine",
        "orange",
        "papaya",
        "passion fruit",
        "peach",
        "pear",
        "pineapple",
        "plum",
        "pomegranate",
        "quince",
        "raspberry",
        "strawberry",
        "watermelon",
        "salad",
        "pizza",
        "pasta",
        "popcorn",
        "lobster",
        "steak",
        "bbq",
        "pudding",
        "hamburger",
        "pie",
        "cake",
        "sausage",
        "Tacos",
        "Kebab",
        "poutine",
        "seafood",
        "chips",
        "fries",
        "masala",
        "paella",
        "som tam",
        "chicken",
        "toast",
        "marzipan",
        "tofu",
        "Ketchup",
        "hummus",
        "chili",
        "maple syrup",
        "parma ham",
        "fajitas",
        "champ",
        "lasagna",
        "poke",
        "chocolate",
        "croissant",
        "arepas",
        "bunny chow",
        "pierogi",
        "donuts",
        "rendang",
        "sushi",
        "ice cream",
        "duck",
        "curry",
        "beef",
        "goat",
        "lamb",
        "turkey",
        "pork",
        "fish",
        "crab",
        "bacon",
        "ham",
        "pepperoni",
        "salami",
        "ribs",
        "other",
      ],
    },
    author: String,
    ingredients: [
      {
        quantity: Number,
        unit: String,
        description: String,
      },
    ],
    cookingTime: { type: Number, required: true },
    sourceUrl: String,
    imageUrl: String,
    date: { type: Date, default: Date.now },
    isPublished: { type: Boolean, default: true },
    price: {
      type: Number,
      required: function () {
        return this.isPublished;
      },
    },
    tags: {
      type: Array,
      validate: {
        validator: function (v) {
          return v && v.length > 0;
        },
        message: `A course should have at least one tag.`,
      },
    },
  })
);

exports.Course = Course;
exports.validateCourse = validateCourse;

1 Answer 1

3

You are setting the ingredients state as a string, so you are basically 'stringify' a string which will result in JSON SyntaxError. If you want to send an array that way you must specify the array bracket [ and ] in order to make it a valid array.

To solve it just change:


      <input
        type="text"
        onChange={e => setingredients(e.target.value)}
        className="form-control"
        placeholder='{"quantity": int, "unit": "", "description": ""}'
      />

You need to change it to:


      <input
        type="text"
        //parse the string input
        onChange={e => setingredients(JSON.parse(e.target.value))}
        className="form-control"
        //notice the array bracket '[' and ']'
        placeholder='[{"quantity": int, "unit": "", "description": ""}, {"quantity": int, "unit": "", "description": ""}]'
      />
Sign up to request clarification or add additional context in comments.

4 Comments

Hello, thank you for your response. What you told me (adding [] in input field) sadly did not allow me to post without an error. I still get: Uncaught SyntaxError: Unexpected end of JSON input and Failed to load resource: the server responded with a status of 500 (Internal Server Error)
When I try to post ingredients with [] wihtout adding setingredients(JSON.parse(e.target.value))} I only get 500 error however
Are you inputting a valid JSON to to the field? try this [{"quantity":"1", "unit":"","description":"egg"}]
Hello, I tried to do it as you told me but here is the error I received: "Course validation failed: ingredients: Cast to embedded failed for value \"'[{\"quantity\":\"1\", \"unit\":\"\",\"description\":\"egg\"}]'\" (type string) at path \"ingredients\"

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.