Trouble getting Express/React routes to work - node.js

I'm just learning Express/React and I'm trying to get set up with routes and basic database connections. I suspect I'm missing something very simple. I've tried to boil it down to the following.
Backend
server.js:
require('dotenv').config({path: '../.env'});
const mysql = require('mysql');
const express = require('express');
const bodyParser = require('body-parser');
const port = process.env.PORT || 5000;
const app = express();
const router = express.Router();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
const users = require('./routes/api/users');
app.use('/api/users', users);
const events = require('./routes/api/events');
app.use('/api/events', events);
const db = mysql.createConnection({
host: process.env.DB_HOST,
database: process.env.DB_NAME,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD
});
db.connect(function(err) {
if (err) throw err;
console.log('MySQL Connected!');
})
app.listen(port, () => console.log(`Listening on port ${port}`));
/routes/api/events.js:
const express = require('express');
const router = express.Router();
// GET api/events
router.get('/', (req, res) => {
res.send({id: "1", name: "hi"});
});
module.exports = router;
Frontend
App.js:
import React, { Component } from 'react';
import EventList from './components/EventList';
class App extends Component {
render() {
return (
<div className="App">
<EventList/>
</div>)
}
}
export default App;
/components/EventList.js:
import React, { Component } from 'react';
import axios from 'axios';
class EventList extends Component {
constructor() {
super();
this.state = {
events: []
}
}
componentDidMount() {
axios.get('/api/events')
.then(events => {
this.setState({events: events.data})
})
.catch(err => console.log(err))
}
render() {
var events = this.state.events;
return (
<div>
<p>Events:</p>
<ul>
{ events.map(({ id, name }) => (
<li>{name}</li>
))}
</ul>
</div>
)
}
}
export default EventList;
The error I get is http://localhost:3000/api/events 500 (Internal Server Error). What am I missing? I have truly scoured all of the docs I can find, but I'm not quite getting it.
Edit
I haven't changed anything, but now I'm getting a 404 (Not Found) instead. I had been getting a 500 for awhile, so it wasn't a momentary fluke. I'm not sure what could have changed.
Update
It may help to know that the .env variables I'm pointing to are actually for a remote MySQL database (i.e., DB_HOST != localhost, but a remote URL). Eventually, I'd like to connect the GET call on the events route to that db, but since it doesn't work with what I have here I figured my first issue to solve was upstream. As noted in comments, the PORT var I'm loading in is 3306. When I start the server, it says it's listening on 3306 as expected.

I think you are running your server on port 5000 and front on port 3000. if you request events with http://localhost:5000/api/events instead of /api/events, you would get 200 status code with your json data.
// as is
axios.get('/api/events')
// to be
axios.get('http://localhost:5000/api/events')

You could try typing componentDidMount function like this.
componentDidMount = async () =>{
//like this
}
In addition, I would recommend making the GET ALL its own function so you could just invoke in the componentDidMount function. whenever you run another CRUD action it will automatically update your events for you and instead of setting state every time you can invoke the this.getEvents() again to update it that way.
componentDidMount = async () =>{
this.getEvents()
}
Also you need to add this package (npm install cors) its so you can connect your api to your frontend.
Import it like this in your server.js file
const cors = require('cors')
app.use(cors())

You should add a proxy inside package.json in frontend like this.
"proxy": "http://localhost:5000",

Related

How to pass database connection in NodeJS

