How to show data in frontend after you send data in express js - node.js

In my backend I want to send the data in this code express.js
const Joi = require('joi')
const people = [
{id:1,username:"Youw"},
{id:2,username:"thuneer"}
]
app.get('/send',(req,res) => {
res.json({data:people})
})
app.post('/send',(req,res) => {
const {error} = validateCourse(req.body)
if (error) {
return res.status(400).json({data:"Username length should be atleast 5"})
}
res.status(200).json({data:people})
})
function validateCourse(params) {
const schema = Joi.object({
username:Joi.string().min(5).required(),
})
return schema.validate(params)
}
So here I know you guys understand it since this is the basic. The username supposed to be atleast 5 and then if not then error will be the data..but How I will show this in frontend? My friend suggested this code to me but I don't know how I will implemented or arrange this
const temp = await fetch("http://localhost:3939/send", { method: "POST", body: JSON.stringify(data) })
const res = await temp.json();
const data = JSON.parse(data);
if (res.errors) {
// handle errors here
if (res.errors.username) {
const usernameError = document.getElementById('username_error');
usernameError.innerText = res.errors.username;
}
}
<form action="/send" method="POST">
<input type="text" name="username" id="user" class="user-text">
<button type="submit">submit</button>
</form>
<div id="username_error"></div>
I don't wanna write this in ajax cause I will write sooner in my reactjs so I want the code to be more general so that I can write it in ReactJS.

Related

Why is my react form not working as it should?

