Error: Unexpected token n in JSON at position 0 (course: react native part 2)

HI, i am taking the course ‘react native part 2’ and for the backend i downloaded the files everything worked till chapter ‘14 - posting data’. I have the following error from the server:
Unexpected token n in JSON at position 0

when i post normal json data from the react native it works , but when I use formData() with images it just doesn’t anymore… I am not able to solve this it is a bit frustrating … the code is just like moshe’s i checked line by line. I looked so much all i know is that apisauce may not support multipart/form-data but in the video the post works without any issues. Please is you know what could be happening it will be a great help.

import apiClient from ‘./client’;

const endpoint = ‘/listings’;
const getListings = () => apiClient.get(endpoint);

const addListing = (listing) => {
console.log(‘:: image ::’, listing.images[0]);
const data = new FormData();
data.append(‘title’, listing.title);
data.append(‘price’, listing.price);
data.append(‘categoryId’, listing.category.value);
data.append(‘description’, listing.description);

listing.images.forEach((image, index) =>
    data.append('images', {
        name: 'image' + index,
        type: 'image/jpeg',
        uri: image
    })
);

if (listing.location)
    data.append('location', JSON.stringify(listing.location));

return apiClient.post(endpoint, data);
// return apiClient.post(endpoint, {
//     title: 'Test',
//     price: '23',
//     categoryId: 4,
//     description: 'Test',
//     location: {
//         latitude: 37.33233141,
//         longitude: -122.0312186
//     }
// });

};

export default {
addListing,
getListings
};

the above code is exactly like the one in the video.

the commented code works that was my test, but when I send the formData object i receive the error. = (

Hello Danny,

I have the same problem.
Have you found a solution ?

Thanks

Best regards,

Yes, something weird happens with the npm 'cause after two weeks of trying to fix it with no success, i downloaded the whole project again… this time i verified that npm doesn’t update some packages specially multer for imgaes… i guess in my first try npm altered it and when running npm install. It’s weird but since then i am not changing the package json. Hope this can fix the error as it worked in my local environment.

1 Like

I experienced the same issue. In order to continue the training i commented out the upload or resize image logic. Hope somebody from the support team to resolve the issue.

@moshhamedani

I found the solution for the above error. Turns out that apisauce doesn’t convert the Content-Type header as Mosh suggests in the tutorial and you have to pass in the header when sending the post request.

Change the following line from:

return apiClient.post(endpoint, data);

To:

return apiClient.post(endpoint, data, {headers: { “Content-Type”: “multipart/form-data” }});

I hope this will fix the issue. Happy hacking :slight_smile:

3 Likes

Hello Danny,

I have the same problem.
Have you found a solution ?

Thanks

Best regards,

yes, as i mentioned before mine is working
This is my current package.json hope it helps:

{
    "name": "done-with-it-backend",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "start": "node index.js"
    },
    "engines": {
        "node": "12.6.x"
    },
    "keywords": [],
    "author": "Mosh Hamedani",
    "license": "ISC",
    "dependencies": {
        "body-parser": "^1.19.0",
        "compression": "^1.7.4",
        "config": "^3.3.1",
        "expo-server-sdk": "^3.5.0",
        "express": "^4.17.1",
        "helmet": "^3.22.0",
        "joi": "^14.3.1",
        "jsonwebtoken": "^8.5.1",
        "multer": "^1.4.2",
        "sharp": "^0.32.6"
    },
    "devDependencies": {
        "nodemon": "^2.0.2"
    }
}

Thanks man, you saved me a lot of time. That was it.

I ran into this issue going through the course as well using the most up-to-date packages as of this writing. Below is the solution step by step for anyone in the future that prefers using updated packages. The version numbers are posted below.

The first change I made was in the front end to listings.js in the API folder. You must specify the content-type in the headers to be “multipart/form-data” for the post request. Apisauce is not defaulting to “multipart/form-data”.

//Before
  return client.post(endpoint, data});

//After
  return client.post(endpoint, data, {
    headers: { "Content-Type": "multipart/form-data" },
  });

This update resolved the 500 error from the server; however, the server started sending an error stating that validate is not a valid method of Joi. Updating validation.js in the middleware folder to call the validate method on the schema instead of Joi resolved the issue. See Joi documentation for more details.

//Before
  const result = Joi.validate(req.body, schema);

//After
  const result = schema.validate(req.body);

After that, it’s necessary to go update Joi usage anywhere in the routes folder to use Joi.object() around the entire object we want to validate. This example is from listings.js.

//Before
const schema = {
  title: Joi.string().required(),
  description: Joi.string().allow(""),
  price: Joi.number().required().min(1),
  categoryId: Joi.number().required().min(1),
  location: Joi.object({
    latitude: Joi.number().required(),
    longitude: Joi.number().required(),
  }).optional(),
};

