React Native unable to run a successful fetch to express API - node.js

Given the following setup:
server.js
const express = require('express')
const path = require('path')
const app = express()
const cors = require('cors')
const port = process.env.PORT || 8080
app.use(cors())
app.get('/api', (req, res) => {
res.send({code: 200, message: 'I have arrived!'})
})
app.listen(port, () => console.log(`I can hear your thoughts on ${port}`))
and the presentational component with call:
App.js
componentDidMount() {
fetch(`/api`, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
}).then(res => {
console.log(res)
return res.json()
}).then(response => {
console.log(response)
this.data = response
}).catch(error => {
this.error = error.message || error.error
})
}
package.json:
"scripts": {
"start": "npm-run-all --parallel start:client start:server",
"start:client": "node node_modules/react-native/local-cli/cli.js start",
"start:server": "node server.js",
"test": "jest"
},
I am running the app via yarn start, and I see the log: "I can hear your thoughts"... The fetch call, however, is never made. If I supply another fully qualified url within the fetch call it returns the data as expected, however I am unable to hit the express api from within the component.
If I place http://localhost:8080/api in the browser I get the response.
If I place http://localhost:8080/api in the fetch call it is never called (or at least doesn't appear to be).
How can I properly configure this to call the express api when running locally?
As always any and all direction is appreciated, so thanks in advance!

I believe the issue for this is two fold.
Firstly you are running the server and the bundler in the same terminal window. I believe that this is causing them to get confused.
Run them in separate windows. Yes that means you have to run an extra command but it will mean that you will be able to clearly see the logs for each of them.
Also you may have to restart you bundler, especially when you add new packages which will mean restarting your server.
Similarly when you update your server you will have to restart it also causing you to restart your bundler. It doesn't seem like a good idea running them in the same window.
Secondly you are using the localhost for the api. This works nicely on your computer because the api is running on your computer's localhost so it can find it. However, when you run it on a device and you use localhost it looks for the api on your device's localhost and cannot find it there, thus it doesn't bring back a result.
Your solution is quite clear. Firstly, run your bundler and your server in different terminal windows. And secondly use the ip address of your computer so that the emulator/simulator can easily find where the api is located.
I copied your code and made only two changes to the react-native code.
Added alerts to show the response and error from the fetch request
Used my local ip address of my computer rather than localhost
Here are some images of me running it on both Android and iOS, using your code and the changes that I detailed above.
Android
iOS

Related

Basic express setup: not sending anything to local port

I created a frontend app and now trying to incorporate backend into it.
ON the same frontend app i added an index.js file in the root directory, and installed express and required it in index.js file.
Very basic setup as below:
const express = require('express')
const cors = require('cors')
const port = process.env.PORT || 3001
const app = express()
app.get('/', (req, res) => {
res.send({
greetings: 'hi'
})
})
app.listen(port, () => {console.log(`Server on port ${port}`)})
Server is successfully on port 3001 as per my terminal, however, on localhost:3001 I'm not seeing any json response I set up in app.get.
It says Cannot GET / instead. When i inspected in devtool(Network) it says 404.
This seems a very straightforward setup, but what could've gone wrong here?
i just figured why. I installed nodemon but my “start” script is “node index.js”. Should’ve used “nodemon index.js”
Working now with nodemon index.ks
Your code is fine, There are no errors, I tested it and it works as expected.
However few things to note, Keep Backend in Seperate folder/dirctory unless required.
Coming back to your question, There are many possiblity such as some modules are not installed properly
try running following command
//this will install if any library is currupt or not installed properly
npm i
if it doesn't work then try clearing cache
Also keep in mind, In nodeJS dev server does not automatically refresh changes, you need to restart server to see changes or you can use dev dependancy called Nodemon (this will auto restart server on saving changes)

Suddenly started encountering Internal Server Error 500 on Mac for React App with Express server

