Not sure why my frontend and backend aren't connecting; is it an API issue? Help pls - node.js

The issue is that the connection doesn't seem to be going through. The error log doesn't show anything: there's no sign data is being sent to the database. E.g. when I log in, or sign up, I can see that I'm doing that in the console. No dice here.
Vue component:
<script>
import KitchenAssignment from "../models/kitchenAssignment";
export default {
name: 'sendAssignment',
data(){
return {
kitchenAssignment: new KitchenAssignment('', '', '', '')
};
},
methods: {
submitForm(){
this.$store.dispatch('auth/saveAssignment')
}
}
}
</script>
Pertinent bit of my auth.service.js file:
import axios from 'axios';
const API_URL = 'https://appname.herokuapp.com/api/auth/';
...
saveAssignment(kitchenAssignment){
return axios.post(API_URL + 'kitchenAssignments', {
kAName: kitchenAssignment.kAName,
startTime: kitchenAssignment.startTime,
endTime: kitchenAssignment.endTime,
minRoleRequired: kitchenAssignment.password
})
}
My routes file:
const { authJwt } = require("../middleware"); // nothing in here yet, just trying to be sure i can actually send data to database
const controller = require("../controllers/kitchenAssignment.controller.js");
module.exports = function(app) {
app.use(function(req, res, next) {
res.header(
"Access-Control-Allow-Headers",
"Origin, Content-Type, Accept"
);
next();
});
app.post('/api/auth/kitchenAssignments', controller.saveAssignment);
};
My controller file:
const db = require("../models");
const KitchenAssignment = db.kitchenAssignment;
const Op = db.Sequelize.Op;
//const { default: kitchenAssignment } = require("../../vue-vuex-jwt-auth-master/src/models/kitchenAssignment");
exports.saveAssignment = async (req, res) => {
try {
const kitchenAssignment = await KitchenAssignment.create({
kAName: req.body.kAName,
startTime: req.body.startTime,
endTime: req.body.endTime,
minRoleRequired: req.body.minRoleRequired
});
}
catch (error) {
res.status(500).send({ message: error.message });
}
}
Not sure it's relevant, but here's my input area for the user in the .vue component? Maybe it's incorrect somehow.
<template>
<div class="container">
<header class="jumbotron">
<h3>{{content}}</h3>
<div>
<input v-model="kitchenAssignment.kAName"
placeholder="Input kitchen assignment name."
/>
<input v-model="kitchenAssignment.startTime"
placeholder="Input start time."
type="time"/>
<input v-model="kitchenAssignment.endTime"
placeholder="Input end time."
type="time"/>
<input
v-model="kitchenAssignment.minRoleRequired"
placeholder="Select minium role required."
v-validate="'required|pattern:^(Kitchen Leader|Sous Chef|Line Cook|Junior Cook|Dishwasher)$'"
name="minimum role required"
/>
<button #click="submitForm">Enter</button>
</div>
</header>
</div>
</template>
Would appreciate any help. I've tried a lot of stuff, can't recall everything but it led me to this point. Been reading up on Apis to no avail.

Related

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.

How i can update the image url if user select new file other wise image url not update in nodejs