I'm trying to send data to my database using a react form and it's not working as it should. When I click the button, the function handleSubmit is called but it gets an error for some reason.
This is the error I see in the console when I click the button:
Here is the code for the react form:
import { useState } from "react";
const ProjectAdminForm = () => {
const [sdg_desc, setSDGDesc] = useState('')
const [sdg_num, setSDGNum] = useState('')
const [goal, setGoal] = useState('')
const [orginization, setOrginization] = useState('')
const [source, setSource] = useState('')
const [location, setLocation] = useState('')
const [published, setPublished] = useState('')
const [website_url, setWebsiteURL] = useState('')
const [assignment_type, setAssignmentType] = useState('')
const [sharepoint_link, setSharepointLink] = useState('')
const [statement, setStatement] = useState('')
const [error, setError] = useState(null)
const handleSubmit = async (e) => {
e.preventDefault() // Prevents refresh of page from happening
console.log('button clicked')
const project = {sdg_desc, sdg_num, goal, orginization, source, location, published, website_url, assignment_type, sharepoint_link, statement}
// Sending form response to backend
const response = await fetch('/api/projects', {
method: 'POST',
body: JSON.stringify(project),
headers: {
'Content-Type': 'application/json'
}
})
const json = await response.json
// Checking for error
if (!response.ok) {
setError(json.error)
}
if (response.ok) {
// Reset form inputs back to empty string
setSDGDesc('')
setSDGNum('')
setGoal('')
setOrginization('')
setSource('')
setLocation('')
setPublished('')
setWebsiteURL('')
setAssignmentType('')
setSharepointLink('')
setStatement('')
setError(null)
console.log('new project added', json)
}
}
return (
<form className="create" onSubmit={handleSubmit}>
<h3>Add a New Project</h3>
<label>SDG Name:</label>
<input
type="text"
onChange={(e) => setSDGDesc(e.target.value)}
value={sdg_desc}
/>
<label>SDG Num:</label>
<input
type="text"
onChange={(e) => setSDGNum(e.target.value)}
value={sdg_num}
/>
<label>Goal:</label>
<input
type="text"
onChange={(e) => setGoal(e.target.value)}
value={goal}
/>
<label>Orginization:</label>
<input
type="text"
onChange={(e) => setOrginization(e.target.value)}
value={orginization}
/>
<label>Source:</label>
<input
type="text"
onChange={(e) => setSource(e.target.value)}
value={source}
/>
<label>Location:</label>
<input
type="text"
onChange={(e) => setLocation(e.target.value)}
value={location}
/>
<label>Published:</label>
<input
type="text"
onChange={(e) => setPublished(e.target.value)}
value={published}
/>
<label>Website URL:</label>
<input
type="text"
onChange={(e) => setWebsiteURL(e.target.value)}
value={website_url}
/>
<label>Assignment Type:</label>
<input
type="text"
onChange={(e) => setAssignmentType(e.target.value)}
value={assignment_type}
/>
<label>Sharepoint Link:</label>
<input
type="text"
onChange={(e) => setSharepointLink(e.target.value)}
value={sharepoint_link}
/>
<label>Statement:</label>
<input
type="text"
onChange={(e) => setStatement(e.target.value)}
value={statement}
/>
<button>Add Project</button>
{error && <div className="error">{error}</div>}
</form>
)
}
export default ProjectAdminForm
Here is the code of the backend api it points too:
// create new project
const createProject = async (req, res) => {
// adding in a new project
const {sdg_desc, sdg_num, goal, orginization, source, location, published, website_url, assignment_type, sharepoint_link, statement} = req.body
try {
const project = await Project.create({sdg_desc, sdg_num, goal, orginization, source, location, published, website_url, assignment_type, sharepoint_link, statement})
res.status(200).json(project)
} catch (error) {
res.status(400).json({error: error.message})
}
}
Could someone tell me why when I click the "Add Project" button I get this error?
There are multiple problems.
I am gonna assume you are using MongoDB and mongoose, so your code for backend should look like this.
const createProject = async (req, res) => {
const {sdg_desc, sdg_num, goal, orginization, source, location, published, website_url, assignment_type, sharepoint_link, statement} = req.body
const newProject = new Project{(
sdg_desc: sdg_desc,
sdg_num : sdg_num,
goal : goal,
orginization : orginization,
source : source,
location : location,
published : published,
website_url : website_url,
assignment_type : assignment_type,
sharepoint_link : sharepoint_link,
statement : statement
)};
try {
await newProject.save();
} catch (error) {
res.status(500).json({error: error.message})
}
res.status(201).json({project: newProject});
}
And on the frontend when you are using the fetch API, you need to specify full link to the backend, so it should look like this
const response = await fetch('http://localhost:8000/api/projects', {
method: 'POST',
body: JSON.stringify(project),
headers: {
'Content-Type': 'application/json'
}
})
Also make sure that the data you are sending to the backend have the same name on the frontend
Looks like the problem is coming from your backend, are you using any middlewares, validators like Joi ?
<button>Add Project</button>
Should be
<button onClick={handleSubmit}>Add Project</button>
or
<button onClick={()=>handleSubmit()}>Add Project</button>
You don’t call your function for some reason
I think you can work it out with form like that (but honestly I haven’t seen even single time somebody does it like that in react) but then you have to give something like:
<button type=“submit”>…
So also I would refactor a bit your bunch of setState:
const [state,setState] = useState({});
const handleInputChange = (e) => setState({…state, [e.target.name]:e.target.value});
On inputs:
<input name=“project” onChange={handleInputChange}>…</input>
This will give much more flexible state with less code
You should add console.log(req.body)
This would point to source of error

Cors error when sending data from front-end client side react component to node.js server [duplicate]