I'm using the express framework in NodeJS with typescript in the backend.
I have a very easy architecture in my index file:
index.html
import express = require("express");
import mongoClient = require("mongodb");
import apiRoutes from "./routes";
import { MONGO_CONNECTION } from "./config/mongo_config";
const app = express();
mongoClient.MongoClient.connect(MONGO_CONNECTION, { }, (err: any, mclient: any) => {
if (err) throw err;
const mongodb = mclient.db('test');
app.use('/api', isOnline, apiRoutes);
app.listen(80, () => console.log('API running on port 80'));
});
The express routes are separated in an other file (in my version it is separated in multiple file, just to keep it simple), here just an example:
routes/index.ts
import express = require("express");
import { Router } from "express";
const router = Router({mergeParams: true});
router.get('/example', (req: express.Request, res: express.Response) => {
res.json('Hello World');
});
export default router;
I don't want to use mongoose. So is there any way to pass the DB connection to another file without connecting again?
You can export an object in your index.js
export const mongodb = {};
And then instead of this:
const mongodb = mclient.db('test');
Use:
mongodb.connection = mclient.db('test');
or something like this.
Then other parts of the code can import it but make sure that it is not undefined before you use it because it may have not been initialized yet.
Another option would be to export a promise that would be resolved with a connection established:
export const mongodb = new Promise((resolve, reject) => {
// establish your connection here ...
resolve(mclient.db('test'));
// ...
);
And then in your importing code you will also use it with await.

ERR_CONNECTION_REFUSED for React and axios

I'm trying to build a instagram scraper with puppeteer and react that works with putting the username on an input and then I want to show the scraped data on the console, I already built the puppeteer script and It works, it returns the data correctly, But I have some issues trying to get the data from a post with axios, I'm using node js and express for my server, when I try to do the post with axios I keep getting an error.
I want to write the username on the input, then I want the puppeteer script to run, and then I want to console log the data that the puppeteer script returns
Error on console
POST http://localhost:4000/api/getData/username_im_scraping net::ERR_CONNECTION_REFUSED
This is my code
Server > index.js
const path = require("path");
const express = require("express");
const webpack = require("webpack");
const cors= require('cors');
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
const config = require(path.join(__dirname, "../webpack.config.js"));
const compiler = webpack(config);
const app = express();
const { script } = require("./script");
app.use(webpackDevMiddleware(compiler, config.devServer));
app.use(webpackHotMiddleware(compiler));
app.use(express.static(path.join(__dirname, '../build')));
app.use(cors());
app.get("/api/getData/:username", async (req, res) => {
console.log(`starting script for user ${req.params.username}`);
const data = await script(req.params.username);
console.log(`stopping script for user ${req.params.username}`);
res.send(data);
});
app.get('/*', (req, res) => {
res.sendFile(path.join(__dirname, '../build', 'index.html'));
});
app.listen(process.env.PORT || 4000, () => {
console.log('Server is listening on port 4000');
});
Homepage.js
import React, { useState } from "react";
import axios from "axios";
const Homepage = props => {
const [username, setUsername] = useState("");
const onChange = ({ target: { value } }) => setUsername(value);
const onClick = () => {
axios.post('http://localhost:4000/api/getData/' + username, {
header: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8;application/json' },
mode: "cors"
})
.then((response) => {
console.log(response);
})
.catch((error) => {
if (error.response) {
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
console.log(error.request);
} else {
console.log('Error', error.message);
}
})
};
return (
<div>
Time to start coding!
<input value={username} onChange={onChange} />
<button onClick={onClick}>Get instagram followers!</button>
</div>
);
};
export default Homepage;
The problem here is you defined your route with get like app.get("/api/getData/:username") but you are sending a post request. Either change the router to app.post or your axios method to axios.get.
UPDATE
Besides the changes above, after you shared the repository with me i checked and saw you have another problem, which was that you were not running your server so the ERR_CONNECTION_REFUSED message shown.
I forked your repository and made the following changes and created a PR for it. Please take a look at it, and if you want just merge it to your master branch.
P.S please for the next time create a .gitignore file and add node_modules to there so you don't push and pull your node_modules which takes some more amount of time.

Graphql call : Network error - failed to fetch for long tasks only

I am firing a query using on the react front-end which in turn triggers a long task on the server. The task is triggered again on the server (no fire visible on the network tab on front-end) and after some time the front-end throws a network error - failed to fetch.
All short task queries/mutations work just fine. I tried useLazyQuery, refetch from useQuery and creating a custom useLazyQuery as well. Same result for all as well as from the graphql playground.
I'm using apollo-server-express and the timeout set is 1200s. Following is my server.js
import express from "express";
import { ApolloServer } from "apollo-server-express";
import mongoose from "mongoose";
import fs from "fs";
import { resolvers } from "./graphql/resolvers/resolvers.js";
import { typeDefs } from "./GraphQL/typeDefs";
const cors = require("cors");
const startServer = async () => {
console.log("[Server] Creating Express Server");
var dir = "src/graphql/resolvers/audiofiles";
if (!fs.existsSync(dir)) {
console.log("[Server] Creating directory ", dir);
fs.mkdirSync(dir);
}
const app = express();
app.use(cors());
console.log("[Server] Creating ApolloServer");
const apollo = new ApolloServer({
typeDefs,
resolvers
});
apollo.applyMiddleware({ app });
console.log("[Server] Connecting to MongoDB");
await mongoose
.connect("mongodb://localhost:27017/database",
{
useNewUrlParser: true
}
)
.then(
app.listen(
{
port: 4000,
origin: {
requestTimeout: "1200s"
}
},
() =>
console.log(`Server ready at http://****:4000${apollo.graphqlPath}`)
)
)
.catch((err) => console.log(err));
};
startServer();
This is not a cors issue as it works for other queries but and for long tasks, fails after some time.
My front-end apollo-client has a fetch policy "no-cache" and I don't see a second call in the network tab. I think there would've been a third one if the front-end didn't get a network error. I'm sure this has got something to do with the front-end.
I want to understand whether this is a front-end problem or a server side problem. What is a good way to diagnose the problem?

Getting 404 (Not Found) on axios.delete()

I'm trying to implement the delete functionality for my survey creation app.
I'm using MongoDB with mongoose for the database, node.js/Express for the backend server and React/Redux for the frontend side.
Although I think I set routing correctly, I get 404 (Not Found) on axios.delete().
The error says that http://localhost:3000/api/surveys/delete/{the-survey-id-here} is not found.
I have been reading the documentation of axios, Express, mongoose and other websites, however, nothing worked for me.
I tried the following things.
Use findByIdAndRemove() instead of deleteOne()
Pass surveyId in the action creator const response = await axios.delete("/api/surveys", data: { surveyId });
Use <a></a> instead of <button></button>
Here are my codes.
SurveyList.js (react component which has the delete button)
import { fetchSurveys, deleteSurvey } from '../../actions'
...
<div className="card-action">
<a className="red-text text-accent-1">Yes: {survey.yes}</a>
<a className="red-text text-accent-1">No: {survey.no}</a>
<button
className="btn-floating btn-small waves-effect waves-light red right"
onClick={() => this.props.deleteSurvey(survey._id)}
>
<i className="material-icons">delete_forever</i>
</button>
</div>
...
const mapStateToProps = ({ surveys }) => {
return { surveys }
}
export default connect(
mapStateToProps,
{ fetchSurveys, deleteSurvey }
)(SurveyList)
actions/index.js (action creator)
export const deleteSurvey = (surveyId) => async dispatch => {
const response = await axios.delete(`/api/surveys/delete/${surveyId}`)
dispatch({ type: FETCH_SURVEYS, payload: response.data })
}
surveyRoute.js (routing handler)
app.delete('/api/surveys/delete/:surveyId', async (req, res) => {
await Survey.deleteOne({ _id: req.params.surveyId })
const surveys = await Survey.find({ _user: req.user.id }).select({
recipients: false
})
res.send(surveys)
})
server/index.js
const express = require('express')
const mongoose = require('mongoose')
const cookieSession = require('cookie-session')
const passport = require('passport')
const bodyParser = require('body-parser')
const keys = require('./config/keys')
require('./models/User')
require('./models/Survey')
require('./services/passport')
mongoose.connect(keys.mongoURI)
const app = express()
app.use(bodyParser.json())
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000, // milliseconds
keys: [keys.cookieKey]
})
)
app.use(passport.initialize())
app.use(passport.session())
require('./routes/authRoutes')(app)
require('./routes/billingRoutes')(app)
require('./routes/surveyRoutes')(app)
if(process.env.NODE_ENV === 'production'){
app.use(express.static('client/build'))
const path = require('path')
app.get('*',(req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
})
}
const PORT = process.env.PORT || 5000
app.listen(PORT)
In my case, leads/ is the end point. try your end point + survey id
With axios#^0.19.2
axios
.delete('/api/leads/' + id)
.then(res => {
dispatch({
type: DELETE_LEAD,
payload: id
});
So if I undestood correctly, you have a Expressjs App and a client app running on different ports, if so I think I found the issue
You are making ajax calls as if you had both frontend and backend running on the same server. Here are two possible solutions
Change the address of the Axios delete action to point to the right server if you are planning to run the backend and frontend on different servers
app.delete('http://localhost:5000/api/surveys/delete/:surveyId', async (req, res) => {
await Survey.deleteOne({ _id: req.params.surveyId })
const surveys = await Survey.find({ _user: req.user.id }).select({
recipients: false
})
res.send(surveys)
})
Off course you have to change the address once you deploy it to production and enable CORS
Build the client app for producion, this process will compile all css, js and html and save it to client/build folder as stated on line
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
This will cause the frontend and backend code to run on the same server
Thank you very much for all answers and comments. The problem was the proxy. I am using http-proxy-middleware to handle requests between different ports in the dev environment. All comments about ports made me realize that I need to check the 'setupProxy.js' file. I modified the code inside the file as below and that fixed the problem.
setupProxy.js
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy ('/auth/google', { target: 'http://localhost:5000' }))
app.use(proxy ('/api/**', { target: 'http://localhost:5000' }))
}
Thank you very much again for all the answers and comments.
CORS (Delete verb) must be enabled in your route middleware.
This thread may help you.

