How to HTTP API requests from Android to locahost
If you're developing a Cordova Android app and struggling to make fetch() calls to a localhost API endpoint, you're not alone.
This common issue stumps many developers, but the solution is straightforward once you understand the underlying problem.
The Problem: Localhost Isn't What You Think
In my recent project, I encountered this frustrating error when trying to call a local API endpoint:
const response = await fetch('http://localhost:3000/poc3/convert', {
method: 'POST',
body: formData,
headers: { 'Accept': 'audio/mpeg' },
});
// Error: TypeError: Failed to fetch
The confusing part? The API worked perfectly when:
- Testing directly from a browser on my computer
- Using the production URL (https://mywebsite/poc3/convert) from the app
Why This Happens
The key realization is that "localhost" refers to different machines depending on context:
- When running in an Android app (on device or emulator), localhost means the Android device itself
Since your API server is running on your development machine, not the Android device, the fetch request fails.
The Solution: Use Your Computer's IP Address
Here's how to properly configure your Cordova app to connect to your local development server:
1. Install the Advanced HTTP Plugin
First, add the cordova-plugin-advanced-http plugin which handles network requests more reliably in Cordova:
cordova plugin add cordova-plugin-advanced-http
2. Replace fetch() with the HTTP Plugin
Modify your request code to use the plugin instead of fetch:
async function uploadAndConvert(arrayBuffer, fileName) {
const formData = new FormData();
const audioBlob = new Blob([arrayBuffer], { type: 'audio/aiff' });
formData.append('file', audioBlob, fileName);
return new Promise((resolve, reject) => {
cordova.plugin.http.sendRequest(
'http://XXX.XXX.X.XX:3000/poc3/convert', // Use your computer's IP
{
method: 'post',
data: formData,
headers: { 'Accept': 'audio/mpeg' },
serializer: 'multipart',
responseType: 'arraybuffer'
},
(response) => {
if (response.status >= 400) {
let serverError = 'Unknown server error';
try {
serverError = String.fromCharCode.apply(null, new Uint8Array(response.data.slice(0, 256)));
} catch (e) {}
console.error(`Server Error ${response.status}:`, serverError);
reject(new Error(`Server responded with error ${response.status}`));
return;
}
resolve(new Blob([response.data], { type: 'audio/mpeg' }));
},
(error) => {
const errorMessage = error.error || error.message || JSON.stringify(error);
reject(new Error(`Request failed: ${errorMessage}`));
}
);
});
}
3. Find Your Computer's IP Address
On macOS, run:
ifconfig | grep "inet " | grep -v 127.0.0.1
On Windows, use:
ipconfig
Replace XXX.XXX.X.XX in the code with your actual IP address.
4. Update Content Security Policy
In www/index.html, update the CSP meta tag to allow connections to your local IP:
<meta http-equiv="Content-Security-Policy"
content="connect-src http://XXX.XXX.X.XX:3000">
5. Modify config.xml for Android
Add this to your config.xml to allow cleartext HTTP traffic (only for development!):
<platform name="android">
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:usesCleartextTraffic="true" />
</edit-config>
</platform>
Important Security Note
- Remove this setting before releasing your app
- Only connect to HTTPS endpoints in production
- Never hardcode local IP addresses in production code
The android:usesCleartextTraffic="true" setting disables HTTPS requirements, which is necessary for local development but should never be used in production. Make sure to:
Why This Works
- The app has permission to make network requests
- The app can use HTTP (not just HTTPS) for local development
- The Content Security Policy allows connections to your local server
By using your computer's actual IP address instead of localhost, your Android device knows to look for the API server on your development machine rather than on itself. The additional configuration changes ensure that:
Final Thoughts
This solution helped me overcome a frustrating development hurdle. Remember that mobile development often requires thinking differently about networking compared to web development. What works in a browser on your computer might need adjustment when running in a mobile app context.
If you'd like to see the original discussion or contribute alternative solutions, check out the Stack Overflow thread where this solution was documented.
Have you encountered similar networking challenges in Cordova or other mobile frameworks? Share your experiences in the comments below!