I'm having trouble sending a data from a form to my database using axios in my MERN stack app. The server seems to work fine as I can make the POST request using Postman, however when I try to submit I get an axios error in the console with a 404 status code. Any help would be appreciated and, yes, the server is running on port 3000.
import { useState } from "react";
import axios from "axios";
import { CircularProgress } from "@mui/material";
const CreateSpecForm = () => {
const [name, setName] = useState("");
const [method, setMethod] = useState("");
const [glass, setGlass] = useState("");
const [ice, setIce] = useState("");
const [garnish, setGarnish] = useState("");
const [ingredients, setIngredients] = useState([
{ name: "", amount: "", measurement: "" },
]);
const [image, setImage] = useState(null);
const [description, setDescription] = useState("");
const [isSubmitting, setIsSubmitting] = useState(false);
const handleNameChange = (event) => {
setName(event.target.value);
};
const handleMethodChange = (event) => {
setMethod(event.target.value);
};
const handleGlassChange = (event) => {
setGlass(event.target.value);
};
const handleIceChange = (event) => {
setIce(event.target.value);
};
const handleGarnishChange = (event) => {
setGarnish(event.target.value);
};
const handleDescriptionChange = (event) => {
setDescription(event.target.value);
};
const handleImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
setImage(event.target.files[0]);
}
};
const handleIngredientNameChange = (event, index) => {
const values = [...ingredients];
values[index].name = event.target.value;
setIngredients(values);
};
const handleIngredientAmountChange = (event, index) => {
const values = [...ingredients];
values[index].amount = event.target.value;
setIngredients(values);
};
const handleIngredientMeasurementChange = (event, index) => {
const values = [...ingredients];
values[index].measurement = event.target.value;
setIngredients(values);
};
const handleAddIngredient = () => {
const values = [...ingredients];
values.push({ name: "", amount: "", measurement: "" });
setIngredients(values);
};
const handleSubmit = async (event) => {
event.preventDefault();
setIsSubmitting(true);
try {
const formData = new FormData();
formData.append("name", name);
formData.append("method", method);
formData.append("glass", glass);
formData.append("ice", ice);
formData.append("garnish", garnish);
formData.append("description", description);
formData.append("image", image);
formData.append("ingredients", JSON.stringify(ingredients));
console.log(
name,
method,
glass,
garnish,
ice,
description,
JSON.stringify(ingredients),
image
);
const headers = {
"Content-Type": "multipart/form-data",
};
const response = await axios.post("/api/recipes", formData, { headers });
console.log(response.data);
} catch (error) {
console.error(error);
} finally {
setIsSubmitting(false);
}
};
return (
<div>
<h1>Create Recipe</h1>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="name">Name:</label>
<input
type="text"
id="name"
value={name}
onChange={handleNameChange}
/>
</div>
<div>
<label htmlFor="method">Method:</label>
<select id="method" value={method} onChange={handleMethodChange}>
<option value="shaken">Shaken</option>
<option value="stirred">Stirred</option>
<option value="built">Built</option>
<option value="thrown">Thrown</option>
<option value="Blended">Blended</option>
</select>
</div>
<div>
<label htmlFor="glass">Glass:</label>
<select id="glass" value={glass} onChange={handleGlassChange}>
<option value="coupe">Coupe</option>
<option value="rocks">Rocks</option>
<option value="flute">Flute</option>
<option value="wine">Wine</option>
<option value="snifter">Snifter</option>
<option value="collins">Collins</option>
<option value="hurricane">Hurricane</option>
</select>
</div>
<div>
<label htmlFor="ice">Ice:</label>
<select id="ice" value={ice} onChange={handleIceChange}>
<option value="cubed">Cubed</option>
<option value="crushed">Crushed</option>
<option value="none">None</option>
<option value="thrown">Thrown</option>
</select>
</div>
<div>
<label htmlFor="garnish">Garnish:</label>
<input
type="text"
id="garnish"
value={garnish}
onChange={handleGarnishChange}
/>
</div>
<div>
<label htmlFor="description">Description:</label>
<textarea
id="description"
value={description}
onChange={handleDescriptionChange}
/>
</div>
<div>
<label htmlFor="image">Image:</label>
<input type="file" id="image" onChange={handleImageChange} />
</div>
<div>
<label>Ingredients:</label>
{ingredients.map((ingredient, index) => (
<div key={index}>
<input
type="text"
placeholder="Name"
value={ingredient.name}
onChange={(event) => handleIngredientNameChange(event, index)}
/>
<input
type="text"
placeholder="Amount"
value={ingredient.amount}
onChange={(event) => handleIngredientAmountChange(event, index)}
/>
<select
value={ingredient.measurement}
placeholder="measurement"
onChange={(event) =>
handleIngredientMeasurementChange(event, index)
}
>
<option value="ml">ml</option>
<option value="oz">oz</option>
<option value="dash">dash</option>
<option value="brsp">brsp</option>
<option value="whole">whole</option>
</select>
</div>
))}
<button type="button" onClick={handleAddIngredient}>
Add Ingredient
</button>
<button onClick={() => setIngredients(ingredients.slice(0, -1))}>
Remove Last Ingredient
</button>
</div>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? <CircularProgress /> : "Create Recipe"}
</button>
</form>
</div>
);
};
export default CreateSpecForm;
and here is my server side code
const express = require("express");
const multer = require("multer");
const AWS = require("aws-sdk");
const sharp = require("sharp");
const mongoose = require("mongoose");
require("dotenv").config();
const app = express();
app.use(express.json());
// Connect to MongoDB
mongoose.connect(process.env.MONGODB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", function () {
console.log("Connected to MongoDB");
});
// Define recipe schema and model
const recipeSchema = new mongoose.Schema({
name: { type: String, required: true },
description: { type: String, required: true },
image: { type: String },
ingredients: { type: [String] },
glass: { type: String, required: true },
ice: { type: String, required: true },
garnish: { type: String, required: true },
method: { type: String, required: true },
});
const Recipe = mongoose.model("Recipe", recipeSchema);
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
});
const upload = multer({ dest: "uploads/" });
app.post("/api/recipes", upload.single("image"), async (req, res) => {
const { name, description, ingredients, method, ice, glass, garnish } =
req.body;
const imageFile = req.file;
try {
const compressedImage = await compressImage(imageFile);
const imageUrl = await uploadImageToS3(compressedImage);
const recipe = new Recipe({
name,
description,
image: imageUrl,
ingredients,
method,
ice,
glass,
garnish,
});
await recipe.save();
console.log(recipe);
res.status(201).json({ message: "Recipe created successfully" });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Error creating recipe" });
}
});
const compressImage = async (imageFile) => {
if (!imageFile) return null;
const maxWidthOrHeight = 500;
const maxFileSizeInMB = 5;
const imageMaxSizeInBytes = maxFileSizeInMB * 5024 * 5024;
const imageBuffer = await sharp(imageFile.path).toBuffer();
const metadata = await sharp(imageBuffer).metadata();
const resize =
metadata.width > metadata.height
? { width: maxWidthOrHeight }
: { height: maxWidthOrHeight };
const compressedImageBuffer = await sharp(imageBuffer)
.resize(resize)
.jpeg({ quality: 80 })
.toBuffer();
if (compressedImageBuffer.byteLength > imageMaxSizeInBytes) {
throw new Error("The compressed image is still too large.");
}
const compressedImage = {
buffer: compressedImageBuffer,
originalName: imageFile.originalname,
};
return compressedImage;
};
const uploadImageToS3 = async (image) => {
const bucketName = process.env.AWS_S3_BUCKET_NAME;
const objectKey = `recipe-images/${Date.now()}-${image.originalName}`;
const params = {
Bucket: bucketName,
Key: objectKey,
Body: image.buffer,
ContentType: "image/jpeg",
ACL: "private",
};
const s3Result = await s3.upload(params).promise();
return s3Result.Location;
};
app.listen(3000, () => {
console.log("Server started");
});
I've console.logged out all the variables being passed and they seem to match and I can make the POST request to MongoDB using Postman.
I think the problem can be that you're calling axios.post("/api/recipes",...)
but the URL should include the full server address together with the server port, or maybe there's some code that specifies the server that here is missing?
e.g. axios.post("<ip address or server name>:3000/api/recipes",...)