Problems with displaying images from MongoDB using Multer on ReactJS

Basically to summarize my problem: I have some image to be displayed and it's displayed on localhost:5000 with status 200 where my Express server is running and when it comes to localhost:3000 i.e. my React Development Server, I made a request using Axios and it does give me gibberish and I don't know how to handle it at all.
React Code:
componentDidMount() {
axios.get('/filesuploaded/video_______82395d6a5af4e98fb8efca56f0ae3c1b_____.jpeg')
.then(Response => console.log(Response))
.catch(err => console.log(err));
}
Express Code:
route.get('/:filename' , (req , res) => {
GridFS.files.findOne({filename: req.params.filename} , (err , file) => {
const readstream = GridFS.createReadStream(file.filename);
readstream.pipe(res);
})
});
Random Gibberish:
{data: "����..."
SOLUTION:
So I played around more with the code and I had forgotten that this existed in my package.json of my client side and I have used it to full potential and rewrote my server side code without using multer anywhere.
Package.json:
"proxy": "http://localhost:5000" //This helps React communicate with ExpressJS through ports.
Server Side Config:
const route = require('express').Router();
const mongoose = require('mongoose');
const GridFS = require('gridfs-stream');
//Route for getting files
route.get('/file/:id' , (req , res) => {
//Setting Up GridFS-Stream
const db = mongoose.connection.db;
const MongoDriver = mongoose.mongo;
const gfs = new GridFS(db , MongoDriver);
const readstream = gfs.createReadStream({
_id: req.params.id,
});
//Reading to Response
readstream.pipe(res);
});
module.exports = route;
Front End Config:
import React, { Component } from 'react'
export default class Files extends Component {
//Render Method
render() {
return (
<div>
<img src = {window.location.pathname} alt = "something" />
</div>
)
}
}
Here the window.location.pathname will translate to /file/:id and send a GET request to ExpressJS, hence loading the image!

Resources