Connecting to multiple collections in MongoDB - node.js

I am trying to access two collections of my MongoDB for my React app. I have constructed two API_URLs:
const API_URL = window.location.hostname === 'localhost' ? 'http://localhost:5000/api/v1/Sessions' : 'production-url-here'
const API_URL1 = window.location.hostname === 'localhost' ? 'http://localhost:5000/api/v1/members' : 'production-url-here'
The sessions collection is returned with raw data when I put the url into my browser but the members URL returns a "not found" message.
I have mirrored my server and client side code of the sessions collection for my members collection and Im unsure as to what the issue is.
Here is the code for members.js:
const express = require('express');
const Joi = require("joi");
const db = require('../db');
const members = db.get('members');
//Defining the schema for the backend.
const schema1 = Joi.object().keys({
name: Joi.string().min(1).max(100).required(),
bio: Joi.string().min(1).max(500).required(),
latitude: Joi.number().required(),
longitude: Joi.number().required()
});
const router = express.Router();
//Gets all sessions that are found within the DB.
router.get('/', (req, res) => {
members
.find()
.then(allmembers =>{
res.json(allmembers);
});
});
module.exports = router;
Here is the code for Sessions.js :
const express = require('express');
const Joi = require("joi");
const db = require('../db');
const Sessions = db.get('Sessions');
//Defining the schema for the backend.
const schema = Joi.object().keys({
event: Joi.string().min(1).max(100).required(),
venue: Joi.string().min(1).max(500).required(),
address: Joi.string().min(1).max(100).required(),
dtstart: Joi.string().required(),
dtend: Joi.string().required(),
latitude: Joi.number().required(),
longitude: Joi.number().required()
});
const router = express.Router();
//Gets all sessions that are found within the DB.
router.get('/', (req, res) => {
Sessions
.find()
.then(allSessions =>{
res.json(allSessions);
});
});
//POST for when no errors are produced.
router.post('/', (req, res, next) => {
const result = Joi.validate(req.body, schema);
if (result.error == null) {
//Removes the need to write eg) req.body.name below.
const { event, venue, address, latitude, longitude, dtstart, dtend,} = req.body;
const Session = {
event,
venue,
address,
dtstart,
dtend,
latitude,
longitude,
date: new Date()
};
Sessions.insert(Session).then(insertedMessage => {
res.json(insertedMessage);
});
}
else {
next(result.error);
}
});
module.exports = router;
Frontend code:
import React, { Component } from 'react';
import './App.css';
import L from 'leaflet';
import Joi from 'joi';
//import only modules needed or error.
import { Map, TileLayer, Marker, Popup } from 'react-leaflet';
import { Card, CardTitle, CardText } from 'reactstrap';
import {Form, FormGroup, Label, Input } from 'reactstrap';
import { Button } from 'reactstrap';
import Chart from './components/Chart';
var myIcon = L.icon({
iconUrl: 'https://purepng.com/public/uploads/large/purepng.com-harpharpstringedsoundboardfingersmodern-1421526538276nepuu.png',
iconSize: [25, 51],
iconAnchor: [12.5, 51],
popupAnchor: [0, -51],
draggable: true,
});
//Joi creates the schema for validation
const schema = Joi.object().keys({
event: Joi.string().min(1).max(100).required(),
venue: Joi.string().min(1).max(500).required(),
address: Joi.string().min(1).max(100).required(),
dtstart: Joi.string().required(),
dtend: Joi.string().required()
});
const schema1 = Joi.object().keys({
name: Joi.string().min(1).max(100).required(),
bio: Joi.string().min(1).max(500).required(),
latitude: Joi.number().required(),
longitude: Joi.number().required()
});
//URL declaration, if hostname is localhost, request backend. otherwise URL.
const API_URL = window.location.hostname === 'localhost' ? 'http://localhost:5000/api/v1/Sessions' : 'production-url-here'
const API_URL1 = window.location.hostname === 'localhost' ? 'http://localhost:5000/api/v1/members' : 'production-url-here'
class App extends Component {
state = {
location: {
lat: 51.505,
lng: -0.09,
},
UserslocationFound: false,
zoom: 12,
/* Monitors the state of the users inputs (detects changes). */
UsersSession: {
event: '',
venue: '',
address: '',
dtstart: '',
dtend: ''
},
Sessions: [],
members: []
}
componentDidMount() {
//Grabs the markers from the Thesession API to be displayed.
fetch(API_URL)
.then(res => res.json())
.then(Sessions => {
this.setState({
Sessions
});
});
fetch(API_URL1)
.then(res => res.json())
.then(members => {
this.setState({
members
});
});
/*Asks user for location via google alert. */
navigator.geolocation.getCurrentPosition((position) => {
this.setState({
location: {
lat: position.coords.latitude,
lng: position.coords.longitude
},
UserslocationFound: true,
zoom: 15,
draggable: true
});
}, () => {
console.log("Location not given 😞");
fetch('https://ipapi.co/json')
.then(res => res.json())
.then(location => {
console.log(location);
this.setState({
location: {
lat: location.latitude,
lng: location.longitude
},
UserslocationFound: true,
zoom: 15
});
});
});
}
formSubmitted = (event) => {
/* prevents the page from refreshing on submit. */
event.preventDefault();
console.log(this.state.UsersSession);
const UsersSession = {
event: this.state.UsersSession.event,
venue: this.state.UsersSession.venue,
address: this.state.UsersSession.address,
dtstart: this.state.UsersSession.dtstart,
dtend: this.state.UsersSession.dtend
};
//importing Joi to get the result through validation of the inputs with the schema.
const result = Joi.validate(UsersSession, schema);
if(!result.error) {
//fetching against API_URL
fetch(API_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
...UsersSession,
latitude: this.state.location.lat,
longitude: this.state.location.lng,
})
}).then(res => res.json())
.then(Sessions => {
console.log(Sessions)
});
}
}
/*Updates the state on UsersSession */
valueChanged = (event) => {
/*declaring event.target as it throws errors in chrome */
const { name,value } = event.target;
/*Sets usersSession to be the value defined in inputs */
this.setState((prevState) => ({
UsersSession: {
...prevState.UsersSession,
[name]: value
}
}))
}
//Sharing of code between React components
render() {
const position = [this.state.location.lat, this.state.location.lng]
return (
<div className ="map">
<Map className ="map" center={position} zoom={this.state.zoom}>
/* tile imported to use over leafletjs*/
<TileLayer
attribution='&copy OpenStreetMap contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
/* displays marker for when users location is given/found */
{ this.state.UserslocationFound ?
<Marker
position={position}
icon={myIcon}>
</Marker> : ''
}
{this.state.Sessions.map(UsersSession => (
<Marker
position={[UsersSession.latitude, UsersSession.longitude]}
icon={myIcon} >
<Popup>
<em>{UsersSession.event}, </em>
{UsersSession.venue} {'\n'}
<Button color="primary" size="sm">More info</Button>
<Chart/>
</Popup>
</Marker>
))}
{this.state.members.map(Users => (
<Marker
position={[Users.latitude, Users.longitude]}
icon={myIcon} >
<Popup>
<em>{Users.name}, </em>
{Users.bio} {'\n'}
<Button color="primary" size="sm">More info</Button>
<Chart/>
</Popup>
</Marker>
))}
</Map>
<Card body className="message-form">
<CardTitle>Welcome to TradMap!</CardTitle>
<CardText>Please input the details of your Session below.</CardText>
<Form onSubmit={this.formSubmitted}>
<FormGroup>
<Label for="name">Session Title</Label>
<Input
/*when the state changes */
onChange={this.valueChanged}
type="text"
name="event"
id="event"
placeholder="..." />
<Label for="startDate">Start Date</Label>
<Input
onChange={this.valueChanged}
type="date"
name="dtstart"
id="dtstart" />
<Label for="EndDate"> End Date </Label>
<Input
onChange={this.valueChanged}
type="date"
name="dtend"
id="dtend" />
<Label for="venue">Session Venue</Label>
<Input
onChange={this.valueChanged}
type="textarea"
name="venue"
id="venue"
placeholder="..." />
<Label for="Address">Session Address</Label>
<Input
onChange={this.valueChanged}
type="textarea"
name="address"
id="address"
placeholder="..." />
</FormGroup>
<Button type ="submit" color="info" disabled={!this.state.UserslocationFound}>submit</Button>
</Form>
</Card>
</div>
);
}
}
export default App;

