Using createProxyMiddleware for the same routes on FE and BE while avoiding CORS - express

I'm building an app using node.js on the server side and used create-react-app for the client.
When running locally I want to use render Home component for path '/home' like so:
<BrowserRouter>
<div>
<Route exact path="/home" component={Home} />
</div>
</BrowserRouter>
and on the server side I want to to use '/home' to get requests like so:
app.use('/home', require('./routes/home'))
where 'routes/home/' is an express router:
module.exports = router.get('/', (req, res) => {
res.send({ status: 200, data: 'Hello World!' })
})
The problem is that I got CORS error at first, so I added createProxyMiddleware in order to proxy the server responses:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
const portPrefix = 'localhost:3000'
const options = {
target: 'http://localhost:5000',
router: {
'dev.localhost:3000': 'http://localhost:5000'
}
}
app.use([`/home`], createProxyMiddleware(options));
};
But now, when I make a request from the client (port 3000) to '/home' the request is redirected to port 5000 and I get the res.send({...}) immediately (instead of rendering the Home component that is using axios to make the request and handle the response..)
My Home component:
import React, { useState, useEffect } from 'react'
import axios from 'axios'
axios.defaults.baseURL = 'http://localhost:5000'
const Home = () => {
const [loading, setLoading] = useState(true)
const [data, setData] = useState("")
useEffect(() => {
async function makeRequest() {
const res = await axios.get('/home')
setData(res.status === 200 ? res.data.data : "test string")
setLoading(false)
}
makeRequest()
}, [])
return (
<div className="container">
{ !loading && <h1>{data}</h1> }
Home Page
</div>
)
}
export default Home
I saw that there's a solution to avoid createProxyMiddleware and just add headers to the response:
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:3000"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
next();
});
My question are:
How will adding headers behave on production?
Is the headers solution the better solution for local development, since createProxyMiddleware is there to assist with that I guess.
If I decide to use createProxyMiddleware, how can I use the same routes for both client and server (e.g. '/home' to render Home component on FE and '/home' for get requests on BE)? because now it "skips" the client side and goes straight to the server.
Thanks a lot!

Related

Can't receive a response message when using proxy-middleware between react ui and express backend