//After
const schema = Joi.object({
  title: Joi.string().required(),
  description: Joi.string().allow(""),
  price: Joi.number().required().min(1),
  categoryId: Joi.number().required().min(1),
  location: Joi.object({
    latitude: Joi.number().required(),
    longitude: Joi.number().required(),
  }).optional(),
});

Lastly, the server started sending a validation error from Joi that location must be an object. It is necessary to use JSON.stringify() in the front end to send the location object to the backend. Joi validation was receiving the object as a string and throwing an error. This is a solution to that issue, but perhaps there is a better solution. This update is in validation.js in the middleware folder as well. Essentially, the code checks if there is a location in the req.body. If there is a location, then it will parse it and pass it as the location object before Joi can validate the data.

//Before & After 1st change to Back End
  const result = schema.validate(req.body);

//After
  const result = schema.validate(
    req.body.location
      ? { ...req.body, location: JSON.parse(req.body.location) }
      : req.body
  );

After these changes, the success alert appeared. This solution is using the below package versions in the front end:

"dependencies": {
    "@expo/metro-runtime": "3.2.1",
    "@react-navigation/bottom-tabs": "^6.5.20",
    "@react-navigation/native": "^6.1.17",
    "@react-navigation/stack": "^6.3.29",
    "apisauce": "^3.0.1",
    "expo": "^51.0.2",
    "expo-constants": "^16.0.1",
    "expo-image-picker": "^15.0.4",
    "expo-location": "^17.0.1",
    "expo-permissions": "^14.4.0",
    "expo-status-bar": "^1.12.1",
    "formik": "^2.4.6",
    "lottie-react-native": "^6.7.0",
    "react": "^18.2.0",
    "react-native": "^0.74.1",
    "react-native-gesture-handler": "^2.16.2",
    "react-native-safe-area-context": "^4.10.1",
    "react-native-screens": "^3.31.1",
    "yup": "^1.4.0"
  },
  "devDependencies": {
    "@babel/core": "^7.24.5"
  },

These versions in the back end:

"engines": {
    "node": "21.7.3"
  },
  "dependencies": {
    "body-parser": "^1.20.2",
    "compression": "^1.7.4",
    "config": "^3.3.11",
    "expo-server-sdk": "^3.10.0",
    "express": "^4.19.2",
    "helmet": "^7.1.0",
    "joi": "^17.13.1",
    "jsonwebtoken": "^9.0.2",
    "multer": "^1.4.5-lts.1",
    "sharp": "^0.33.3"
  },
  "devDependencies": {
    "nodemon": "^3.1.0"
  }

@jgallaway This was very helpful, thank you. Could you help me with another problem I had. Not sure if you experienced this but after updating all the dependecies, my backend keeps on crashing and I get this error message:
node:fs:1878
binding.unlink(path);
^

Error: EPERM: operation not permitted, unlink ‘C:\Users.…\Backend\uploads\d601b4a1ad43f14307870f0f578c402d’
at Object.unlinkSync (node:fs:1878:11)
at C:\Users..\Backend\middleware\imageResize.js:21:8
at async Promise.all (index 0)
at async module.exports (C:\Users.…\Backend\middleware\imageResize.js:26:3) {
errno: -4048,
code: ‘EPERM’,
syscall: ‘unlink’,
path: ‘C:\Users\…\Backend\uploads\d601b4a1ad43f14307870f0f578c402d’
}
I think it has something to do with how the image is handled and resized once it’s in the backend but I’m not sure. If you can, please help. All our dependencies are similar so don’t worry about that

Hello Eddy, I'm glad you found it helpful. I didn't run into that error. My initial suggestion without seeing the code is to try to delete the upload in the path below and any photos in the public folder that don't have plain English words as their file name. You might not have any though. Here is my current server code in Github.

path: ‘C:\Users\…\Backend\uploads\d601b4a1ad43f14307870f0f578c402d’

@jgallaway I’ve checked out your server side code and compared it to mine. Everything looks exactly the same. The file I was mostly interested in though was imageResize.js because that was where the error message that crashed my backend was pointing to. I’m not very good with node but what I could tell from the error message is that it had something to do with fs.unlinkSync() method and the await Promise.all() method.

I had tried to delete all the files from the upload file but what I realized was that those were the same files that I was trying to upload from my ListingEdit screen so every time I uploaded a picture, it would end up there

What OS are you using? I can try to replicate the error using virtualBox. Yes, the listing uploads from the front end will go to the uploads folder if something goes wrong in the backend. Mosh has that in a comment as well from his code.

    // Order of these middleware matters.
    // "upload" should come before other "validate" because we have to handle
    // multi-part form data. Once the upload middleware from multer applied,
    // request.body will be populated and we can validate it. This means
    // if the request is invalid, we'll end up with one or more image files
    // stored in the uploads folder. We'll need to clean up this folder
    // using a separate process.

omgggg, thanks man, u saved my life, I was stuck here for over 12hours