I'm creating a book project where i'm saving the books images into the cloudinary and there url's saving into the mongodb database which are working well.But i'm facing issue during the updation of a book when i update my book then the url of book is not updated and console giving me error Cannot read properties of undefined (reading 'map') where i want to update the url with new one url of image but its not working Please any one can solve this
this is my update.js code
module.exports.updateBook = async (req, res) => {
try {
const { id } = req.params;
const book = req.body;
const singleBook = await Book.findById(id);
// Delete Prvious Url From the Cloudinary and Reset It to the new ..
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
book.image = req.files.map((f) => ({
url: f.path,
filename: f.filename,
}));
console.log("Single Book ===", singleBook);
const updateBook = await Book.findByIdAndUpdate(
id,
{ $set: book },
{ new: true }
);
if (updateBook) {
res
.status(200)
.json({ success: true, message: "Book Updated Successfully!" });
} else {
res.status(400).json({
success: false,
message: "Book Not Updated There Is an error!",
});
}
} catch (err) {
console.log("** Error In Update Book **", err.message);
}
};
this is my route handler
const express = require("express");
const router = express.Router();
const book = require("../controller/book");
const authenticated = require("../middleware/verifyToken");
const multer = require("multer");
const { storage } = require("../cloudinary");
const upload = multer({ storage });
// Update Book By ID
router.route("/:id").put(authenticated, upload.array("image"), book.updateBook);
module.exports = router;
this is my reactjs update method
const formik = useFormik({
initialValues: {
title: book?.title,
author: book?.author,
price: book?.price,
description: book?.description,
image: book?.image[0].url,
},
validationSchema: validationSchema,
enableReinitialize: true,
onSubmit: (values) => {
const formData = new FormData();
formData.append("title", values.title);
formData.append("price", values.price);
formData.append("description", values.description);
formData.append("author", values.author);
formData.append("image", values.image);
Axios.put(`${Base_URL}/book/${id}`, values, {
headers: {
Authorization: authHeader(),
},
})
.then((res) => {
if (res.data.success) {
message = res.data.message;
setAlertContentupdate(message);
setAlertupdate(true);
setTimeout(() => {
handleClose();
navigate(`/book/${id}`);
getBook();
console.log("Response == ", res.data.message);
}, 3000);
}
})
.catch((err) => {
console.log("Error ====", err.message);
});
},
this is my jsx code for updating book
<form onSubmit={formik.handleSubmit}>
<TextField
name="title"
autoFocus
margin="dense"
label="Book Title"
type="text"
fullWidth
variant="standard"
value={formik.values.title}
onChange={formik.handleChange}
error={formik.touched.title && Boolean(formik.errors.title)}
helperText={formik.touched.title && formik.errors.title}
/>
<TextField
name="author"
margin="dense"
label="Book Author"
type="text"
fullWidth
variant="standard"
value={formik.values.author}
onChange={formik.handleChange}
error={formik.touched.author && Boolean(formik.errors.title)}
helperText={formik.touched.author && formik.errors.author}
/>
{/* File Input Field */}
{/* Picture Input */}
<input
type="file"
name="image"
accept=".png, .jpeg, .jpg"
onChange={(e) => {
formik.setFieldValue("image", e.target.files[0]);
}}
/>
{formik.touched.image && formik.errors.image ? (
<div style={{ color: "#e53935", fontSize: "12px" }}>
{formik.errors.image}
</div>
) : null}
{/* Price Input Field */}
<TextField
name="price"
margin="dense"
label="Book Price"
type="text"
fullWidth
variant="standard"
value={formik.values.price}
onChange={formik.handleChange}
error={formik.touched.price && Boolean(formik.errors.price)}
helperText={formik.touched.price && formik.errors.price}
/>
<TextField
name="description"
margin="dense"
label="Book Description"
type="text"
fullWidth
variant="standard"
value={formik.values.description}
onChange={formik.handleChange}
error={
formik.touched.description &&
Boolean(formik.errors.description)
}
helperText={
formik.touched.description && formik.errors.description
}
/>
<DialogActions>
<Button onClick={handleClose}>Cancel</Button>
<Button type="submit">Update</Button>
</DialogActions>
</form>
In formik i'm getting the book data from back end api's and putting into the formik initial values But Problem is that when i clicked on the update button then the backend compiler giving me this error Cannot read properties of undefined (reading 'map') Please any one can solve this thanks in advance
So this line looks like the issue for me:
cloudinary.v2.uploader.destroy(singleBook.image[0].filename);
Using this is actually deleting your asset so you are probably want to just update it using the explicit API. See https://cloudinary.com/documentation/image_upload_api_reference#explicit
So maybe something like:
cloudinary.v2.uploader.explicit(singleBook.image[0].filename);
Let me know if this helps?

AWS Simple Email Service + Nodemailer = no errors in dev and in prod -- instant delivery in dev -- yet nothing is delivered in prod?

I have been trouble shooting what the disconnect might be between production and development for sending emails using SES + Nodemailer in Nextjs. However, I can't seem to determine what is happening as there are no bounces in my SES console and no errors being logged to the dev console or real-time serverless function logs in production. The repo can be found here
Any insight would be greatly appreciated.
client-side form, #components/Portals/nodemail.tsx
import { Input, Button, Textarea, Logo, ModalBackdrop } from '#components/UI';
import { useState, SyntheticEvent, FC, useEffect, useCallback } from 'react';
import { useUI } from '#components/context';
import css from './contact-us.module.css';
import cn from 'classnames';
import { validEmail } from '#lib/validate-email';
const SendEmail: FC = () => {
const { setModalView } = useUI();
const [inputE1, setInputE1] = useState('');
const [inputE2, setInputE2] = useState('');
const [inputE3, setInputE3] = useState('');
const [inputE4, setInputE4] = useState('');
// const inputText = useRef<HTMLTextAreaElement>(null);
const [dirty, setDirty] = useState(false);
const [message, setMessage] = useState('');
const [disabled, setDisabled] = useState(false);
const [loading, setLoading] = useState(false);
const userSend = async (e: SyntheticEvent<EventTarget>) => {
e.preventDefault();
// const prod = `drisdell.org/api/nodemailer`;
// const dev = '/api/nodemailer';
if (!dirty && !disabled) {
setDirty(true);
handleValidation();
}
setLoading(true);
setMessage('');
let res = await fetch('/api/nodemailer', {
body: JSON.stringify({
text: inputE3,
subject: inputE4,
name: inputE2,
email: inputE1
}),
headers: {
Accept: 'application/json, text/plain, */*',
'Content-Type': 'application/json'
},
method: 'POST'
});
const { error, data } = await res.json();
if (error) {
setMessage(error);
return;
}
setLoading(false);
setInputE1('');
setInputE2('');
setInputE3('');
setInputE4('');
setMessage(
'Success πŸŽ‰ email sent! We will get back to you within several business days' +
`${data}`
);
await setModalView('SUCCESS_VIEW');
};
const handleValidation = useCallback(() => {
if (dirty) {
setDisabled(!validEmail(inputE1) || !inputE2 || !inputE3 || !inputE4);
}
}, [inputE1, inputE2, inputE3, inputE4, dirty]);
useEffect(() => {
handleValidation();
}, [handleValidation]);
return (
<form
onSubmit={userSend}
className={cn('w-100 flex flex-col justify-between')}
>
<div className='flex justify-center pb-4 '>
<Logo className='h-20 w-20 md:h-40 md:w-40 rounded-full' />
</div>
<div className='relative max-w-xl mx-auto'>
<ModalBackdrop />
{message && (
<div className='text-white border border-white p-2 mb-2 rounded-2xl'>
{message}
</div>
)}
<label htmlFor='email'>{'Email Address'}</label>
<Input
id='from-input'
name='from'
placeholder='user#example.net'
onChange={setInputE1}
required={true}
type='email'
className='mb-2 bg-primary-9 text-primary-0 font-medium focus:outline-none rounded-md'
/>
<label htmlFor='name'>{'Full Name'}</label>
<Input
id='name-input'
name='name'
placeholder='first & last names'
onChange={setInputE2}
required={true}
type='text'
className='mb-2 bg-primary-9 text-primary-0 font-medium focus:outline-none rounded-md'
/>
<label htmlFor='subject'>{'Subject'}</label>
<Input
id='subject-input'
name='subject'
placeholder='Email subject...'
onChange={setInputE4}
required={true}
type='text'
className='mb-2 bg-primary-9 text-primary-0 font-medium focus:outline-none rounded-md'
/>
<label htmlFor='text'>{'Body'}</label>
<Textarea
id='text-textarea'
name='text'
placeholder='Email body...'
onChange={setInputE3}
required={true}
minLength={5}
cols={1}
className='mb-2 bg-primary-9 text-primary-0 font-medium focus:outline-none rounded-md'
/>
<div className='w-auto px-8 flex flex-col'>
<Button
type='submit'
variant='slim'
loading={loading}
disabled={disabled}
className={cn(
css.root,
'my-4 w-auto max-w-sm bg-primary-7 text-primary-0 hover:bg-primary-9 rounded-md duration-150 transition-colors'
)}
>
{'SUBMIT EMAIL'}
</Button>
</div>
<span className='pt-1 text-center text-sm'>
<span className='text-primary-9'>Interested in a Career?</span>
<a
className='text-primary-9 font-bold hover:underline cursor-pointer'
onClick={() => setModalView('SUBMIT_RESUME_VIEW')}
>
Submit a Resume
</a>
</span>
</div>
</form>
);
};
export default SendEmail;
pages/api/nodemailer.ts
import nodemailer, { SentMessageInfo } from 'nodemailer';
import { NextApiRequest, NextApiResponse } from 'next';
import secrets from 'aws';
import Mail from 'nodemailer/lib/mailer';
const {
SMTP_SENDER_ADDRESS,
SMTP_RECIPIENT_ADDRESS,
SMTP_PASSWORD,
SMTP_USERNAME,
SMTP_BCC_ADDRESS
} = secrets;
const senderAddress = SMTP_SENDER_ADDRESS;
const toAddress = SMTP_RECIPIENT_ADDRESS;
const ccAddress = SMTP_SENDER_ADDRESS;
const bccAddress = SMTP_BCC_ADDRESS;
const smtpUsername = SMTP_USERNAME;
const smtpPassword = SMTP_PASSWORD;
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { text, subject, name, email } = req.body;
try {
const body_subject = `Contact Us Submission Event - ${subject}`;
const body_text = `Contact Us Form Submission via AWS SES & Nodemailer
---------------------------------------------------------
${text}
`;
const body_html = `<html>
<head></head>
<body>
<h1>${subject}</h1>
\n
<h2>Name: ${name}</p>
\n
<h2>email: ${email}</h2>
\n
<p>${text}</p>
</body>
</html>`;
let transporter = nodemailer.createTransport({
host: 'email-smtp.us-east-2.amazonaws.com',
port: 465,
secure: true,
auth: {
user: smtpUsername,
pass: smtpPassword
}
});
let mailOptions: Mail.Options = {
sender: senderAddress,
from: `${senderAddress}`,
to: toAddress,
cc: ccAddress,
bcc: bccAddress,
subject: body_subject,
text: body_text,
html: body_html
};
let response: SentMessageInfo = transporter.sendMail(
mailOptions,
(info, err) => {
if (!err)
console.log(
'\n info.message: ',
info?.message,
'\n info.stack: ',
info?.stack,
'\n info.name: ',
info?.name
);
console.log(err);
return info;
}
);
if (response === typeof Error) {
return res.status(400).json({
error:
'There was an internal error βš™... \n Shoot me an email at [Mary.Drisdell#drisdellconsulting.com]'
});
}
return res.status(200).json({ error: '', data: response ?? '' });
} catch (error) {
return res.status(500).json({ error: error.message || error.toString() });
}
};
Vercel Dev CLI
While troubleshooting I came across the vercel dev command that can be used locally to test api routes in a vercel environment with the next framework. That said, I have been utilizing this new dev script and it results in a near-instant delivery to targeted addresses with no errors all the same...
Vercel real-time serverless function log for /api/nodemailer route after sending an email that was never delivered but has also never bounced πŸ€”
Any thoughts? I have tried just about everything including quadruple checking my environmental secrets in production. The DNS records tied to the primary domain for SES match up, as does the unlimited AWS SES Access Programmatic IAM policy I've configured. Any help would be tremendously appreciated as I have been troubleshooting this daily since last weekend.
The live production site where you can send an email is here.

Req.Body is empty object on Angular 2 Form submission + Bodyparser

I have a reactive (model driven) form in my application and I'm having trouble getting the form data through to mongoose. When I submit the data via http post method, I keep getting an error because the backend is receiving an empty object.
I know the backend works because when I submit a post request with Postman, I get a result status of "201 Created". I've been reading the forums and various blogs for the past few days and I'm still stumped.
Questions
What do I need to change to get the form data to hit the backend
appropriately?
Is there a best practice for Angular2 Form data submission?
Notes
It may look like there are some things missing, such as the URL, and you will notice some additional fields in the form. I purposely did not include all of these for conciseness and obfuscated the actual links to my backend.
Backend - Mongoose
jobs.controllers.js
var mongoose = require('mongoose');
var Job = mongoose.model('Job');
module.exports.addNewJob = function(req, res){
console.log(req.body);//added to see req payload from client
Job
.create({
_id: req.body.jobid,
desc: req.body.name,
type: req.body.jobType,
location: req.body.location
},function(err, job){
if(err){
console.log("Error creating job");
res
.status(400)
.json(err);
}else{
console.log("Job Created", job);
res
.status(201)
.json(job);
}
});
}
jobs.model.js
var mongoose = require('mongoose');
var jobSchema = new mongoose.Schema({
_id: {
type: String
},
desc: {
type: String
},
type: {
type: String,
default: "TM"
},
location: String
});
mongoose.model('Job', jobSchema);
db.js
var mongoose = require ('mongoose');
var dburl = 'mongodb://localhost:27017/dbname';
mongoose.connect(dburl);
mongoose.connection.on('connected', function(){
console.log('Mongoose connected to ' + dburl);
});
mongoose.connection.on('disconnected', function(){
console.log('Mongoose disconnedted');
});
mongoose.connection.on('error', function(err){
console.log('Mongoose connection error: ' + err);
});
require('./jobs.model.js');
index.js
var express = require('express');
var router = express.Router();
var ctrlJobs = require('../controllers/jobs.controllers.js');
router
.route('/jobs')
.get(ctrlJobs.jobsGetAll)
.post(ctrlJobs.addNewJob);
module.exports = router;
app.js
require('./api/data/db.js');
var express = require('express');
var app = express ();
var path = require('path');
var bodyParser = require('body-parser');
var routes = require('./api/routes');
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.urlencoded({extended: false}));
app.use('/api', routes);//Goes to index.js
Front End - Angular2 Final
jobs.service
import { Injectable } from '#angular/core';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
#Injectable()
export class JobsService{
private _url = "http://123.456.789.100/api/jobs"; //removed actual address for post
constructor(private _http: Http){}
addNewJob(form: Object){
let headers = new Headers({'Content-Type':'application/json'});
let options = new RequestOptions({headers: headers});
return this._http
.post(this._url, JSON.stringify(form), options)
.map(this.extractData)
.catch(this.handleError);
}
private extractData(res: Response){
let body = res.json();
return body.data || { };
}
private handleError (error: Response | any) {
let errMsg: string;
if (error instanceof Response) {
const body = error.json() || '';
const err = body.error || JSON.stringify(body);
errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
} else {
errMsg = error.message ? error.message : error.toString();
}
console.error(errMsg);
return Observable.throw(errMsg);
}
}
job-form.component
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Router, ActivatedRoute } from '#angular/router';
import { JobsService } from './jobs.service';
#Component({
templateUrl: './job-form.component.html',
providers: [JobsService]
})
export class JobFormComponent implements OnInit {
id: string;
job: any;
jobForm: FormGroup;
title: string;
constructor(
private _fb: FormBuilder,
private _router: Router,
private _route: ActivatedRoute,
private _jobsService: JobsService
){
}
ngOnInit(): void {
this.jobForm = this._fb.group({
jobid: ['', Validators.required],
name: ['', Validators.required],
jobType: ['', Validators.required],
location: ['', Validators.required]
});
this.id = this._route.snapshot.params['id'];
this.title = this.id ? "Edit Job" : "Create New Job";
}
save(form: any){
console.log(this.jobForm.value);
this._jobsService.addNewJob(form).subscribe((dataResponse) => {
console.log("Submitting" + dataResponse);
});
}
reset(){
this.jobForm.reset();
}
}
job-form.component.html
<form [formGroup]="jobForm" novalidate (ngSubmit)="save(jobForm.value)">
<div class="row">
<div class="col-xs-2">
<label>Job Number</label>
<input [ngModel]="job?.jobid ? job.jobid : ''" class="form-control" type="text" formControlName="jobid">
</div>
<div class="col-xs-8">
<label>Title</label>
<input [ngModel]="job?.name ? job.name : ''" class="form-control" type="text" formControlName="name">
</div>
<div class="col-xs-2">
<label>Type</label><br />
<select [ngModel]="job?.jobType ? job.jobType : ''"class="form-control" formControlName="jobType">
<option value="TM">T&M</option>
<option value="LS">Lump Sum</option>
</select>
</div>
</div>
<div class="row">
<div class="col-xs-2">
<label>Status</label><br />
<select class="form-control" formControlName="status" [ngModel]="job?.status ? job.status : ''">
<option value="Pending">Pending</option>
<option value="Active">Active</option>
<option value="On Hold">On Hold</option>
<option value="Complete">Complete</option>
<option value="Closed">Closed</option>
</select>
</div>
<div class="col-xs-5">
<label>Location</label>
<input [ngModel]="job?.location ? job.location : ''" class="form-control" type="text" formControlName="location">
</div>
</div>
<br />
<div class="row">
<div class="col-xs-1">
<button type="btn btn-primary" class="btn btn-primary" [disabled]="!jobForm.valid">Submit</button>
</div>
<div class="col-xs-1">
<button class="btn btn-default" (click)="reset()">Reset</button>
</div>
</div>
</form>
Testing
With the headers set to application/json the req.body is an empty object
{}
When I set the headers to application/x-www-form-urlencoded the req.body shows
{ '{"jobid":"8746541","name":"asdfasdfasdfasdf","jobType":"LS","location":"asdfa"}':''}
on submit
XHR
PostmanSuccess
in a file jobs.service you are setting Header to
'Content-Type':'application/json'
in a file index.js you are handling body request
app.use(bodyParser.urlencoded({extended: false}));
so there are two solution you can do.
1.) Either change in a jobs.service
'Content-Type': 'application/x-www-form-urlencoded'
2.) Or change in index.js
app.use(bodyParser.json());
Solution
Added line app.use(bodyParser.json()); to app.js. Essentially, this tells the application that it should natively understand JSON. Submitting the form in the application now results in a new record being added to mongodb.
In app.js add the following middlewares...
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
For more information visit this site:link!
1.At front end level:-
When using reactive driven forms
Pass the front end class file binding
object as getRawValue().
1.1 At front end to backend integration
such as service in which our definite
method when call we have to pass the
binded object with when saving includes
headers as though it was post method.
Add middleware by requiring bodyparser. Use use as a bodyparser.json.
Navigate to particular route in postman and first check with new data and post it.
Now bind with front end object.
What we are passing to post rest API method was object from front end side.
Happy ending πŸ˜ƒ

Resources