remy / nodemon

Monitor for any changes in your node.js application and automatically restart the server - perfect for development
http://nodemon.io/
MIT License
26.34k stars 1.74k forks source link

When using nodemon to upload frontend files to the backend, password encryption on the backend fails to execute correctly because nodemon reloads the index.js file. #2212

Closed Jinyuanzhang1992 closed 5 months ago

Jinyuanzhang1992 commented 5 months ago

Problem

I have a file upload functionality on the frontend that uploads files to the backend server, which parses the files into JSON format and returns them to the frontend. The frontend then calls another API (API B) to write the data into MongoDB.

The JSON data parsed from the backend is returned correctly and can be logged to the console. However, when calling API B to write to the database, the process gets stuck at password encryption. nodemon then automatically reloads the src/index.js file, and the code after the encryption step doesn’t execute.

The frontend returns an error:

{ "message": "Network Error", "name": "AxiosError", "stack": "AxiosError: Network Error\n at XMLHttpRequest.handleError (webpack-internal:///(app-pages-browser)/./node_modules/axios/lib/adapters/xhr.js:119:14)\n at Axios.request (webpack-internal:///(app-pages-browser)/./node_modules/axios/lib/core/Axios.js:54:41)\n at async postUser (webpack-internal:///(app-pages-browser)/./src/app/api/postUser.ts:9:26)\n at async handleUpload (webpack-internal:///(app-pages-browser)/./src/app/components/usersInterface/bulkAddUsers/BulkAddUsers.tsx:175:31)", "config": { "transitional": { "silentJSONParsing": true, "forcedJSONParsing": true, "clarifyTimeoutError": false }, "adapter": [ "xhr", "http", "fetch" ], "transformRequest": [ null ], "transformResponse": [ null ], "timeout": 0, "xsrfCookieName": "XSRF-TOKEN", "xsrfHeaderName": "X-XSRF-TOKEN", "maxContentLength": -1, "maxBodyLength": -1, "env": {}, "headers": { "Accept": "application/json, text/plain, /", "Content-Type": "application/json" }, "method": "post", "url": "http://localhost:51003/api/users", "data": "[{\"name\":{\"firstName\":\"John\",\"lastName\":\"Doe\"},\"dob\":\"2001-04-15\",\"account\":\"john\",\"password\":\"12345\",\"role\":{\"userType\":\"student\"},\"contact\":{\"email\":\"john.doe@example.com\",\"phone\":\"+61412345678\"},\"address\":{\"houseNumber\":\"123\",\"street\":\"Example Street\",\"suburb\":\"Example Suburb\",\"city\":\"Sydney\",\"state\":\"NSW\",\"country\":\"Australia\",\"postalCode\":\"2000\"}},{\"name\":{\"firstName\":\"Jane\",\"lastName\":\"Smith\"},\"dob\":\"2000-09-21\",\"account\":\"jane\",\"password\":\"12345\",\"role\":{\"userType\":\"student\"},\"contact\":{\"email\":\"jane.smith@example.com\",\"phone\":\"+61487654321\"},\"address\":{\"houseNumber\":\"456\",\"street\":\"Sample Avenue\",\"suburb\":\"Sample Suburb\",\"city\":\"Melbourne\",\"state\":\"VIC\",\"country\":\"Australia\",\"postalCode\":\"3000\"}},{\"name\":{\"firstName\":\"Alice\",\"lastName\":\"Johnson\"},\"dob\":\"1999-12-05\",\"account\":\"alice\",\"password\":\"12345\",\"role\":{\"userType\":\"student\"},\"contact\":{\"email\":\"alice.johnson@example.com\",\"phone\":\"+61423456789\"},\"address\":{\"houseNumber\":\"789\",\"street\":\"Demo Road\",\"suburb\":\"Demo Suburb\",\"city\":\"Brisbane\",\"state\":\"QLD\",\"country\":\"Australia\",\"postalCode\":\"4000\"}}]" }, "code": "ERR_NETWORK", "status": null }

If I don’t use file upload and hard code the data on the frontend, API B can successfully write to the database. However, using file upload results in the same error even with hard-coded data.

code for upload files: const [selectedFiles, setSelectedFiles] = useState<File[]>([]); const [previewContent, setPreviewContent] = useState<string[]>([]); const [reminderShow, setReminderShow] = useState(false); const [isConfirmed, setIsConfirmed] = useState(false);

