How to make a fetch request to localhost in React Native/Expo? - react-native

I'm creating a React Native/Expo app that uses Deno on the backend. I've created an API in the backend that I can run locally on localhost:4000. When I try to use fetch to call the API in the Expo app, however, I keep getting an error
[Unhandled promise rejection: TypeError: Network request failed] at node_modules/whatwg-fetch/dist/fetch.umd.js:535:17 in setTimeout$argument_0
Here is how I set up the backend
import { Application } from "https://deno.land/x/oak/mod.ts";
import { oakCors } from "https://deno.land/x/cors/mod.ts";
import { APP_HOST, APP_PORT } from "./config.ts";
import router from "./routes.ts";
import _404 from "./controllers/404.ts";
import errorHandler from "./controllers/errorHandler.ts";
const app = new Application();
app.use(oakCors());
app.use(errorHandler);
app.use(router.routes());
app.use(router.allowedMethods());
app.use(_404);
console.log(`Listening on port:${APP_PORT}...`);
And how I use fetch to call the API
const App = () => {
const getData = async () => {
const response = await fetch("http://localhost:4000/something");
const data = await response.json();
console.log(data);
};
useEffect(() => {
getData();
}, []);
return (
...
);
};
Note
Some answers on StackOverflow suggest fetching http://[IP_ADDRESS]:4000/something instead of localhost. I've tried that with no luck.
I've verified that the API is working. I can call it successfully in VSCode's Thunder Client and I can also see the data by going to http://localhost:4000 in the browser.

I found a solution to this issue. I'm running my Expo app on a physical device while my server is running on my computer, on localhost. It makes sense that I'm unable to make requests to localhost on my device, because localhost is not running there.
I fixed this issue by using ngrok, a tool that forwards localhost to a cloud URL, and fetching that URL in the app.

Use the local IP assign to your device, like: http://192.168.20.109:port/api/x
find the local IP using the command "ipconfig" in windows or ifconfig in linux

Related

SvelteKit app Vercel random function crashes

[GET] /proizvodi/9?fbclid=PAAaY2n3yQfX-Wzxs5pTMWeP9nMUkdIv2V4qg8kObIDQgUAdPnwLtWEr4ICYg_aem_AT5iXQ0y2WTFTVjFAkqIfOf-vx44GuIUXoJKxT5IunSH48Cf1QVjlfC8Q1rKapxuuR9daAL70GWwpLZ_rINriwy815oEYfwfXFvOuh3HvHBvhWRFkVdfeQom7PK6UZvTp0s
02:39:09:44
2023-01-26T01:39:09.507Z f0d6641c-416c-4c0a-ba67-c59890f6f936 ERROR [2023-01-26T01:39:09.507Z] #firebase/firestore: Firestore (9.15.0): Could not reach Cloud Firestore backend. Backend didn't respond within 10 seconds.
This typically indicates that your device does not have a healthy Internet connection at the moment. The client will operate in offline mode until it is able to successfully connect to the backend.
2023-01-26T01:39:09.539Z efb98c51-3cde-4791-981f-5d22145fe3c0 ERROR Unhandled Promise Rejection {"errorType":"Runtime.UnhandledPromiseRejection","errorMessage":"TypeError: fetch failed","reason":{"errorType":"TypeError","errorMessage":"fetch failed","cause":{"errorType":"Error","errorMessage":"Client network socket disconnected before secure TLS connection was established","code":"ECONNRESET","host":"translate.googleapis.com","port":443,"localAddress":null,"stack":["Error: Client network socket disconnected before secure TLS connection was established"," at connResetException (node:internal/errors:711:14)"," at TLSSocket.onConnectEnd (node:_tls_wrap:1593:19)"," at TLSSocket.emit (node:events:525:35)"," at endReadableNT (node:internal/streams/readable:1359:12)"," at process.processTicksAndRejections (node:internal/process/task_queues:82:21)"]},"stack":["TypeError: fetch failed"," at fetch (/var/task/node_modules/undici/index.js:105:13)"," at process.processTicksAndRejections (node:internal/process/task_queues:95:5)"]},"promise":{},"stack":["Runtime.UnhandledPromiseRejection: TypeError: fetch failed"," at process.<anonymous> (file:///var/runtime/index.mjs:1194:17)"," at process.emit (node:events:525:35)"," at emit (node:internal/process/promises:149:20)"," at processPromiseRejections (node:internal/process/promises:283:27)"," at process.processTicksAndRejections (node:internal/process/task_queues:96:32)"]}
Unknown application error occurred
Runtime.Unknown
App works perfectly on the localhost, but on when deployed to vercel, I am getting these functions crashes now and then, seems like it's random. It's happening on all urls.
This is really a bummer since it's an e-commerce website and it's ruining my conversion rate.enter image description here
I tried changing the adapter in svelte.config.js to #sveltejs/adapter-vercel hoping it would work but to no avail.
Vercel uses cloud functions which lose web socket connections when they are frozen (as they are after a few minutes of not running). To ensure it works you can create a new connection for each request (so inside load function or GET function).
// $lib/firebase.js
import { initializeApp } from "firebase/app";
const firebaseConfig = {
// FIREBASE_CONFIGURATION
};
export const getFirebaseApp = () => initializeApp(firebaseConfig);
// src/routes/example/+page.server.ts
import { getFirestore } from "firebase/firestore";
import { getFirebaseApp } from "$lib/firebase";
export const load = async ({ params, url }) => {
const app = getFirebaseApp();
const db = getFirestore(app);
// use database and give a response
})
Another idea is would be to use #google-cloud/firestore serverside instead of #firebase/firestore because it is specifically for the backend (see this tutorial).