I see that the request sent from the ui created using React is forwarded to the backend, but I can't get the response from the ui. There may be details that I missed as I am very new to these issues, thanks in advance :)
//react Login.js
function Login() {
const fetch = actions.fetchUser();
async function handleSubmit() {
try {
fetch();
} catch (err) {
console.error('err', err);
}
}
export default Login;
//index.js
import axios from 'axios';
export const fetchUser = () => async () => {
await axios.get('/api/login');
};
//setupProxy.js
const { createProxyMiddleware } = require('http-proxy-middleware');
module.exports = function (app) {
app.use(
['/api'],
createProxyMiddleware({
target: 'http://localhost:5000',
}),
);
};
//express app.js
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
const port = 5000;
app.use(cors());
app.use(bodyParser.json());
require('./routes/login')(app);
app.listen(port, () => {
console.log(`Example app listening on port ${port}`)
})
// espress login.js
module.exports = app => {
app.get('/api/login', (req, res) => {
console.error('express login');
res.send('login');
});
First of all, do not mix cjs and mjs import/exports.
second of all, you export your middleware but never register/use it. At least your code does not show that part.
Here is very minimal example how you can proxy your react UI via express.
const express = require('express');
const proxy = require('express-http-proxy');
const app = express();
app.get('/api', (req, res) => {
res.send({my: 'data'});
});
// register other routes here
app.use(proxy('http://127.0.0.1:3000'));
app.listen(5000, '0.0.0.0', () => {
console.log('Server is running at http://127.0.0.1:5000');
});
React app content will be available on http://127.0.0.1:5000 with your routes.
And http://127.0.0.1:5000/api will be your express route.
Note: I assume your react app runs on the port 3000

I cant post to localhost with axios help please?

I am trying to send data to localhost with axios post, But for some reason what ever I try, I get the same error
"not send Error: Network Error"
this is my front end code
import React, { Component } from 'react';
import axios from 'axios';
class App extends Component {
constructor(props) {
super(props);
this.state = { }
}
sendData =()=>{
var url ="http://localhost:9000/api"
var data={
code:"hello world",
}
axios.post(url,data)
.then(res => {
console.log("data send")
})
.catch(err => {
console.log("not send"+err)
console.error(err);
})
}
render() {
return (
<div>
<form>
<input type="text" name="data"/>
</form>
{this.sendData()}
</div>
);
}
}
export default App;
and this is my back end code
var express=require('express');
var app=express();
app.use(express.json());
app.post('/api', function(req,res){
res.json(req.body);
})
app.listen(9000);
Can you post the error as well. It is most likely to be A CORS issue. If you can call the API from A Rest Client like Postman and not browser it is a CORS Issue
Install cors package npm install cors
Update your backed to allow CORS
var express = require('express')
var cors = require('cors') // New
var app = express()
app.use(cors()) // New
app.use(express.json());
app.post('/api', function(req,res){
res.json(req.body);
})
app.listen(9000);

How do I send response object from my get request to the front end with Express and Axios?

I am trying to pull data from MongoDB to populate some timers in this app I'm building. However, I can't seem to send my response to the front end with Axios. Here is my route on the backend:
const express = require('express');
const router = express.Router();
const TimerModel = require('../models/Timer');
router.get('/', async (req, res) => {
try {
const timers = await TimerModel.find({});
console.log('Succesful get req', timers);
res.send(timers);
} catch (err) {
console.log(err.message);
res.status(500).send('Server Error');
}
});
module.exports = router;
My console.log in the try statement prints the correct data but I'm having issues with sending it to the front end. Here is the component:
import React, { useState, useEffect } from 'react';
import Timer from '../Timer/Timer';
import axios from 'axios';
import './Wrapper.css';
function Wrapper() {
//State effects
useEffect(() => {
axios
.get('/')
.then((res) => {
console.log(res);
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
});
const handleChange = (event) => {
setTitle(event.target.value);
};
const addTimer = () => {
const timer = <Timer title={title} key={timers.length} />;
let allTimers = timers.slice();
allTimers.push(timer);
setTimers(allTimers);
setTitle('');
};
return (
//JSX Code
);
}
export default Wrapper;
In the axios call I make, I get this weird object when I run console.log(res) and I get my index.html for the res.data. Why don't I have access to the timers object I made with my backend request? Isn't it being sent when I run the command res.send(timers) in my route?
You need to add your API url in axios request. Currently, axios is taking url of your React website that is why your response have index.html file of React website.
useEffect(() => {
axios
.get('api_url/')
.then((res) => {
console.log(res);
console.log(res.data);
})
.catch((err) => {
console.log(err);
});
});
You can save the result in a state like
`````````````
`````````````
const [time, setTimer] = useState(null)
useEffect(() => {
axios.get('/').then(res => setTimer(res.data)
}, [])
`````````````
`````````````
and then use time vairable where you want

How to use twilio with dynamic routes in nuxt

Hoping I can explain this clearly and someone has some insight on how I can solve this.
I am trying to enter a input then have a text message delivered to the number that was entered. That simple.
On the homepage, I have an input component with:
<template>
<form class="right-card" #submit.prevent="submit">
<input v-model="search" />
<button class="clear" type="submit" v-on:click="submit"></button>
</form>
</template>
With this function set as a method to pass the param
export default {
data () {
return {
search: ''
}
},
methods: {
submit: function (event) {
this.$router.push(`sms/${this.search}`)
}
}
}
Then I have a /sms page located in pages/sms/_sms.vue which is landed on once the form is submitted
<template>
<div>
<h1>Success Page {{phoneNumber}} {{$route.params}}</h1>
<KeyboardCard/>
</div>
</template>
<script>
import KeyboardCard from '~/components/KeyboardCard.vue'
import axios from '~/plugins/axios'
export default {
asyncData ({ params, error }) {
return axios.get('/api/sms/' + params.sms)
.then((res) => {
console.log(res)
console.log(params)
return { phoneNumber: res.data }
})
.catch((e) => {
error({ statusCode: 404, message: 'Sms not found' })
})
},
components: {
KeyboardCard
}
}
</script>
And finally within api/sms/sms.js I have this on express running.
(note my API keys are replaced with placeholder)
router.get('/sms/:sms', (req, res, next) => {
console.log('express reached')
const accountSid = 'ACCOUNTSIDPLACEHOLDER'
const authToken = 'AUTHTOKENPLACEHOLDER'
const client = require('twilio')(accountSid, authToken)
client.messages.create({
to: '14169190118',
from: '+16477993562',
body: 'This is the ship that made the Kessel Run in 14 parsecs?!'
})
.then((message) => console.log(message.sid))
})
How can I pass the parameter.sms within the to field in my /api/routes/sms.js
Expected: When user enters # into the input how can the api/sms/:sms be called dynamically to the number that was typed in the input component?
Thanks in advance if anyone see's whats going on here :)
Edit: I have my middleware defined in the nuxt.config file, like so:
serverMiddleware: [
// API middleware
'~/api/index.js'
]
and my api/index.js file has:
const express = require('express')
// Create express instnace
const app = express()
// Require API route
const sms = require('./routes/sms')
// Import API Routes
app.use(sms)
// Export the server middleware
module.exports = {
path: '/api',
handler: app
}
I guess this is more an Express.js related question than a Vue.js question.
You can use the passed sms param from your request, like this:
router.get('/sms/:sms', (req, res, next) => {
console.log('express reached')
const accountSid = 'ACCOUNTSIDPLACEHOLDER'
const authToken = 'AUTHTOKENPLACEHOLDER'
const client = require('twilio')(accountSid, authToken)
client.messages.create({
to: req.params.sms,
from: '+16477993562',
body: 'This is the ship that made the Kessel Run in 14 parsecs?!'
})
.then((message) => console.log(message.sid))
})

Expected behaviour on page refresh with react router and express?

I am hoping this is a simple question. I have an express server with one route.
var express = require('express');
var app = express();
var path = require('path');
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname + '/src/index.html'));
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
The index.html links to the js below:
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, Route, Link, browserHistory } from 'react-router';
import Redux from 'redux';
var App = React.createClass({
render: () => {
return(
<div>React!
<Link to={`/page2`}>Page 2</Link>
</div>
);
}
});
var AnotherView = React.createClass({
render: () => {
return(
<div>Page 2
<Link to={`/`}>App</Link></div>
);
}
});
ReactDOM.render(
<Router history={browserHistory}>
<Route path="/" component={App}></Route>
<Route path="/page2" component={AnotherView}/>
</Router>,
document.getElementById('app')
);
I can click the links and the urls change when I go to localhost:3000 and start from there. The question is this, what should happen if I go to localhost:3000/page2 when using the router? Is it supposed to figure out that its supposed to show the AnotherView component and show it? I am getting "Cannot GET /page2" from express. If it is what do I need to do to make it do that?
app.get('*', function (req, res) {
res.sendFile(path.join(__dirname + '/src/index.html'));
});
The server should handle all the request, not only '/'.