I'm trying to send over two pieces of text data from my React frontend to an Express backend but whenever I do the post command with Axios the body appears as {} in the backend and I cannot use it. My code is listed below.
Client (App.js):
import { useState, useEffect } from 'react';
import React from 'react'
import './App.css';
import Axios from 'axios'
function App() {
const [cocktailName, setCocktailName] = useState("");
const [cocktailMain, setCocktailMain] = useState("");
const submitRecipe = () => {
const recipeData = {"cocktailName": cocktailName, "cocktailMain": cocktailMain};
Axios.post('http://localhost:3001/api/insert', recipeData).then(() => {alert('successful insert');});
}
return (
<div className="App">
<h1>CRUD Application Test</h1>
<div className='InputForm'>
<label> Cocktail Name: </label>
<input type="text" name="Cocktail Name" onChange={(e)=>
{setCocktailName(e.target.value);}}/>
<br></br>
<label> Cocktail Main Ingredient: </label>
<input type="text" name="Cocktail Main Ingredient" onChange={(e)=> {
setCocktailMain(e.target.value)}}/>
<br></br>
<button onClick={submitRecipe}>Submit</button>
</div>
</div>
);
}
export default App;
Server (App.js):
const app = express()
const mysql = require('mysql')
const bodyParser = require('body-parser')
const cors = require('cors')
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: 'password',
database: 'cruddatabase'
});
app.use(cors());
app.use(bodyParser.urlencoded({extended: true}));
app.post('/api/insert', (req, res)=> {
console.log(req.body)
const cocktailName = req.body.cocktailName;
const cocktailMain = req.body.cocktailMain;
console.log(cocktailName)
console.log(cocktailMain)
//This is where i'll eventually insert it into a database
const sqlInsert = "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)"
// db.query(sqlInsert, [cocktailName, cocktailMain], (err, result) => {
// console.log(err)
// });
});
app.listen(3001, () => {
console.log("running on port 3001")
});
Any help at all is greatly appreciated.
Change this line:
app.use(bodyParser.urlencoded({extended: true}));
With this one:
app.use(express.json());
Axios send a JSON when you give an object as data without specifying the Content-Type like you did. So urlencoded is not the right set here.
you need to have a res.send() somewhere within this block
app.post('/api/insert', (req, res)=> {
console.log(req.body)
const cocktailName = req.body.cocktailName;
const cocktailMain = req.body.cocktailMain;
console.log(cocktailName)
console.log(cocktailMain)
//This is where i'll eventually insert it into a database
const sqlInsert = "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)"
// db.query(sqlInsert, [cocktailName, cocktailMain], (err, result) => {
// console.log(err)
// });
});
Firstly to get a response from your backend, you need to specify what to receive by yourself, you can send the response as json by doing: res.json("..."). You should change the ... to any response you want to get back. And if you want to get your data back, you can put it there. You can also do res.send("...") to send a message after the request was completed
Secondly, you need to let your backend accept json data by adding this after the app variable.
app.use(express.json());
Lastly, I would encourage you to you async function to make your code looks cleaner. You can change your post request code to something like this and let me know if it works.
app.post("/api/insert", async (req, res) => {
try {
const { cocktailName, cocktailMain } = req.body;
const sqlInsert = await "INSERT INTO cocktail_recipes (cocktailName, cocktailMain) VALUES (?,?)";
} catch (e) {
console.log(e.message);
}
});
Related
//This is frontend
import React, { useState } from "react";
import axios from "axios";
import "./App.css";
const App = () => {
const [logFile, setLogFile] = useState<File | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLogFile(e.target.files![0]);
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const formData = new FormData();
formData.append("logFile", logFile!);
console.log(formData);
const response = await axios.post(
"http://localhost:3001/logs",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
);
console.log(response.data);
} catch (error) {
console.error(error);
}
};
return (
<>
<div className="App">
<h1>Upload file</h1>
<form onSubmit={handleSubmit}>
<div>
<input type="file" onChange={handleChange} />
</div>
<button type="submit">Submit</button>
</form>
</div>
</>
);
};
export default App;
//This is backend
const express = require('express');
const app = express();
const port = 3001;
const winston = require('winston');
const cors = require("cors")
app.use(cors())
const logger = winston.createLogger({
transports: [
new winston.transports.File({
level: 'error',
filename: 'error.log',
handleExceptions: true,
json: true,
maxsize: 5242880,
maxFiles: 5,
colorize: false
}),
new winston.transports.File({
level: 'warn',
filename: 'warning.log',
handleExceptions: true,
json: true,
maxsize: 5242880,
maxFiles: 5,
colorize: false
})
],
exitOnError: false
});
app.use(express.json());
app.get("/", (req, res) => {
res.json({ "message": req.body.logFile })
})
app.post('/logs', (req, res) => {
const logFile = req.body.logFile;
const errorLogs = [];
const warnLogs = [];
// Log error and warn messages to separate files
logFile?.forEach(log => {
if (log.level === 'error') {
logger.error(log);
errorLogs.push(log);
} else if (log.level === 'warn') {
logger.warn(log);
warnLogs.push(log);
}
});
res.json({ errorLogs, warnLogs });
});
app.listen(port, () => {
console.log(`Server is listening at http://localhost:${port}`);
});
I'm building a log parser to log error and warn levels using react js to upload file and return filtered data as json from express server. But I'm getting empty object for formData but logfile is showing uploaded fie in console in my react js frontend
I want to send a log file from frontend to backend and return filtered json data from backend to frontend
One issue with your code is that the form element doesn't have an onSubmit event, so the form is never submitted. To fix this, you can wrap your input element and submit button in a form element and add an onSubmit event that calls the handleSubmit function.
Another issue is that you're using the fetch API to make the POST request, which may not work correctly with a multipart/form-data content type. You can use the Axios library instead, which makes it easier to send binary data in the request body.
import React, { useState } from "react";
import axios from "axios";
import "./App.css";
const App = () => {
const [logFile, setLogFile] = useState<File | null>(null);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setLogFile(e.target.files![0]);
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
try {
const formData = new FormData();
formData.append("logFile", logFile!);
const response = await axios.post("http://localhost:3001/logs", formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
console.log(response.data);
} catch (error) {
console.error(error);
}
};
return (
<>
<div className="App">
<h1>Upload file</h1>
<form onSubmit={handleSubmit}>
<div>
<input type="file" onChange={handleChange} />
</div>
<button type="submit">Submit</button>
</form>
</div>
</>
);
};
export default App;
Edit:
The problem is that you are using req.body.logFile to access the uploaded file, but the file is being sent as a multipart/form-data request, not as a JSON request. You need to use a middleware that can handle multipart/form-data requests and extract the file.
To handle multipart/form-data requests in an Express application, you can use the multer middleware. Here is an example of how you can use it to extract the file from the request:
const multer = require('multer');
const storage = multer.memoryStorage();
const upload = multer({ storage });
app.post('/logs', upload.single('logFile'), (req, res) => {
const logFile = req.file;
...
});
In this example, the upload.single('logFile') will extract the file from the logFile field of the multipart/form-data request and store it in memory as a Buffer. You can then access the file using req.file
My fetch takes too long until it fails
I tried chrome, edge and postman
other fetch requests from pixabay api are working great
I compared the code to other projects I've made and found nothing
I also added a proxy to "package.json" as suggested on some places and it didnt work
posting below parts of my code:
controller:
import axios from 'axios'
export const getAll = async () =>{
const response = await axios.get('https://pixabay.com/api/?key=25540812-faf2b76d586c1787d2dd02736')
.then(resp=>resp)
.catch(err=>console.log(err));
return response;
}
router:
import express from "express";
import * as homeController from '../controllers/home.controller.js'
const homeRouter = express.Router();
homeRouter.get('/all', homeController.getAll)
export default homeRouter
indexjs:
import express from "express"
import dotenv from "dotenv"
import homeRouter from './routers/home.router.js'
dotenv.config();
const PORT = 3000 //process.env.PORT
console.log(PORT);
const app = express();
app.use(express.json());
app.use(homeRouter)
app.listen(PORT, ()=>{console.log(`server is connected on port ${PORT}`)})
fetch:
const getAll = async()=>{
try {
const response = await fetch (`http://localhost:3000/all`)
console.log("hu");
if (!response.ok) {
throw new Error();
}
else{
console.log("ok");
}
const responseObj = await response.json();
console.log(responseObj);
}
catch (error) {
console.log(error);
}
}
useEffect(()=>{
getAll();
},[])
Posting the answer by #Jaromanda X for everyone to see:
"see this app.get('/', (req, res) => { ... where's you req and res ??? nowhere, that's where - hint: export const getAll = async (req, res) =>{"
Apparently EVERY controller made with express needs to send a response back (in the form of res.send)
Should be obvious but somehow I missed it
Thanks everyone!
I am currently having an issue with my React Application not working on any other browser other than Chrome. The Javascript loads just fine with no errors on Chrome and the application is currently fully deployed on Heroku (link: https://weathrd.herokuapp.com/).
In regards to my application, I have a search query set up in the "overview.js" component that creates a "get" request, with a parameter passed in that gets fed into the weather api I am using. Then, I retrieve the json information from the "/forecast" page and feed that back into "overview.js" to display on the screen.
I do not have any regex notation within any of my code, so I don't think that would be an issue here. I also have fully updated my Heroku deploy code and I do not think there is some sort of confusion on Heroku? Regardless, here is my server code, overview component code, and the error I am receiving on Safari:
server code:
const PORT = process.env.PORT || 8000;
const path = require('path');
const express = require('express');
const cors = require('cors');
const axios = require('axios');
require('dotenv').config();
const app = express();
app.use(cors());
app.use(express.static("public"))
app.get('/', (req, res) => {
res.json('hi');
});
app.get('/forecast', (req, res) => {
const options = {
method: 'GET',
url: `http://api.weatherapi.com/v1/forecast.json?`,
params: {
q: req.query.city,
key : process.env.REACT_APP_API_KEY,
days: '3',
api: 'no',
alerts: 'no',
},
};
axios.request(options).then((response) => {
res.json(response.data);
}).catch((error) => {
console.log(error);
});
});
app.listen(PORT, () => console.log(`Server running on http://localhost:${PORT} `))
Safari Error:
The error also mentions the component from which I am making the API request from "overview.js", so here is that code also:
overview.js
import React, {useState} from 'react';
import './overview.css';
import { RecentSearches } from '../Recent Searches/recentSearches';
import { Hourly } from '../Hourly/hourly';
import { Fiveday } from '../5 Day Forecast/fiveday';
import 'animate.css';
const axios = require('axios');
export function Overview() {
const [forecast, setForecast] = useState(null);
// this callback function receives the searched city entered from recentSearches and applies it to fetchForecast
const getSearch = (searchedCity) => {
fetchForecast(searchedCity);
};
async function fetchForecast(searchedCity) {
const options = {
method: 'GET',
url: 'https://weathrd.herokuapp.com/forecast',
params: {city: searchedCity}
};
axios.request(options).then((response) => {
console.log(response.data);
setForecast(response.data);
}).catch((error) => {
console.log(error);
})
};
return (
<div>
<div className='jumbotron' id='heading-title'>
<h1>Welcome to <strong>Weathered</strong>!</h1>
<h3>A Simple Weather Dashboard </h3>
</div>
<div className='container-fluid' id='homepage-skeleton'>
<div className='d-flex' id='center-page'>
<RecentSearches getSearch={getSearch}/>
<Hourly forecast={forecast}/>
</div>
</div>
<Fiveday forecast={forecast}/>
</div>
)
};
Thanks for any assistance!
I managed to store files on my pc using the HTML form action attribute and then handling this request on my Express webserver.
When I now try to replace this with an eventlistener on the submit button of the form instead of using the action attribute to send the post request I can not get it to work.
I get a error message 400 bad request.
Fetch
let form = document.querySelector('#uploadForm')
let inpFile = document.querySelector('#inpFile')
form.addEventListener('submit', async event => {
event.preventDefault()
const formData = new FormData()
formData.append('inpFile', inpFile.files[0])
fetch('http://myip/upload', {
method: 'POST',
headers: {
'Content-Type' : 'multipart/form-data'
},
body: formData
}).catch(console.error)
})
HTML
<form ref='uploadForm'
id='uploadForm'
method='post'
encType="multipart/form-data">
<input type="file" name="sampleFile" id="inpFile" />
<input type='submit' value='Submit' />
</form>
Express Server
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.post('/upload', (req, res) => {
let sampleFile = req.files.sampleFile
sampleFile.mv(__dirname + '\\files\\' + sampleFile.name, (err) => {
if (err)
return res.status(500).send(err)
res.send('File uploaded!')
})
})
According to your html and fetch code your express code should looks like this:
const express = require('express')
const app = express();
const path = require('path')
const things = require('./routes/things')
const fileUpload = require('express-fileupload')
app.use('/upload', fileUpload({
createParentPath: true
}));
app.post('/upload', (req, res) => {
const { inpFile } = req.files;
inpFile.mv(path.join(__dirname, 'files', inpFile.name))
.then(() => res.send('File uploaded!'))
.catch(err => res.status(500).send(err));
})
You need to bind middleware to the application:
app.use('/upload', fileUpload({
createParentPath: true
}));
And your file object should be in req.files.inpFile.
Also you need to remove headers from your fetch request.
How would I go about rendering a component after an axios POST request? I want to load a new component after the success response has been received from Stripe. I am trying to update the state of my component by adding a setState after receiving the response and load a simple div if the state has any values. The issue I am having is that component is not re-rendering when I use the setState.
Below is how I have a stripe component setup and the express server:
import StripeCheckout from 'react-stripe-checkout';
import axios from 'axios';
import './stripe-button.styles.scss';
import { createStructuredSelector } from 'reselect';
import { selectCurrentUser } from '../../redux/user/user.selectors';
import { setCurrentUser } from '../../redux/user/user.actions';
class StripeCheckoutButton extends React.Component {
constructor(props) {
super(props);
this.state = {
cardListBacklog: []
};
}
onToken = token => {
console.log(token);
const { cartItems, price } = this.props;
const priceForStripe = price * 100;
const orderSummary = cartItems.reduce(
(cartItemAll, cartItem) =>
(cartItemAll += cartItem.name + cartItem.quantity),
''
);
axios({
url: 'payment',
method: 'post',
data: {
amount: priceForStripe,
order: orderSummary,
token
}
})
.then(response => {
alert(
`Payment successful, ${response.data.success.billing_details.name}; please check your email for your receipt.`
);
this.setState({cardListBacklog: response.data});
})
.catch(error => {
console.log('Payment error: ', JSON.parse(error));
alert('There was an issue with your payment. Please try again!');
});
};
render() {
const publishableKey = 'pk_test_gxxxxxxxxxxxxxxxxxxxxxxxxxxx';
const { price } = this.props;
const priceForStripe = price * 100;
return (
this.state.cardListBacklog.length
?
<div>Payment Successful</div>
:
<StripeCheckout
label="Pay Now"
name="Ltd."
billingAddress
shippingAddress
image="https://i.imgur.com/vWgUzv.png"
description={`Your total is $${price} USD`}
amount={priceForStripe}
panelLabel="Pay Now"
token={this.onToken}
stripeKey={publishableKey}
label="Pay with 💳"
/>
);
}
}
export default StripeCheckoutButton;
Here is my Server.js:
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const path = require('path');
if (process.env.NODE_ENV !== 'production') require('dotenv').config();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const app = express();
const port = process.env.PORT || 5000;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
if (process.env.NODE_ENV === 'production') {
app.use(express.static(path.join(__dirname, 'client/build')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'));
});
}
app.listen(port, error => {
if (error) throw error;
console.log('Server running on port: ' + port);
});
app.post('/payment', (req, res) => {
const body = {
source: req.body.token.id,
amount: req.body.amount,
receipt_email: req.body.token.email,
description: req.body.order,
currency: 'usd'
};
stripe.charges.create(body, (stripeErr, stripeRes) => {
if (stripeErr) {
res.status(500).send({ error: stripeErr });
} else {
res.status(200).send({ success: stripeRes });
}
});
});
this.state.cardListBacklog.length
This is the issue. Its giving 0 before update, and undefined after its updated. Console log and check if its true.
this.state = {
cardListBacklog: false
};
and
this.setState({cardListBacklog: true});
should do the trick.
I guess, you expects stripeRes in the state, but you are receiving an object
{success: stripeRes} instead.
You response with an object from the server here
res.status(200).send({ success: stripeRes });
But on the client side in the state you expect the array, not an object.
this.state.cardListBacklog.length
Object doesn't have length property by default.
You should check something else on the client. Maybe you should update state
on success response like
this.setState({cardListBacklog: response.data.success });
This is not super cool, but should give you an idea that client side and server side expect different things.
You should rethink your API.
The idea to use flag of successful response here (https://stackoverflow.com/a/59011695/10559239) makes sense to you, if you doesn't want to use response data in near future. Good as a first step.
But the main problem, as I can see is inconsistency between server and client.