React native how to send requests to localhost?

first time using React Native with Expo. I saw some tutorials on how to fetch something from locahost but nothing seems to work.
I'm making this simple get request in my backend
app.get("/", async (req, res) => {
res.send("Success");
});
app.listen(5000, () => console.log("Running on 5000"));
On the front end i have
const requestTest = async () => {
try {
const msg = await axios.get("http://192.168.0.29:5000/");
console.log(msg);
} catch (error) {
console.log(error);
}
};
What i tried so far:
Replaced http://localhost:5000/ for my ipv4 address 192.168.0.29, like http://192.168.0.29:5000/.
Since i'm using expo when i start the server it says Metro waiting on exp://192.168.0.29:19000
So i tried to fetch http://192.168.0.29:19000/, also didn't work. After starting the server, i ran this command line adb reverse tcp:5000 tcp:5000, also didn't work.
OBS:
When i connect my app on Expo GO, i'm connecting it through Tunnel, cause if i try to connect it via lan it throws a Network Error.

How do I mock server-side API calls in a Nextjs app?

I'm trying to figure out how to mock calls to the auth0 authentication backend when testing a next js app with React Testing Library. I'm using auth0/nextjs-auth0 to handle authentication. My intention is to use MSW to provide mocks for all API calls.
I followed this example in the nextjs docs next.js/examples/with-msw to set up mocks for both client and server API calls. All API calls generated by the auth0/nextjs-auth0 package ( /api/auth/login , /api/auth/callback , /api/auth/logout and /api/auth/me) received mock responses.
A mock response for /api/auth/me is shown below
import { rest } from 'msw';
export const handlers = [
// /api/auth/me
rest.get(/.*\/api\/auth\/me$/, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
user: { name: 'test', email: 'email#domain.com' },
}),
);
}),
];
The example setup works fine when I run the app in my browser. But when I run my test the mocks are not getting picked up.
An example test block looks like this
import React from 'react';
import {render , screen } from '#testing-library/react';
import Home from 'pages/index';
import App from 'pages/_app';
describe('Home', () => {
it('should render the loading screen', async () => {
render(<App Component={Home} />);
const loader = screen.getByTestId('loading-screen');
expect(loader).toBeInTheDocument();
});
});
I render the page inside the App component like this <App Component={Home} /> so that I will have access to the various contexts wrapping the pages.
I have spent about 2 days on this trying out various configurations and I still don't know what I might be doing wrong. Any and every help is appreciated.
This is probably resolved already for the author, but since I ran into the same issue and could not find useful documentation, this is how I solved it for end to end tests:
Overriding/configuring the API host.
The plan is to have the test runner start next.js as custom server and then having it respond to both the next.js, as API routes.
A requirements for this to work is to be able to specify the backend (host) the API is calling (via environment variables). Howerver, access to environment variables in Next.js is limited, I made this work using the publicRuntimeConfig setting in next.config.mjs. Within that file you can use runtime environment variables which then bind to the publicRuntimeConfig section of the configuration object.
/** #type {import('next').NextConfig} */
const nextConfig = {
(...)
publicRuntimeConfig: {
API_BASE_URL: process.env.API_BASE_URL,
API_BASE_PATH: process.env.API_BASE_PATH,
},
(...)
};
export default nextConfig;
Everywhere I reference the API, I use the publicRuntimeConfig to obtain these values, which gives me control over what exactly the (backend) is calling.
Allowing to control the hostname of the API at runtime allows me to change it to the local machines host and then intercept, and respond to the call with a fixture.
Configuring Playwright as the test runner.
My e2e test stack is based on Playwright, which has a playwright.config.ts file:
import type { PlaywrightTestConfig } from '#playwright/test';
const config: PlaywrightTestConfig = {
globalSetup: './playwright.setup.js',
testMatch: /.*\.e2e\.ts/,
};
export default config;
This calls another file playwright.setup.js which configures the actual tests and backend API mocks:
import {createServer} from 'http';
import {parse} from 'url';
import next from 'next';
import EndpointFixture from "./fixtures/endpoint.json";
// Config
const dev = process.env.NODE_ENV !== 'production';
const baseUrl = process?.env?.API_BASE_URL || 'localhost:3000';
// Context
const hostname = String(baseUrl.split(/:(?=\d)/)[0]).replace(/.+:\/\//, '');
const port = baseUrl.split(/:(?=\d)/)[1];
const app = next({dev, hostname, port});
const handle = app.getRequestHandler();
// Setup
export default async function playwrightSetup() {
const server = await createServer(async (request, response) => {
// Mock for a specific endpoint, responds with a fixture.
if(request.url.includes(`path/to/api/endpoint/${EndpointFixture[0].slug}`)) {
response.write(JSON.stringify(EndpointFixture[0]));
response.end();
return;
}
// Fallback for pai, notifies about missing mock.
else if(request.url.includes('path/to/api/')) {
console.log('(Backend) mock not implementeded', request.url);
return;
}
// Regular Next.js behaviour.
const parsedUrl = parse(request.url, true);
await handle(request, response, parsedUrl);
});
// Start listening on the configured port.
server.listen(port, (error) => {
console.error(error);
});
// Inject the hostname and port into the applications publicRuntimeConfig.
process.env.API_BASE_URL = `http://${hostname}:${port}`;
await app.prepare();
}
Using this kind of setup, the test runner should start a server which responds to both the routes defined by/in Next.js as well as the routes intentionally mocked (for the backend) allowing you to specify a fixture to respond with.
Final notes
Using the publicRuntimeConfig in combination with a custom Next.js servers allows you to have a relatively large amount of control about the calls that are being made on de backend, however, it does not necessarily intercept calls from the frontend, the existing frontend mocks might stil be necessary.

React native axios is not working properly

I have this route on me BE
app.get('/welcomeRoute',(req,res)=> {
res.send("Hello world")
})
app.listen(4000,()=> {
console.log("Server is up")
})
I installed axios on my react native project with npm i axios , I created my project with expo init.
Here is how I use route
test = () => {
axios.get('http://localhost:4000/welcomeRoute').then((res)=> {
console.log('--------res', res);
}).catch((err)=> {
console.log('--------err', err);
})
}
Whenever i call this route i Always get --------err [Error: Network Error].
I tested this route in the postman and it is working fine.
Any suggestions please?
If the issue is on Android, the solution is to add below in in your AndroidManifest.xml file
android:usesCleartextTraffic="true"
In IOS, unencrypted network requests are blocked by default, i.e. https will work, http will not.
Your server from your emulator/device you need to access server via IP Address. Instead of http://localhost:port you should use http://your_ip_address:port
Actually in React Native you better give ip address of system rather than localhost because the device don't recognize your ip address using localhost

React Native with axios - Network error calling localhost with self signed https endpoint

I created service in Visual Studio with Conveyor extension to make it accessible in local network. I installed certificate on my Android device so there is green padlock when calling it in browser and service works fine.
However when calling it from React Native app with axios then i get Network error.
That's my code:
const fetchData = async () => {
console.log('url', `${Settings.API_URL}/location/GetDataByMyIp`);
try {
const result = await axios(
`${Settings.API_URL}/location/GetDataByMyIp`,
);
console.log('result', result);
ipData = {
city: result.data.city,
longitude: result.data.longitude,
latitude: result.data.latitude,
};
} catch (e) {
console.log('error', e);
}
};
await fetchData();
In console i see:
url https://192.168.1.14:45455/api/location/GetDataByMyIp
error Error: Network Error
at createError (path_to_app\node_modules\axios\lib\core\createError.js:16)
at EventTarget.handleError (path_to_app\node_modules\axios\lib\adapters\xhr.js:83)
at EventTarget.dispatchEvent (path_to_app\node_modules\event-target-shim\dist\event-target-shim.js:818)
at EventTarget.setReadyState (path_to_app\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:575)
at EventTarget.__didCompleteResponse (path_to_app\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:389)
at path_to_app\node_modules\react-native\Libraries\Network\XMLHttpRequest.js:502
at RCTDeviceEventEmitter.emit (path_to_app\node_modules\react-native\Libraries\vendor\emitter\EventEmitter.js:189)
at MessageQueue.__callFunction (path_to_app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:425)
at path_to_app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:112
at MessageQueue.__guard (path_to_app\node_modules\react-native\Libraries\BatchedBridge\MessageQueue.js:373)
I tried to add this line to AndroidManifest.xml application tag but still have the error
android:usesCleartextTraffic="true"
Some solutions say about adding as 2nd parameter to axios, but it doesn't work with React Native.
new https.Agent({
rejectUnauthorized: false,
});
I tried to call some other service from the internet and then error was gone.
Is there some solution to this? I haven't found anything more. My last idea is to host service on Azure so I'll have properly signed SSL, but i guess it has to be a way to run it locally.
Edit: It works through http