Can't make external API request on Android

Hey all, first time Capacitor user (and to some degree Android dev) here.

I’m trying to make an external request to another domain (api.example.com) but whenever I try running it on my Android phone though Android Studio (npx cap open android followed by hitting the “play” button" in Studio), the URL in the logcat says https://localhost/api.example.com/... and I get what amounts to a 404. I do not have this problem when running this app on the server (app.example.com)

The stack that I’m working with:
SvelteKit 2.0.0,
CapacitorJS: 6.0.0
Node: 20.11.1
Android Studio: 2023.3.1

{
  ...
  "devDependencies": {
		"@capacitor/cli": "^6.0.0",
		"@sveltejs/adapter-auto": "^3.0.0",
		"@sveltejs/adapter-node": "^5.0.1",
		"@sveltejs/adapter-static": "^3.0.1",
		"@sveltejs/kit": "^2.0.0",
		"@sveltejs/vite-plugin-svelte": "^3.0.0",
		"@types/eslint": "^8.56.0",
		"eslint": "^8.56.0",
		"eslint-config-prettier": "^9.1.0",
		"eslint-plugin-svelte": "^2.35.1",
		"prettier": "^3.1.1",
		"prettier-plugin-svelte": "^3.1.2",
		"sass": "^1.72.0",
		"svelte": "^4.2.7",
		"vite": "^5.0.3"
	},
	"dependencies": {
		"@capacitor/android": "^6.0.0",
		"@capacitor/core": "^6.0.0",
		"@capacitor/ios": "^6.0.0",
		"dexie": "^4.0.1",
		"uuid": "^9.0.1"
	},
  ...
}

I have tried doing the following in my capacitor.config.json file:

  • capacitor.config.json
    • adding the URL to the server.allowNavigation array as https://api.example.com/* (and without the glob)
    • setting server.clearText to true.
  • AndroidManifest.xml
    • adding the ACCESS_NETWORK_STATE and ACCESS_WIFI_STATE permissions (INTERNET was already there).
  • CapacitorHttp
    • I know this is for patching fetch to use native requests instead, but I was having issues in getting this to work with CSRF and POSTing on desktop browsing (didn’t bother trying to build it if I couldn’t get it to work on desktop). Going back to regular fetch works, so I’m sticking with it unless you all say this is the way to do it.

I admit I haven’t tried many solutions, but I also haven’t been able to find situations similar to mine on the web. I’ve been searching variations of “capacitor android cant make external requests” but I keep finding the same answers from several years ago recommending the above and how it solves their problems.

Is there some config I need to make to allow requests out from the app? Did I botch setting up Capacitor? How can I figure out more information to properly diagnose what’s going on?

I appreciate any help you all can lend!

It should just work :grin: Please share your code where you are making calls to your external API.

That’s what I was afraid of :sweat_smile: Time to break this out into a new project to see if I can isolate the issue without all of the extra app code. As an example, even doing a “simple” GET request runs into the same problem:

/**
 * Fetches a CSRF token from the server.
 * @returns {Promise<{parameterName: String, headerName: String, token: String}>} - A promise that resolves to the CSRF token.
 */
export function csrfToken() {
	return fetch(import.meta.env.VITE_API_URL + '/csrf', {
		mode: 'cors',
		credentials: 'include'
	}).then(res => res.json())
}

It feels like Capacitor is just appending the URL part in my fetch request to the host for the webview, but I’m not sure how to stop it from doing that.

It seems that VITE_API_URL is not set. Are you 1000% sure that env variable is set?

EDIT

Actually, you said it is api.example.com. Try setting that env var to https://api.example.com.

1 Like

@twestrick Thank you for being my rubber duck! :duck:

Turns out the issue was a typo all along. :man_facepalming: I forgot that I had, at some point during development, redeclared my environment variable on the script that built the app version, overriding the .env file I was using. The overriding variable was missing the second forward slash after the protocol, resulting in https:/api.example.com which I’m assuming fetch or Capacitor had no idea how to handle other than treat it as a relative URL instead of throwing an error. This didn’t affect my web build because I never overrode the environment variable on the script that built my web version.

So @twestrick, yes I am 1000% sure it’s set… just not correctly :upside_down_face:

Yes it is, it looks like below in a .env file that gets loaded when getting built

# Hopefully it's obvious, but api.example.com is not the actual domain, just an example.
VITE_API_URL=https://api.example.com 

The reason I know it’s loaded is because the URL turns into https://localhost/api.example.com/csrf. When building for the web environment, it uses what I would expect and works correctly: https://api.example.com/csrf.

One thing to note is that I don’t use Capacitor to build for my web environment, just whatever SvelteKit puts out for adapter-static, which ultimately amounts to a SPA. For the Android, I run the same build command (npm run build:spa which runs the follo:
1. npx cap copy (Should this be sync instead?)
2. npx cap open android

After that I just run Android Studio’s debugger with my phone connected to my computer via USB and it (appears to) sideloads the app onto my phone and I can use it until I need to make a fetch request from anything outside of the app.

1 Like