This question already has answers here:
CORS error even after setting Access-Control-Allow-Origin or other Access-Control-Allow-* headers on client side
(2 answers)
Closed 7 months ago.
I am attempting to send data from my client side react component below to my node.js server. Within my console it shows the data is being passed through from client side to server. However, I am getting the following cors error:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.
I am not sure why I am still getting this as I have required and implemented cors below, as well as put res.setHeader("Access-Control-Allow-Origin", "*"); in the post request.
Any help would be greatly appreciated! Thank you :)
UPDATE
I solved the issue by enabling the cors middleware app.use(cors())
node server.js file
const express = require("express");
const cors = require("cors");
const app = express();
const port = 4000;
app.post("/", cors(), (req, res) => {
res.setHeader("Access-Control-Allow-Origin", "*");
const emailInfo = req.body.emailUserInput;
console.log(emailInfo);
// sendgrid details //
require("dotenv").config();
const sgMail = require("#sendgrid/mail");
const apikey = process.env.SENDGRID_API_KEY;
sgMail.setApiKey(apikey);
const msg = {
to: emailInfo,
from: "email",
subject: "Sending with Twilio SendGrid is Fun",
text: "and easy to do anywhere, even with Node.js",
html: "<strong>and easy to do anywhere, even with Node.js</strong>",
};
// email sending logic //
//ES8
(async () => {
try {
await sgMail.send(msg);
} catch (error) {
console.error(error);
if (error.response) {
console.error(error.response.body);
}
}
})();
});
app.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
client-side react component
import "../StyleComponents/CreateEvent.css";
import { useState } from 'react';
import { db } from '../firebase';
import { uid } from "uid";
import { set, ref } from "firebase/database";
import Header from "./Header";
import { useNavigate } from 'react-router-dom';
import { getStorage, ref as sref, uploadBytes } from "firebase/storage";
import PlacesAutocomplete from 'react-places-autocomplete';
import { geocodeByAddress, getLatLng } from 'react-places-autocomplete';
import axios from 'axios';
function CreateEvent() {
// store user inputs in state //
const [titleUserInput, setTitleUserInput] = useState('');
const [dateUserInput, setDateUserInput] = useState('');
const [timeUserInput, setTimeUserInput] = useState('');
const [address, setAddress] = useState('');
const [setCoordinates] = useState({
lat: null,
lng: null,
})
const [headerUserInput, setHeaderUserInput] = useState('');
const [detailsUserInput, setDetailsUserInput] = useState('');
const [lengthUserInput, setLengthUserInput] = useState('');
const [emailUserInput, setEmailUserInput] = useState('');
const [userSubmit, setUserSubmit] = useState('');
const [error, setError] = useState(false);
// Create a root reference for storing event photos //
const storage = getStorage();
// handle changing user input data //
const handleTitleChange = (e) => {
setTitleUserInput(e.target.value);
}
const handleDateChange = (e) => {
setDateUserInput(e.target.value);
}
const handleTimeChange = (e) => {
setTimeUserInput(e.target.value);
}
const handleSelect = async (value) => {
const results = await geocodeByAddress(value);
const latLng = await getLatLng(results[0]);
setAddress(value);
setCoordinates(latLng);
}
const handleDetailsChange = (e) => {
setDetailsUserInput(e.target.value);
}
const handleLengthChange = (e) => {
setLengthUserInput(e.target.value);
}
const handleHeaderChange = (e) => {
// Create a root reference for storing event photos //
setHeaderUserInput(e.target.files[0]);
}
const handleEmailChange = (e) => {
setEmailUserInput(e.target.value);
}
const navigate = useNavigate();
// make call to the backend database to send email user input data //
const url = 'http://localhost:4000';
const getEmailInput = () => {
axios.post(url, {emailUserInput}).then((res) => {
console.log(res);
}).catch(console.log('error'));
}
// submit user data to database with unique ID for each event //
const writeToDataBase = () => {
let uuid = uid()
if (titleUserInput.length === 0 || dateUserInput.length === 0 || timeUserInput.length === 0 || address.length === 0 || emailUserInput.length === 0) {
setError(true);
}
if (titleUserInput && dateUserInput && timeUserInput && address && emailUserInput) {
const storageRef = sref(storage, uuid);
set(ref(db, `/${uuid}`), {
EventPhoto: headerUserInput,
EventTitle: titleUserInput,
EventDate: dateUserInput,
EventTime: timeUserInput,
EventLength: lengthUserInput,
EventLocation: address,
EventDetails: detailsUserInput,
});
getEmailInput('');
setUserSubmit('');
uploadBytes(storageRef, headerUserInput).then(() => {
navigate(`/EventCreated/${uuid}`);
});
}
}
return (
<>
< Header />
<div className="event-creation-container">
<h1>Create a New Event</h1>
<form>
<div className="event-name-container event-input">
<label for="eventTitle">Name of Event<span>*</span></label>
<input type="text" id="EventTitle" value={titleUserInput} onChange={handleTitleChange} />
{error && titleUserInput === '' ?
<label id="form-validation-label">Event name must be filled</label> : ""}
</div>
<div className="date-time-length">
<div className="date-input-container event-input">
<label for="Date">Date<span>*</span></label>
<input type="date" id="EventDate" value={dateUserInput} onChange={handleDateChange} />
{error && dateUserInput === '' ? <label id="form-validation-label">Event date must be filled</label>: ""}
</div>
<div className="time-input-container event-input">
<label for="Time">Time<span>*</span></label>
<input id="EventTime" type="time" name="time" timezone="timezone" value={timeUserInput} onChange={handleTimeChange} />
</div>
{error && timeUserInput === '' ? <label id="form-validation-label">Event time must be filled</label> : ""}
<div className="length-input-container event-input">
<label for="Length">Length</label>
<input id="EventLength" type="text" value={lengthUserInput} onChange={handleLengthChange} />
</div>
</div>
<div className="location-input-container event-input">
<label for="Location">Location<span>*</span></label>
<PlacesAutocomplete onChange={setAddress} value={address} onSelect={handleSelect}
>
{({ getInputProps, suggestions, getSuggestionItemProps, loading }) => (
<div>
<input id="EventLocation" {...getInputProps()} />
<div className="location-suggestions">
{loading ? <div>...loading</div> : null}
{suggestions.map((suggestion) => {
const style = {
backgroundColor: suggestion.active ? "#41b6e6" : "#fff"
};
return <div {...getSuggestionItemProps(suggestion, { style })}>{suggestion.description}</div>
})}
</div>
</div>
)}
</PlacesAutocomplete>
</div>
{error && address ==='' ? <label id="form-validation-label">Event location must be filled</label> : ""}
<div className="details-input-container event-input">
<label for="Event_Details">Event Details</label>
<textarea type="text" id="EventDetails" value={detailsUserInput} onChange={handleDetailsChange} />
</div>
<div className="header-input-container event-input">
<div className="upload-image-flex-container">
<label for="header_image">Upload Header Image (optional)</label>
<input className="upload-input" type="file" id="
EventImage" name="filename" accept="image/png, image/jpeg" onChange={handleHeaderChange} />
</div>
</div>
<div className="orangizer-email-container">
<label for="organizer-email">Organizer's Email<span>*</span></label>
<p>The event page link will be sent to your email</p>
<input id="EventEmail" type="email" name="email" value={emailUserInput} onChange={handleEmailChange} />
{error && emailUserInput === '' ? <label id="form-validation-label">Event organizer's email must be entered</label> : ""}
</div>
<div className="create-event-btn-container">
<button className="event-create-button" type="button" value={userSubmit} onClick={writeToDataBase}>Create Event</button>
</div>
</form>
</div>
</>
)}
export default CreateEvent;
Your issue here may be the browser sending out a CORS preflight request which is then followed by your POST request. This is an OPTIONS request sent automatically which is where it is expecting to get the CORS headers from. See here.
Currently even though you've implemented CORS on your post endpoint, it's not catching that preflight request which is likely what you need to do. The 'cors' npm package describes that on their page. Implementing this should then allow the browser to recognise the origin so you no longer get errors back.

How to upload files from a backend (Heroku) to frontend in (Netlify) hosted on github

I've developed an app that's uploaded to Github and I'm using Heroku to host the (Backend folder) from Github using (automatic deployment) and also using Netlify to host the (Frontend folder) and it's working great in my local computer, but when I try to upload files from my form in frontend it sends a request to the backend and the backend it self saves the file to /uploads folder that's located in frontend directory.
My file structure is like this:
[Server]
- controllers
- - food.js
[Client]
- public
-- uploads
- src
-- pages
--- dashboard
---- food
----- AddFood.js
it's working great on localhost, and this is my code:
(client)
AddFood.js:
import { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import Axios from 'axios'
import useDocumentTitle from '../../../hooks/useDocumentTitle'
import Modal from '../../../components/Modal/Modal'
import { Success, Error, Loading } from '../../../components/Icons/Status'
import { createSlug } from '../../../functions/slug'
import goTo from '../../../functions/goTo'
const AddFood = () => {
useDocumentTitle('Add Food')
//Form States
const [foodName, setFoodName] = useState('')
const [foodPrice, setFoodPrice] = useState('')
const [foodDesc, setFoodDesc] = useState('')
const [foodFile, setFoodFile] = useState('')
const [preview, setPreview] = useState()
const [addFoodStatus, setAddFoodStatus] = useState()
const [addFoodMessage, setAddFoodMessage] = useState()
//Form errors messages
const ImgErr = document.querySelector('[data-form-img-msg]')
const foodNameErr = document.querySelector('[data-form-name-msg]')
const priceErr = document.querySelector('[data-form-price-msg]')
const descErr = document.querySelector('[data-form-desc-msg]')
const formMsg = document.querySelector('[data-form-msg]')
const modalLoading = document.querySelector('#modal')
const BASE_URL =
process.env.NODE_ENV === 'development'
? process.env.REACT_APP_API_LOCAL_URL
: process.env.REACT_APP_API_URL
const updateFoodImg = e => {
const file = e.target.files[0]
if (file) {
const fileType = file.type.split('/')[0]
if (fileType === 'image') setFoodFile(file)
const fileSizeToMB = file.size / 1000000
const MAX_FILE_SIZE = 1 //mb
if (fileSizeToMB > MAX_FILE_SIZE) {
if (ImgErr)
ImgErr.textContent = `file size can't be more than ${MAX_FILE_SIZE} MB`
} else {
ImgErr.textContent = ''
}
}
}
useEffect(() => {
// if there's an image
if (foodFile) {
const reader = new FileReader()
reader.onloadend = () => setPreview(reader.result)
reader.readAsDataURL(foodFile)
} else {
setPreview(null)
}
}, [foodFile])
const handleAddFood = async e => {
e.preventDefault()
//using FormData to send constructed data
const formData = new FormData()
formData.append('foodName', foodName)
formData.append('foodPrice', foodPrice)
formData.append('foodDesc', foodDesc)
formData.append('foodImg', foodFile)
if (
ImgErr.textContent === '' &&
foodNameErr.textContent === '' &&
priceErr.textContent === '' &&
descErr.textContent === ''
) {
//show waiting modal
modalLoading.classList.remove('hidden')
try {
const response = await Axios.post(`${BASE_URL}/foods`, formData)
const { foodAdded, message } = response.data
setAddFoodStatus(foodAdded)
setAddFoodMessage(message)
//Remove waiting modal
setTimeout(() => {
modalLoading.classList.add('hidden')
}, 300)
} catch (err) {
formMsg.textContent = `Sorry something went wrong ${err}`
}
} else {
formMsg.textContent = 'please add all details'
}
}
return (
<>
{addFoodStatus === 1 ? (
<Modal
status={Success}
msg='Added food'
redirectLink='menu'
redirectTime='3000'
/>
) : addFoodStatus === 0 ? (
<Modal
status={Error}
msg={addFoodMessage}
msg=''
/>
) : null}
<section className='py-12 my-8 dashboard'>
<div className='container mx-auto'>
<h3 className='mx-0 mt-4 mb-12 text-2xl text-center'>Add food</h3>
<div>
<div className='food'>
{/* Show Modal Loading when submitting form */}
<Modal
status={Loading}
modalHidden='hidden'
classes='text-blue-500 text-center'
msg='Please wait'
/>
<form
method='POST'
className='form'
encType='multipart/form-data'
onSubmit={handleAddFood}
>
<label className='flex flex-wrap items-center justify-center gap-4 mb-8 sm:justify-between'>
<img
src={
preview === null
? 'https://source.unsplash.com/random?food'
: preview
}
alt='food' //change with food image name
className='object-cover p-1 border border-gray-400 w-28 h-28 dark:border-gray-300 rounded-xl'
/>
<input
type='file'
name='foodImg'
id='foodImg'
accept='image/*'
onChange={updateFoodImg}
className='grow-[.7] cursor-pointer text-lg text-white p-3 rounded-xl bg-orange-800 hover:bg-orange-700 transition-colors'
required
/>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-img-msg
></span>
</label>
<label htmlFor='foodName' className='form-group'>
<input
type='text'
id='foodName'
className='form-input'
autoFocus
required
onChange={e => setFoodName(createSlug(e.target.value.trim()))}
/>
<span className='form-label'>Food Name</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-name-msg
></span>
</label>
<label htmlFor='foodPrice' className='form-group'>
<input
type='number'
id='foodPrice'
className='form-input'
min='5'
max='500'
required
onChange={e => setFoodPrice(e.target.value.trim())}
/>
<span className='form-label'>Price</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-price-msg
></span>
</label>
<label htmlFor='foodDescription' className='form-group'>
<textarea
name='foodDescription'
id='foodDescription'
minLength='10'
maxLength='300'
className='form-input'
required
onChange={e => setFoodDesc(e.target.value.trim())}
></textarea>
<span className='form-label'>Description</span>
<span
className='inline-block my-2 text-red-400 font-[600]'
data-form-desc-msg
></span>
</label>
<div
className='my-14 text-red-400 font-[600] text-center text-xl'
data-form-msg
></div>
<div className='flex items-center justify-evenly'>
<button
type='submit'
className='min-w-[7rem] bg-green-600 hover:bg-green-700 text-white py-1.5 px-6 rounded-md'
>
Add
</button>
<Link
to={goTo('menu')}
className='text-gray-800 underline-hover text-bold dark:text-white'
>
Food Menu
</Link>
</div>
</form>
</div>
</div>
</div>
</section>
</>
)
}
export default AddFood
(server)
foods.js:
const FoodsModel = require(`${__dirname}/../models/food-model.js`)
const { v4: uuidv4 } = require('uuid')
const sharp = require('sharp')
const deleteFile = require('../functions/deleteFile')
const addFood = async (req, res) => {
const { foodName, foodPrice, foodDesc } = req.body
const { foodImg } = req.files
const foodImgName = uuidv4() + foodImg.name
const foodImgMovePath = `${__dirname}/../../client/public/uploads/${foodImgName}.webp`
const foodImgDisplayPath = `/uploads/${foodImgName}`
const foods = new FoodsModel({
foodImgDisplayPath,
foodName,
foodPrice,
foodDesc
})
sharp(foodImg.data)
.rotate()
.resize(200)
.jpeg({ mozjpeg: true, quality: 50 })
.toBuffer()
.then(newBuffer => {
//changing the old jpg image buffer to new webp buffer
foodImg.data = newBuffer
foodImg.mv(foodImgMovePath, err => {
if (err) {
res.json({
message: `Sorry something wrong with server! 😥: ${err}`
})
return
}
foods.save()
res.json({
message: 'Food Added Successfully',
foodAdded: 1
})
})
})
.catch(err => {
res.json({
//https://mhmdhidr-restaurant.netlify.app/uploads/20cc09a0-1811-48b0-bffa-49e7a1981537chicken-legs.webp
message: `Sorry! Something went wrong, check the error => 😥: \n ${err}`,
foodAdded: 0
})
})
}
const getFood = async (req, res) => {
res.json(res.paginatedResults)
}
const deleteFood = async (req, res) => {
const { prevFoodImg } = req.body
const { foodId } = req.params
deleteFile(prevFoodImg)
try {
await FoodsModel.findByIdAndRemove(foodId)
res.json({
message: 'Food Deleted Successfully',
foodDeleted: 1
})
} catch (error) {
res.json({
message: `Sorry! Something went wrong, check the error => 😥: \n ${error}`,
foodDeleted: 0
})
}
}
const updateFood = async (req, res) => {
const { foodName, foodPrice, foodDesc, prevFoodImg } = req.body
const { foodId } = req.params
const { foodImg } = req.files || ''
const foodImgName = uuidv4() + foodImg?.name || ''
const foodImgMovePath = `${__dirname}/../../client/public/uploads/${foodImgName || ''}`
const foodImgDisplayPath =
foodImg !== '' && foodImg !== undefined ? `/uploads/${foodImgName}` : prevFoodImg
try {
await FoodsModel.findByIdAndUpdate(foodId, {
foodImgDisplayPath,
foodName,
foodPrice,
foodDesc
})
if (foodImg !== '' && foodImg !== undefined) {
deleteFile(prevFoodImg)
foodImg.mv(foodImgMovePath, err => {
if (err) {
res.json({ message: `Sorry something wrong with server! 😥: ${err}` })
}
})
}
res.json({
message: 'Food Updated Successfully',
foodUpdated: 1
})
} catch (error) {
res.json({
message: `Sorry! Something went wrong, check the error => 😥: \n ${error}`,
foodUpdated: 0
})
}
}
module.exports = { addFood, getFood, deleteFood, updateFood }
But when I try to upload a file in the Netlify app this error shows up:
Error: ENOENT: no such file or directory, open '/app/controllers/../../client/public/uploads/9631bb96-e41d-4c9a-aa35-d22b551ab662MASHAWI-IN-DUBAI.jpeg.webp'
I've tried to Google it a lot but unfortunately didn't find a solution.
Thanks for your help.
A two part answer:
Your back-end has no business putting files into your front-end's directory structure.
A better choice might be to use an uploads/ folder in the back-end project, exposing those over HTTPS, and linking to them from your front-end.
But that won't work on Heroku due to its ephemeral filesystem.
An even better choice would be to save them to a cloud-based object store like Amazon S3 or Azure Blob Storage, or a more specialized service like Cloudinary if they're images. Heroku tends to recommend S3.
Your back-end now just needs to store the URL to each file and provide that link to your front-end upon request.
Even on other hosts that allow you to save files into your back-end's filesystem, using a third-party service has many benefits. You can trivially scale horizontally (adding new nodes), your application becomes less stateful, etc.
User uploads never belong in your code repository, no matter how and where you choose to host them. They are content, not code, and should not be tracked and versioned alongside your code.

ref.map is not a function?

this is my first post. Thank you all for the years, of assistance btw, I hope to pour in, as much as I've gotten from you guys/gals. Let's get started.
I have a Next.js / React-Redux application and here is my problem:
I'm attempting to update the Redux store, with JSON, that is returned from `fs.readFile' (the 'fs/promises' module:
//Product.js
function Product() {
const suggested_products = useSelector((state) => state.user.suggested_products) // json read from /api/products
const updateProducts = () => {
(JSON.parse(JSON.stringify(suggested_products)))?.map((product) => { // Compliler does not like this line
<div>
<input type='checkbox'>
<p> {product.category}</p>
<p>{product.service_name}</p>
</input
</div>
})
}
return (
if (userSelectedProduct) ? updateProducts() : <p>No data found</p>
}
//Form.js
import { useSWR } from 'swr'
const fetcher = (...args) => fetch(...args).then((res) => res.json());
function Form() {
const [url, setURL] = useState('');
const { data, error } = useSWR(url, fetcher);
<input
value={product}
onChange={
dispatch(updateCustomerCSP((JSON.parse(JSON.stringify(e.target.value)))));
setURL(`api/${product}/`); //Attempt to dynamically fetch data, from client side
dispatch(updateSuggestedProducts(data)); //Update data in store returned from client side fetching
}}
</input>
}
// pages/api/products
import fs from 'fs/promises';
export default function handler(req, res) {
const filePath = path.join(process.cwd(),`/data.js'); // /data.js contains JSON array [{"product-1": "value"}, {"product-2": "value"}], which is why I need to use map function.
try {
const fileData = fs.readFile(filePath);
const data = JSON.parse(JSON.stringify(fileData));
res.status(200).json(data);
} catch (err)
{
res.status(500).json({ error: 'Failed to load data' })
}
}
// This error throws ref.map is not a function from the Products.js component.
Here is the only info that I could find in regards to ref's in React.
Also, the JSON.parse and JSON.stringify wrappers, are to keep Redux happy with object going into the store. Thanks for reading community, looking forward to your input. Should any oneed more info, please let me know. Also, here is info in regards to refs, that I could find:
https://reactjs.org/docs/refs-and-the-dom.html
I figured it out. I actually had the input wrapped in a HOC, and the event handler wasn't properly registered to the input element itself., therefore failing to load in the array object into the store to use.
So be careful of that, when building your own components.
For instance,
function Component() {
function loadArrayInStore() {
loadInStore()
}
const MyButton = (props) => {
<input onChange={props.handler} // I had an inline handler here such as onChange={ (e)= { doNotLoadArrayInStoreAndDontWork()}
}
return (
<MyButton handler={loadArrayInStore} />
)
}
So be watchful, when creating HOC's :)

File upload (with other inputs and textarea) using Angular 13 and Node Js

I am trying to upload files to server using Angular and Node, using multer.
I have Todo Model as :
export class TodoModel {
todo_id !:number;
todo_title !:string;
todo_description !:string;
todo_status !:number;
todo_deleted_flag !:boolean;
todo_image !:Object;
}
todo.component.ts
title:string;
desc:string;
selected_image:File = null;
fileUploadListener(event){
//console.log(event)
//console.log(event.target.files[0])
this.selected_image = <File>event.target.files[0]
console.log(this.selected_image)
}
onSubmit(form:NgForm){
const fd = new FormData()
if(this.selected_image) {
fd.append('todo_image',this.selected_image,this.selected_image.name)
}
console.log(fd);
const todo_model : TodoModel = {
todo_id: null,
todo_title:this.title,
todo_description:this.desc,
todo_status:1,
todo_deleted_flag:false,
todo_image:null
}
console.log(fd);
this.todoAdd.emit(todoadded);
this.todoAdd_DB.emit(todo_model);
this.addTodo_DB(todo_model, fd)
form.resetForm();
}
addTodo_DB(todo_db: TodoModel, fileUpload:Object){
//const todo_db
return this.http.post<{message:any}>('http://localhost:3000/api/todos/post_all_todos_db', todo_db,fileUpload).subscribe(data => {
console.log(data.message);
console.log(todo_db);
})
}
todo.component.html
<div class="col-md-12">
<form (ngSubmit)="onSubmit(todoForm)" #todoForm="ngForm">
<div class="mb-3">
<label for="todo_title" class="form-label">Title</label>
<input type="text" class="form-control" id="todo_title" [(ngModel)]="title" name="title">
</div>
<div class="mb-3">
<label for="label" class="form-label">Description</label>
<textarea class="form-control" id="todo_description" [(ngModel)]="desc" name="desc"></textarea>
</div>
<div class="mb-3">
<label for="todo_image" class="form-label">Image</label>
<input type="file" class="form-control" id='todo_image' (change)="fileUploadListener($event)">
</div>
<button type="submit" class="btn btn-success">Add To Do</button>
</form>
</div>
</div>
And on Server Side, using Node Js and PgSQL :-
app.post('/api/todos/post_all_todos_db',upload_using_multer.single('todo_images') , (req, res, next) => {
// const todo_post = req.body;
const files = req.file;
console.log(files) // - ----------> This does NOT work
console.log(req.body) //------> this works
//PGSQL insert query here
res.status(201).json({
message:"Post Added Successfully"
})
})
While doing console.log() in Angular side, I am getting the form data, but, on Node Js side, I get it as null.
Almost every tutorial I see, uses only one file upload , and that too, try to submit the form using the Form's action. I dont want to do that, so I tried doing this.
I
i once had the same issue and solved it with formdata, my example uploads multiple files. here is an example:
Node.JS
const serverRoutes = (function () {
const express = require('express');
const router = express.Router();
const multer = require('multer');
const upload = multer();
router.post('/myresource', upload.any(), (req, res) => {
console.log(req.files);
});
return router;
});
on angular
export class DataService {
constructor(private http: HttpClient) { }
sendMyFiles(file): Observable<MyResponse> {
const formData = new FormData();
formData.append("file", file);
return this.http.post<MyResponse>(
`${environment.backendAPI}myresource`,
formData
);
}
}

Resources