In my react app I have an API that I am calling from my redux-saga. I am using express for my server. The code has been hosted and is working just fine. But today, I deleted the build directory and reran npm run build and then npm start and started getting this error on my local Mac.
Error on Chrome:
The error in index.js expanded if it helps:
Error on Safari:
I am pretty sure my code is proper as it has been running just fine for months now and I have made no major changes right now expect adding some console.log statements.
My API in Express Server:
app.get("/api/getProblems", (req, res) => {
var sheetKey = SECRET_KEY;
tabletop.init({
key: sheetKey,
callback: (sheet) => {
res.json(sheet);
console.log("Sent list of problems");
},
simpleSheet: true,
});
});
Calling from Saga:
function* fetchProblems() {
const data = yield call(() =>
fetch("/api/getProblems")
.then(response => response.json())
.then(sheet => sheet)
);
//Rest of the logic
}
For a brief moment I was seeing the following error:
Could not proxy request /api/getProblems from localhost:3000 to http://localhost:8000 (ECONNREFUSED)
But then it was replaced by the above error.
It was my mistake in starting the server. I ran npm start in the wrong folder. From the root I had to run the following commands:
cd client; npm run build; cd ..; npm start;

My CRUD app works locally but not on Heroku

I've created a CRUD app and it works locally, but I can't get it to work fine on heroku. It deploys correctly, the website seems to work, but then I can't get the items I need from the database, as it keeps saying connection refused.
I added the .env variables to Heroku, as well as setting the port to process.env.PORT || 5000 and app.listen(port), I'm not sure what's causing the error. I also have a Procfile with web: node server.js, and a "start" script in package.json that points to server.js. It seems that the server doesn't start at all.
Here the repo in case you want to have a look https://github.com/ThomYorke7/inventory, here the app on heroku https://boardgamenerd.herokuapp.com/
The problem lies in the fact that your application has a backend (server) and a frontend (client) which are served differently locally than on Heroku.
I suppose locally your client is running on localhost:3000 (as it is the default with create-react-app you bootstrapped).
While your backend is running on localhost:5000, your client's package.json contains this line to make it work locally:
"proxy": "http://localhost:5000",
If I visit this page of your app: https://boardgamenerd.herokuapp.com/ > boardgames,
then I face these errors on the browser console:
boardgames-list.jsx:18
Error: Network Error
at e.exports (createError.js:16)
at XMLHttpRequest.p.onerror (xhr.js:83)
xhr.js:178
GET http://localhost:5000/boardgames/ net::ERR_CONNECTION_REFUSED
It tells you that your production version still calls backend on localhost:5000.
I.) First I'd try to fix these fetches by changing to relative URLs.
E.g. the above example (boardgames-list.jsx:18)
❌ your current script has hardcoded localhost fetch at the moment:
useEffect(() => {
axios
.get('http://localhost:5000/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
✔️ make it relative to root by removing "http://localhost:5000":
useEffect(() => {
axios
.get('/boardgames/')
.then((response) => {
setBoardgames(response.data);
setLoading(false);
})
.catch((err) => console.log(err));
}, []);
And it will work on Heroku. In case it wouldn't: see my suggestion below.
II.) Second, a suggestion:
Now your https://boardgamenerd.herokuapp.com/boardgames route uses the following backend endpoint to fetch data: https://boardgamenerd.herokuapp.com/boardgames/
The difference is only the last slash ("/") character which can be confusing and cause more issues later!
It is a best practice to add a differentiator path element to your backend endpoints, like /api/. For example: https://boardgamenerd.herokuapp.com/api/boardgames So you can be sure by first sight which GET request related to the backend and which one to the client.
If you'd go with this solution, you will need to add the following to your server.js:
app.use(express.static(path.join(__dirname, 'client', 'build')))
// required to serve SPA on heroku production without routing problems; it will skip only 'api' calls
if (process.env.NODE_ENV === 'production') {
app.get(/^((?!(api)).)*$/, (req, res) => {
res.sendFile(path.join(__dirname, 'client/build', 'index.html'))
})
}
/^((?!(api)).)*$/ regex skips URLs containing "api" in their path, so they won't be served static as the client/build folder's content - api calls won't be served from static and will work fine.

How to connect to MongoDB server and npm start at the same time for MERN app

I am making a MERN app and I notice that every time I want it to work, I have to first connect to the MongoDB server by "node server.js".
Then, I have to do "npm start" to run the react app which is connected to the server I just connected to.
Is there a way to do both of these actions using one command?
If you are using this for production, then you could use nodemon, which is a good tool that hot reloads your server and front end.
"scripts": {
"dev": "SET THIS=development & nodemon server.js",
},
Also, make sure you're server JS has a catch all that points to your app, such as:
server.get('*', (req, res) => {
return handle(req, res)
})
backend
- - server.js //with mongodb connection, and api routes.
//This is what you are going to use for your API calls from the front end. i.e http://backend.com/userapi/
frontend
- - server.js that contains the catch all statment. Also would add custom routes in here i.e
server.get('/posts/:id', (req, res) => {
return app.render(req, res, '/article', { id: req.params.id })
})

Axios (in React-native) not calling server in localhost

I'm building a really easy api and react-native application. The server works well (tested with PostMan) but the application doesn't call the server. It blocks when axios has to send the post request (see below).
I'm desperate :-( Loosing too mush time in it. Please, if you can help me...
Here is my code LogIn page. It dispatch the action creator (working with redux) giving email and password:
...
const LogIn = React.createClass({
submitLogin() {
// log in the server
if (this.props.email !== '' && this.props.psw !== '') {
if (this.props.valid === true) {
this.props.dispatch(logIn(this.props.email, this.props.psw));
} else {
this.props.dispatch(errorTyping());
}
}
},
...
email and password are weel retrieved and sent to the action creator:
import axios from 'axios';
import { SIGNIN_URL, SIGNUP_URL } from '../api';
// import { addAlert } from './alerts';
exports.logIn = (email, password) => {
return function (dispatch) {
console.log(email);
console.log(password);
console.log(SIGNIN_URL);
return axios.post(SIGNIN_URL, { email, password })
.then(
(response) => {
console.log(response);
const { token, userId } = response.data;
dispatch(authUser(userId));
}
)
.catch(
(error) => {
console.log('Could not log in');
}
);
};
};
const authUser = (userId) => {
return {
type: 'AUTH_USER',
userId
};
};
...
The three console.log() before axios show the data in the correct way. SIGNIN_URL is exactly the same I use in postman. ...but axios doesn't call.
Just to give all the cards, this is my store:
import thunk from 'redux-thunk';
import { createStore, compose, applyMiddleware } from 'redux';
import { AsyncStorage } from 'react-native';
import { persistStore, autoRehydrate } from 'redux-persist';
import reducer from '../reducer';
const defaultState = {};
exports.configureStore = (initialState = defaultState) => {
const store = createStore(reducer, initialState, compose(
applyMiddleware(thunk),
autoRehydrate()
));
persistStore(store, { storage: AsyncStorage });
return store;
};
There's no error message in the debugger (but the one given by the axios call ('Could not log in')
I'm on windows 10, with:
"axios": "^0.15.3",
"react": "15.4.2",
"react-native": "0.38.0",
"redux": "^3.6.0"
The call fails even when I prepare a simple GET call and the server is supposed to give back a simple message (tested with postman and browser):
exports.test = () => {
return function () {
return axios.get('https://localhost:3000/v1/test')
.then(
(response) => {
console.log(response);
}
)
.catch(
(error) => {
console.log('error');
}
);
};
};
Last, I tryed also to modify the call adding a header as the following, because the api is coded to accept json:
const head = {
headers: { 'Content-Type': 'application/json' }
};
exports.test = () => {
return function () {
return axios.get('https://api.github.com/users/massimopibiri', head)
.then(
(response) => {
console.log(response);
}
)
.catch(
(error) => {
console.log('error');
}
);
};
};
but even this didn't work. hope somebody can help me. Other similar issues didn't.
The solution came from a different source, but I post it here to help others looking for the same issue. Basically I used Android AVD (emulator) to build the application. But the emulator is in fact another machine, and that's why it couldn't call the localhost.
To solve the probleme, I had to send the request in the following way:
https://10.0.2.2:3000/v1/test
instead of:
https://localhost:3000/v1/test
if u are using mac this solution worked for me.
I am using React Native with Android Simulator ADV. Nexus Api 27
axios.get('http://192.168.1.21:8686/api/v1/test')
.then(response => console.log(response))
.catch(err => console.log(err));
where the ip 192.168.1.21 is from system preferences > Network > Advanced > TCP/IP > IPv4 Address
I also tested axios.get('http://10.0.2.2:8686/bec/api/v1/test') where 10.0.2.2 is localhost from virtual machine to the computer but not worked.
Your Emulator is a device on it's own that is not running on the same IP(localhost or 127.0.0.1) as your web browser, postman or your server.
In order to make request to your server from your emulator you need to access your server via your computer IP Address:
On windows get your IP Address by running ipconfig on the command prompt
On Unix terminal (Linux, Mac OS) run ifconfig to get your IP Address
Instead of http://localhost:port you should use http://your_ip_address:port
I didn't test it on Linux, Mac OS but its working perfectly on windows!
change from localhost to your ip
add http://
http://192.168.43.49:3000/user/
Another solution is to create a hosted network on the localhost computer with these commands from admin cmd:
netsh wlan set hostednetwork mode=allow ssid=wifi_name key=wifi_password
netsh wlan start hostednetwork
Connect your device to the computer's hotspot then get the computer's ip by running:
ipconfig
Now get the IPV4 address and put it on the app's axios/fetch url,
axios.get('https://192.168.137.1:3000/api')
.then(
(response) => {
console.log(response);
}
)
.catch(
(error) => {
console.log('error');
}
);
and it should now work!
I found another solution to the axios.get not working in react native problem:
Problem:- Object not visible and error: unhandled promise. Network error
Solution:-- Use below command:
=>>> npm install -g --save axios
instead of npm install --save axios i.e. Use -g.
And also check whether your emulator is having internet connection.
If your emulator is not having internet connection and is showing error such as: DNS probe finished bad config​., then
STOP ANDROID STUDIO Emulator and run these two commands in terminal
1.>> C:\Users\Admin\AppData\Local\Android\sdk\emulator\emulator.exe -list-avds​
My Output:
Pixel_XL_API_27
​
After this step, you will get the name of avds.
example:- Pixel_XL_API_27​
Now run below command:-
2.>> C:\Users\Admin\AppData\Local\Android\sdk\emulator\emulator.exe -avd Pixel_XL_API_27 -dns-server 8.8.8.8​
I have also faced similar issue and I am using Expo CLI for building and running my React Native application. My backend Express API are also running on same machine. So in my case Axios call is executing from inside Android Virtual Device emulator due to which localhost call is failing. So instead of using localhost I have used IP address and it worked!
If you are using expo client, please check hotspot IP address like 192.168.x.x (in my case ip is of this type) something on Metro Server Page.
If you are not using expo, then check your IP address using following commands :
ipconfig /all (On Windows)
ifconfig -a (OnLinux/Mac)
And then in axios api call, use http://192.168.x.x and if you are using https then use https but mostly for development purpose, you can go with http. But make sure in production environment, it is always good to use https with your domain or subdomain for providing additional security.
Alternate way to solve this issue if connecting a different way doesn't work. As others have said, because the phone is a different machine you can't use localhost. But you can use a tunnel. This will give you a url (open to the whole internet) that replicates your localhost.
Install the package localtunnel globally (yarn global add localtunnel)
Make sure your server is running, and note the port (for example, http://localhost:8081)
In another terminal/command prompt, run the localtunnel command (for me, this was npx localtunnel --port 8081). This will create your server that you can hit from the open web.
You can now replace the url in your react-native app with the url from the console
I hope this helps someone.
For me 'https://10.0.2.2:3000' was not working. I tried to map the localhost:3000 to a URL by ngrok and used that URL
./ngrok http 3000 (running this command on terminal will start session with global URL mapped to localhost port 3000)
Remeber,For this you should have ngrok installed on your system
Try Turning off your firewall it worked for me
Adding Cors to my Express App worked for me.
I did an npm install cors in the terminal in my Express server folder.
And then I just randomly added the following 2 lines of code to my Express server.js file:
const cors = require('cors');
app.use(cors())
Oh and then I specified the exact port in all my axios requests like below:
axios.post('http://localhost:5000/api/users/login/')
My problem: some ports were working for an expo app while the desired one 4000 wasn't. I wondered why. After much time researching, I found the workaround on Linux Mint: the Firewall.
Make sure you are connected over the same network and configured your server properly.
Go to Menu and search for Firewall in the machine hosting your server.
Turn status off or add a firewall rule through the plus icon at the left bottom corner.
Select Simple configuration and put your port there.
Done!

Resources