const handleFileChange = (event: React.ChangeEvent) => { if (!event.target.files) return;

const files = Array.from(event.target.files).filter(
  (file) => file.type === "text/csv" || file.type === "application/json"
);

setSelectedFiles(files);

// 读取文件内容并放到previewContent
files.forEach((file, index) => {
  //创建一个FileReader对象用于读取文件
  const reader = new FileReader();
  //每个文件读取完成后,把内容放到previewContent数组的对应位置
  reader.onload = (e) => {
    //本次读取的文件内容
    const content = e.target?.result as string;
    //将本次读取的文件内容放到previewContent数组的对应位置,确保数组的顺序和文件的顺序一致
    setPreviewContent((prevContent) => {
      const newContent = [...prevContent];
      newContent[index] = content;
      return newContent;
    });
  };

  if (file.type === "text/csv" || file.type === "application/json") {
    reader.readAsText(file);
  }
});

};

parse files to json data: // 设置 multer 存储配置 const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, "uploads/"); }, filename: (req, file, cb) => { cb(null, file.originalname); }, });

// 创建 multer 实例 const upload = multer({ storage: storage }).array("files");

const receiveFiles = async (req, res, next) => { upload(req, res, async (err) => { if (err instanceof multer.MulterError) { const customErr = createNewErrors( "File upload failed.", 500, "uploadError", err.message ); console.log("err.message", err.message); return next(customErr); } else if (err) { const customErr = createNewErrors( "Unknown error occurred.", 500, "unknownError", err.message ); return next(customErr); }

// 文件上传成功,解析文件内容
try {
  let newUsers = [];
  for (const file of req.files) {
    const filePath = path.join(
      __dirname,
      "../../uploads",
      file.originalname
    );
    if (file.mimetype === "text/csv") {
      newUsers = await parseCSVAndInsert(filePath, next);
    } else if (file.mimetype === "application/json") {
      newUsers = await parseJSONAndInsert(filePath, next);
    }
  }
  console.log("newUsers", newUsers);
  res.formatResponse(200, "Files uploaded successfully.", newUsers);
} catch (error) {
  console.error("Error parsing file:", error);
  const err = createNewErrors("Failed to parse file.", 400, "uploadError");
  return next(err);
}

}); };

const parseCSVAndInsert = async (filePath, next) => { try { const fileContent = await fs.readFile(filePath, "utf8"); const parsedData = Papa.parse(fileContent, { header: true, skipEmptyLines: true, });

if (parsedData.errors.length) {
  const errorMessage = parsedData.errors.map((e) => e.message).join(", ");
  const err = createNewErrors(
    `Failed to parse CSV file: ${errorMessage}`,
    400,
    "parseError"
  );
  return next(err);
}

const validatedUsers = await validateUsers(parsedData.data);
console.log("validatedUsers", validatedUsers);
// await insertUsersWithRoles(validatedUsers);
return validatedUsers;

} catch (error) { const err = createNewErrors("Failed to parse CSV file.", 400, "parseError"); return next(err); } };

const parseJSONAndInsert = async (filePath, next) => { console.log("开始解析 JSON 文件"); try { const fileContent = await fs.readFile(filePath, "utf8"); const users = JSON.parse(fileContent);

console.log("users", users);

// const validatedUsers = await validateUsers(users);
// await insertUsersWithRoles(validatedUsers);
// return validatedUsers;
return users;

} catch (error) { const err = createNewErrors( "Failed to parse JSON file.", 400, "parseError" ); return next(err); } };

const validateUsers = async (users) => { const validatedUsers = []; for (const user of users) { const { error, value } = userSchema.validate(user); if (error) { throw createNewErrors( Invalid user data: ${error.message}, 400, "validation" ); } validatedUsers.push(value); } console.log("验证通过"); return validatedUsers; };

I have only test that upload json file.

Expected behaviour

I expected it can write the users data into mongoDB

Actual behaviour

But it doesn't work. If I use the command node src/index.js, it will work smoothly.

Steps to reproduce

produce a frontend tsx file to upload files send request with the data getting from backend to another API B to write into mongoDB

produce a backend js file to parse the files to json data response the json data to frontend

using nodemon to start the server, upload a json file at client, then see the terminal at backend, it will reload index.js


If applicable, please append the --dump flag on your command and include the output here ensuring to remove any sensitive/personal details or tokens.

remy commented 5 months ago

Run nodemon with --verbose and you'll see which file is triggering the reload. My guess is that you're writing a json file into the same place that nodemon is watching (or updating a file that's being watched) with your data and it's causing the reload - as you'd expect.

Alternatively, tell nodemon to ignore the specific file you're writing (the json file).

Happy to reopen if you can provide a much more pared down system to replicate with, but I'm pretty certain that nodemon is reloading because it's watching the file.