The issue was referencing the Members collection on the server side. I had not referenced it within the index.js file of the api directory:
const Members = require('./Members');
router.use('/Members', Members);
This then allowed for my collection to be referenced in my front-end and to be accessed through localhost.

Related

How do I send data from React to Node server using login form?

i'm new in react js, node js and express. I made a simple login form with username and password input form. In SignIn.js, i send username and password value to index.js ( node), but i can't get the value in index.js.
Here is the code :
SignIn.js
import Axios from 'axios';
import {
DribbbleOutlined,
TwitterOutlined,
InstagramOutlined,
GithubOutlined,
} from "#ant-design/icons";
function onChange(checked) {
console.log(`switch to ${checked}`);
}
const { Title } = Typography;
const { Header, Footer, Content } = Layout;
export default class SignIn extends Component {
constructor(props) {
super(props)
this.onChangeUsername = this.onChangeUsername.bind(this);
this.onChangePassword = this.onChangePassword.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state = {
name: '',
email: ''
}
}
onChangeUsername(e) {
this.setState({ username: e.target.value })
}
onChangePassword(e) {
this.setState({ password: e.target.value })
}
onSubmit(e) {
e.preventDefault()
}
render() {
const onFinish = (values) => {
const username = this.state.username;
const password = this.state.password;
Axios.post("http://localhost:3001/signin", {
username : username,
password : password,
}).then((Response)=> {
console.log(Response);
});
console.log("Success:", values);
};
const onFinishFailed = (errorInfo) => {
console.log("Failed:", errorInfo);
};
return (
<>
<Layout className="layout-default layout-signin">
<Header>
<div className="header-col header-brand">
<h5></h5>
</div>
<div className="header-col header-nav">
<h5>TITLE HERE</h5>
</div>
<div className="header-col header-btn">
</div>
</Header>
<Content className="signin">
<Row gutter={[24, 0]} justify="space-around">
<Col
xs={{ span: 24, offset: 0 }}
lg={{ span: 6, offset: 2 }}
md={{ span: 12 }}
>
<Title className="mb-15">Sign In</Title>
<Title className="font-regular text-muted" level={5}>
Enter your Username and password to sign in
</Title>
<Form
onFinish={onFinish}
onFinishFailed={onFinishFailed}
layout="vertical"
className="row-col"
>
<Form.Item
className="username"
label="Username"
name="username"
id="username"
rules={[
{
required: true,
message: "Please input your Username!",
},
]}
onChange={this.onChangeUsername}
>
<Input placeholder="Username" />
</Form.Item>
<Form.Item
className="username"
label="Password"
name="password"
id="password"
rules={[
{
required: true,
message: "Please input your password!",
},
]}
onChange={this.onChangePassword}
>
<Input placeholder="Password" />
</Form.Item>
{/*<Form.Item
name="remember"
className="aligin-center"
valuePropName="checked"
>
<Switch defaultChecked onChange={onChange} />
Remember me
</Form.Item>*/}
<Form.Item>
<Button
type="primary"
htmlType="submit"
style={{ width: "100%" }}
>
SIGN IN
</Button>
</Form.Item>
{/*<p className="font-semibold text-muted">
Don't have an account?{" "}
<Link to="/sign-up" className="text-dark font-bold">
Sign Up
</Link>
</p>*/}
</Form>
Here is the code for index.js (node)
const { Result, message } = require('antd');
const express = require('express');
const app = express();
var cors = require('cors');
const mysql = require("mysql");
const db = mysql.createPool({
host: 'localhost',
user: 'root',
password: '',
database: 'wynadvm'
});
app.use(cors());
app.get('/', function (req, res) {
res.send('hello');
});
app.post('/signin', (req,res) => {
const username = req.query.username;
const password = req.query.password;
db.query(
"SELECT * from users where username = ? and password = ?",
[username , password],
(err,result) => {
if(err) {
res.send({err:err});
}
if(result.length > 0) {
res.send('result.response');
console.log(req.query.username);
//return res.redirect('/UserHomePage');
}
else
{
res.send('{message : "Wrong Username/Password Combination"}');
console.log(req.query.username);
//return res.redirect('/dashboard');
}
}
)
});
app.listen(3001,() => {
console.log("running on port 3001");
});
my username and password const always show undefined in console log.
In Front End:
Edit your import Axios from "axios" to import axios from "axios", and rewrite like this:
axios.post("http://localhost:3001/signin", {
username : username,
password : password,
}).then((response)=> {
console.log(response);
}).catch(error => console.log(error))
In Back End, if you want get value from body request from Front end
easier, install body-parser. Example:
In terminal:
$ npm install body-parser
Add some line to your Back End code:
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
And change variable you asign from query to body like:
From:
const username = req.query.username; //<--- you can't get value from query while you post data request in body
const password = req.query.password;
To:
const username = req.body.username;
const password = req.body.password;
You can use axios for making http requests to the backend.
You can learn more about axios from here: https://www.npmjs.com/package/axios
And below is an example of how you can submit login request using axios
axios.post('/signin', {
username: 'Fred',
password: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
You are sending the data in body, but you are using query to get the username and password, that's why you are getting undefined, try using
const username = req.body.username;
const password = req.body.password;

ReferenceError: _id is not defined

Trying to pass in an _id to allow my comments to post to my front end React app. When I test express route in postman I don't get the comment . See screen shot below. When I try to post the comment I get the reference error _id id not defined.
Update:
The _id is fixed with id: data._id but I am not able to see my comments when I test in browser:
Here is backend express/node GalleryController.js
const mongoose = require('mongoose');
const Posts = mongoose.model('posts');
exports.PostGalleryInput = async (req, res) => {
console.log(req.body);
await new Posts({
body: req.body,
_id: req.params.id
}).save(async (err, data) => {
if (err) {
console.log('err:', err);
res.status(500).json({
message: 'Something went wrong, please try again later.'
});
} else {
res.status(200).json({
message: 'Post Created',
data,
id: _id
});
}
});
};
Here is Data Schema:
const mongoose = require('mongoose');
const PostSchema = new mongoose.Schema(
{
id: String,
title: String,
body: String,
name: String,
url: String,
comment: String
},
{
collection: 'posts'
}
);
module.exports = mongoose.model('posts', PostSchema);
Here is front end React component
import React, { useState, useEffect } from 'react';
import Container from 'react-bootstrap/Container';
import Card from 'react-bootstrap/Card';
import Button from 'react-bootstrap/Button';
import axios from 'axios';
import './css/sharewall.css';
const Cards = () => {
const [posts, setPosts] = useState([]);
const [comment, setComment] = useState('');
const loadPosts = async () => {
try {
let res = await axios.get(`http://localhost:5000/getall`);
setPosts(res.data.reverse());
} catch (error) {
console.log(error);
}
};
function saveComment(e, _id) {
e.preventDefault();
axios({
method: 'POST',
url: 'http://localhost:5000/postinput',
data: {
_id: _id,
comment: comment
}
})
.then((res) => {
loadPosts();
})
.catch((err) => {
console.log(err);
});
}
const loadComment = async () => {
try {
let res = await axios.post('http://localhost:5000/postinput');
setComment(res.data.comment._id);
console.log(res.data.comment._id);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
loadPosts();
loadComment();
}, []);
return (
<div className="compoentclass">
<Container className="mt-5 ml-auto mr-auto">
<div className="text-center">
{posts.map(
(post) =>
post.url && (
<div key={post._id}>
<Card className="">
<Card.Img alt="" src={post.url} />
<Card.ImgOverlay className="overlay">
<Card.Title className="d-flex justify-content-center align-items-center">
<Card.Text className="cardStyle text-light">{post.body}</Card.Text>
</Card.Title>
</Card.ImgOverlay>
</Card>
<div>
<Card.Text>{post.comment}</Card.Text>
</div>
<textarea
className="comment text-center mt-3 mb-3"
onChange={(e) => setComment(e.target.value)}
name="comment"
type="text"
/>
<div className="d-flex justify-content-start mt-n3 mb-4">
<Button
className="shareButton"
variant="secondary"
onClick={(e) => saveComment(e, post._id)}
>
Comment
</Button>
</div>
</div>
)
)}
</div>
</Container>
</div>
);
};
export default Cards;

Does the schema have to match completely to return anything? (mongoose)

I have a very simple backend server and client looking to display data from a mongo db, there are documents in a collection called cards in a database called test.
When I run the regex from this file it gives me back one document, but when I search it through the api on the backend it gives me nothing, no error, just "true" and "data: " in the console.
cconst mongoose = require("mongoose");
const express = require("express");
var cors = require("cors");
const bodyParser = require("body-parser");
const logger = require("morgan");
const Data = require("./data");
const Card = require("./card");
const API_PORT = 3001;
const app = express();
app.use(cors());
const router = express.Router();
// this is our MongoDB database
const dbRoute =
"mongodb+srv://dbUser:PASSWORD#cluster0-jhfnc.mongodb.net/test?retryWrites=true";
// connects our back end code with the database
mongoose.connect(dbRoute, { useNewUrlParser: true });
let db = mongoose.connection;
db.once("open", () => console.log("connected to the database"));
// checks if connection with the database is successful
db.on("error", console.error.bind(console, "MongoDB connection error:"));
// (optional) only made for logging and
// bodyParser, parses the request body to be a readable json format
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(logger("dev"));
// this is our get method
// this method fetches all available data in our database
router.get("/getCardsByName/:cardName", (req, res) => {
const { name } = req.params.cardName;
Card.find({ name: { $regex: ".*" + name + ".*" } }, (err, cards) => {
if (err) return res.json({ success: false, error: err });
console.log("data: " + cards);
return res.json({ success: true, cards: cards });
});
});
// append /api for our http requests
app.use("/api", router);
// launch our backend into a port
app.listen(API_PORT, () => console.log(`LISTENING ON PORT ${API_PORT}`));
The client looks like this:
// /client/App.js
import React, { Component } from "react";
import axios from "axios";
class App extends Component {
// initialize our state
state = {
cards: [],
id: 0,
message: null,
intervalIsSet: false,
idToDelete: null,
idToUpdate: null,
objectToUpdate: null,
cardToSearch: null
};
// when component mounts, first thing it does is fetch all existing data in our db
// then we incorporate a polling logic so that we can easily see if our db has
// changed and implement those changes into our UI
componentDidMount() {
//this.getDataFromDb();
if (!this.state.intervalIsSet) {
let interval = setInterval(this.getDataFromDb, 1000);
this.setState({ intervalIsSet: interval });
}
}
// never let a process live forever
// always kill a process everytime we are done using it
componentWillUnmount() {
if (this.state.intervalIsSet) {
clearInterval(this.state.intervalIsSet);
this.setState({ intervalIsSet: null });
}
}
getCardByCardName = card_name => {
fetch(`http://localhost:3001/api/getCardsByName/${card_name}`)
.then(cards => cards.json())
.then(res => this.setState({ cards: res.cards }));
};
render() {
const { cards } = this.state;
return (
<div>
<ul>
{cards.length <= 0
? "No Cards Found"
: cards.map(card => (
<li style={{ padding: "10px" }} key={card.id}>
<span style={{ color: "gray" }}> id: </span> {card.id} <br />
<span style={{ color: "gray" }}> data: </span>
{card.name}
</li>
))}
</ul>
<div style={{ padding: "10px" }}>
<input
type="text"
style={{ width: "200px" }}
onChange={e => this.setState({ cardToSearch: e.target.value })}
placeholder="Card name"
/>
<button
onClick={() => this.getCardByCardName(this.state.cardToSearch)}
>
FIND
</button>
</div>
</div>
);
}
}
export default App;
And the schema looks like this:
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
// this will be our data base's data structure
const CardSchema = new Schema(
{
_id: Schema.Types.ObjectId,
armor: Number,
artist: String,
attack: Number,
cardClass: String,
classes: Array,
collectible: Boolean,
collectionText: String,
cost: Number,
dbfld: Number,
durability: Number,
elite: Boolean,
entourage: Array,
faction: String,
flavor: String,
health: Double,
hideStats: Boolean,
howToEarn: String,
howToEarnGolden: String,
id: String,
mechanics: Array,
multiClassGroup: String,
name: String,
overload: Number,
playRequirements: Object,
questReward: String,
race: String,
rarity: String,
referencedTags: Array,
set: String,
spellDamage: Double,
targettingArrowText: String,
text: String,
type: String
},
{ timestamps: true }
);
// export the new Schema so we could modify it using Node.js
module.exports = mongoose.model("Card", CardSchema);
It turns out I was using destructuring incorrectly. It should be:
router.get("/getCardsByName/:cardName", (req, res) => {
**const name = req.params.cardName;**
console.log(name);
Card.find({ name: { $regex: ".*" + name + ".*" } }, (err, cards) => {
if (err) return res.json({ success: false, error: err });
console.log("data: " + cards);
return res.json({ success: true, cards: cards });
});
});

ReactNative : variable for FormData() being logged as undefined in ReactNative application when i make an http request with a file

i have been trying to make a post request from my react native app on an android emulator but the object that i am sending in the body of the request is being vvalued as undefined,been trying alot to solve this issue but no success. Please help me
here is the code to my form in the app known as "API.js" its named so just for testing the API endpoints
import React, { Component } from "react";
import {
Text,
TextInput,
View,
TouchableHighlight,
StyleSheet,
Button,
FormData
} from "react-native";
import Permissions from "react-native-permissions";
import ImagePicker from "react-native-image-crop-picker";
import axios from "axios";
var styles = StyleSheet.create({});
var msg = "Select Image";
var data;
export default class API extends Component {
constructor(props) {
super(props);
this.state = {
name: "",
price: "",
size: "",
description: "",
image: "",
popularity: ""
};
}
FormDataFunc() {
let form = new FormData();
form.append("name", this.state.name);
form.append("price", this.state.price);
form.append("size", this.state.size);
form.append("description", this.state.description);
form.append("image", this.state.image);
form.append("popularity", this.state.popularity);
return form;
return form;
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
Submit() {
axios({
method: "post",
url: "http://192.168.0.102:3000/products",
data: this.FormDataFunc,
headers: {
Accept: "application/json",
"Content-Type": "application/x-www-form-urlencoded"
},
body: this.FormDataFunc
})
.then(() => {
console.log("DONE!");
this.props.navigation.navigate("offersScreen");
})
.catch(err => {
console.log(err);
console.log(this.FormDataFunc);
});
}
componentDidMount() {
Permissions.check("photo").then(response => {
// Response is one of: 'authorized', 'denied', 'restricted', or 'undetermined'
console.log(response);
});
}
render() {
const image = () => {
ImagePicker.openPicker({
multiple: false,
includeBase64: true
}).then(images => {
console.log(images);
this.setState({
images: { data: `\`${images.mime}\`;base64,\`${images.data}\`` }
});
console.log(this.state.images);
msg = "Selected";
});
};
return (
<View>
<TextInput placeholder="Name" name="name" />
<TextInput placeholder="Price" name="price" />
<TextInput placeholder="Size" name="size" />
<TextInput placeholder="Description" name="description" />
<TouchableHighlight onPress={image} name="image">
<Text>{msg}</Text>
</TouchableHighlight>
<TextInput placeholder="Popularity" name="popularity" />
<TouchableHighlight title="Submit" onPress={this.Submit}>
<Text>Send</Text>
</TouchableHighlight>
</View>
);
}
}
here is the code to my backend route "product.js" which recieves the request(node.js and express)
const express = require("express");
const router = express.Router();
const bodyParser = require("body-parser");
const path = require("path");
const cloudinary = require("cloudinary");
const mongoose = require("mongoose");
//Product Model
const product = require("../models/product").product;
router.use(bodyParser.json());
//GET route
router.get("/", (req, res) => {
product.find().then(product => res.json(product));
});
// POST route
cloudinary.config({
cloud_name: "cloud222",
api_key: "783935981748815",
api_secret: "uY47vBS1rI2x5jvFtnXQIjMMqU0"
});
router.post("/", (req, res) => {
//MISC//
let imageUrl;
console.log("starting");
//MISC//
//CLOUDINARY UPLOAD
cloudinary.v2.uploader.upload(req.body.image, (error, result) => {
if (error) {
console.log(error);
console.log(`${req.body.image}`);
} else {
imageUrl = result.secure_url;
//MONGO UPLOAD
var productv = {
name: req.body.name,
price: req.body.price,
size: req.body.size,
description: req.body.description,
image: imageUrl,
popularity: req.body.popularity
};
const productvar = new product(productv);
productvar
.save()
.then(product => console.log(product))
.then(product => res.json(product))
.catch(err => console.log(err));
}
});
//END//
});
module.exports = router;
the data is being logged as undefined and therefore the request doesnt get a response
I think the problem is that you have created a FormData and wanted to post it, but the headers you added is "application/json". You should change it:
headers: {
'content-type': 'multipart/form-data',
'Accept':'multipart/form-data',
},
Hope it works for you.
Problem solved
Had to use onChangeText = instead of onChange as onChange does not return a string

Upload image file from React front end to Node/Express/Mongoose/MongoDB back end (not working)

I’ve spent most of a day looking into this and trying to make it work. This is an app with a React/Redux front end, and a Node/Express/Mongoose/MongoDB back end.
I currently have a Topics system where an authorized user can follow/unfollow topics, and an admin can Add/Remove topics.
I want to be able to upload an image file when submitting a new topic, and I want to use Cloudinary to store the image and then save the images path to the DB with the topic name.
The problem I am having is that I am unable to receive the uploaded file on the back end from the front end. I end up receiving an empty object, despite tons of research and trial/error. I haven’t finished setting up Cloudinary file upload, but I need to receive the file on the back end before even worrying about that.
SERVER SIDE
index.js:
const express = require("express");
const http = require("http");
const bodyParser = require("body-parser");
const morgan = require("morgan");
const app = express();
const router = require("./router");
const mongoose = require("mongoose");
const cors = require("cors");
const fileUpload = require("express-fileupload");
const config = require("./config");
const multer = require("multer");
const cloudinary = require("cloudinary");
const cloudinaryStorage = require("multer-storage-cloudinary");
app.use(fileUpload());
//file storage setup
cloudinary.config({
cloud_name: "niksauce",
api_key: config.cloudinaryAPIKey,
api_secret: config.cloudinaryAPISecret
});
const storage = cloudinaryStorage({
cloudinary: cloudinary,
folder: "images",
allowedFormats: ["jpg", "png"],
transformation: [{ width: 500, height: 500, crop: "limit" }] //optional, from a demo
});
const parser = multer({ storage: storage });
//DB setup
mongoose.Promise = global.Promise;
mongoose.connect(
`mongodb://path/to/mlab`,
{ useNewUrlParser: true }
);
mongoose.connection
.once("open", () => console.log("Connected to MongoLab instance."))
.on("error", error => console.log("Error connecting to MongoLab:", error));
//App setup
app.use(morgan("combined"));
app.use(bodyParser.json({ type: "*/*" }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
router(app, parser);
//Server setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);
console.log("server listening on port: ", port);
TopicController/CreateTopic
exports.createTopic = function(req, res, next) {
console.log("REQUEST: ", req.body); //{ name: 'Topic with Image', image: {} }
console.log("IMAGE FILE MAYBE? ", req.file); //undefined
console.log("IMAGE FILES MAYBE? ", req.files); //undefined
const topic = new Topic(req.body);
if (req.file) {
topic.image.url = req.file.url;
topic.image.id = req.file.publid_id;
} else {
console.log("NO FILE UPLOADED");
}
topic.save().then(result => {
res.status(201).send(topic);
});
};
router.js
module.exports = function(app, parser) {
//User
app.post("/signin", requireSignin, Authentication.signin);
app.post("/signup", Authentication.signup);
//Topic
app.get("/topics", Topic.fetchTopics);
app.post("/topics/newTopic", parser.single("image"), Topic.createTopic);
app.post("/topics/removeTopic", Topic.removeTopic);
app.post("/topics/followTopic", Topic.followTopic);
app.post("/topics/unfollowTopic", Topic.unfollowTopic);
};
CLIENT SIDE
Topics.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Loader, Grid, Button, Icon, Form } from "semantic-ui-react";
import {
fetchTopics,
followTopic,
unfollowTopic,
createTopic,
removeTopic
} from "../actions";
import requireAuth from "./hoc/requireAuth";
import Background1 from "../assets/images/summer.jpg";
import Background2 from "../assets/images/winter.jpg";
const compare = (arr1, arr2) => {
let inBoth = [];
arr1.forEach(e1 =>
arr2.forEach(e2 => {
if (e1 === e2) {
inBoth.push(e1);
}
})
);
return inBoth;
};
class Topics extends Component {
constructor(props) {
super(props);
this.props.fetchTopics();
this.state = {
newTopic: "",
selectedFile: null,
error: ""
};
}
onFollowClick = topicId => {
const { id } = this.props.user;
this.props.followTopic(id, topicId);
};
onUnfollowClick = topicId => {
const { id } = this.props.user;
this.props.unfollowTopic(id, topicId);
};
handleSelectedFile = e => {
console.log(e.target.files[0]);
this.setState({
selectedFile: e.target.files[0]
});
};
createTopicSubmit = e => {
e.preventDefault();
const { newTopic, selectedFile } = this.state;
this.props.createTopic(newTopic.trim(), selectedFile);
this.setState({
newTopic: "",
selectedFile: null
});
};
removeTopicSubmit = topicId => {
this.props.removeTopic(topicId);
};
renderTopics = () => {
const { topics, user } = this.props;
const followedTopics =
topics &&
user &&
compare(topics.map(topic => topic._id), user.followedTopics);
console.log(topics);
return topics.map((topic, i) => {
return (
<Grid.Column className="topic-container" key={topic._id}>
<div
className="topic-image"
style={{
background:
i % 2 === 0 ? `url(${Background1})` : `url(${Background2})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover"
}}
/>
<p className="topic-name">{topic.name}</p>
<div className="topic-follow-btn">
{followedTopics.includes(topic._id) ? (
<Button
icon
color="olive"
onClick={() => this.onUnfollowClick(topic._id)}
>
Unfollow
<Icon color="red" name="heart" />
</Button>
) : (
<Button
icon
color="teal"
onClick={() => this.onFollowClick(topic._id)}
>
Follow
<Icon color="red" name="heart outline" />
</Button>
)}
{/* Should put a warning safety catch on initial click, as to not accidentally delete an important topic */}
{user.isAdmin ? (
<Button
icon
color="red"
onClick={() => this.removeTopicSubmit(topic._id)}
>
<Icon color="black" name="trash" />
</Button>
) : null}
</div>
</Grid.Column>
);
});
};
render() {
const { loading, user } = this.props;
if (loading) {
return (
<Loader active inline="centered">
Loading
</Loader>
);
}
return (
<div>
<h1>Topics</h1>
{user && user.isAdmin ? (
<div>
<h3>Create a New Topic</h3>
<Form
onSubmit={this.createTopicSubmit}
encType="multipart/form-data"
>
<Form.Field>
<input
value={this.state.newTopic}
onChange={e => this.setState({ newTopic: e.target.value })}
placeholder="Create New Topic"
/>
</Form.Field>
<Form.Field>
<label>Upload an Image</label>
<input
type="file"
name="image"
onChange={this.handleSelectedFile}
/>
</Form.Field>
<Button type="submit">Create Topic</Button>
</Form>
</div>
) : null}
<Grid centered>{this.renderTopics()}</Grid>
</div>
);
}
}
const mapStateToProps = state => {
const { loading, topics } = state.topics;
const { user } = state.auth;
return { loading, topics, user };
};
export default requireAuth(
connect(
mapStateToProps,
{ fetchTopics, followTopic, unfollowTopic, createTopic, removeTopic }
)(Topics)
);
TopicActions/createTopic:
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile); //this is still here
// const data = new FormData();
// data.append("image", imageFile);
// data.append("name", topicName);
const data = {
image: imageFile,
name: topicName
};
console.log("DATA TO SEND: ", data); //still shows image file
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
When I send it like this, I receive the following on the back end:
(these are server console.logs)
REQUEST: { image: {}, name: 'NEW TOPIC' }
IMAGE FILE MAYBE? undefined
IMAGE FILES MAYBE? undefined
NO FILE UPLOADED
If I go the new FormData() route, FormData is an empty object, and I get this server error:
POST http://localhost:3090/topics/newTopic net::ERR_EMPTY_RESPONSE
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile);
const data = new FormData();
data.append("image", imageFile);
data.append("name", topicName);
// const data = {
// image: imageFile,
// name: topicName
// };
console.log("DATA TO SEND: ", data); // shows FormData {} (empty object, nothing in it)
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
Solution was to switch to using Firebase instead, and deal with image upload on the React client (this was attempted with cloudinary but with no success). The resulting download url can be saved to the database with the topic name (which is all I wanted from cloudinary) and now it is displaying the correct images along with the topics.

Resources