How to get two services actually to talk to each other

engma-linguisticsHOBBY

5 months ago

Project ID: 008e0a79-b0ff-47e6-87dc-90e0ea26533b

I have a React frontend (private github repo) and a FastAPI backend (another private github repo) (to any maintainers of Railway I can add you if you'd like to see the code.) Also a Postgres database but that doesn't come into play in this need for help.

Each appears up and healthy. I can get my FastAPI backend health check and navigate to its public URL. The backend shows the pages I'd expect without having any backend connection. All fairly simple, I hope.

In the frontend, there's a login page. Relevant (I hope) code follows. Note that I am not a frontend dev, I am proficient instead with Python and had a hefty amount of LLM help putting the frontend together; before decoupling my code into two codebases as I couldn't for the life of me get the monorepo approach to work (almost certainly a 'me' problem, not Railway), this did work locally with frontend talking to backend. Changes are hopefully somewhat minimal since then.

Frontend's LoginPage.tsx:
```import React, { useState, useEffect } from 'react';

const LoginPage: React.FC = () => {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);

useEffect(() => {
// Log the API URL when component mounts, this is from .env.production and I'm not using that right now.
console.log('VITEAPIURL:', import.meta.env.VITEAPIURL);
}, []);

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
setIsLoading(true);

// Ensure the API URL starts with https:// if it doesn't already include a protocol
// baseurl is just hardcoded here but I ought be able to get it from the project's environment variable set.
const baseUrl = "bactrian.railway.internal";
const apiUrl = baseUrl.startsWith('http') ? `${baseUrl}/login` : `https://${baseUrl}/login`;
console.log('Making request to:', apiUrl);

try {
  const response = await fetch(apiUrl, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ email }),
  });

  if (!response.ok) {
    throw new Error('Login failed');
  }

  const data = await response.json();
  localStorage.setItem('token', data.access_token);
  // Set authentication state in localStorage so it persists across page reloads
  localStorage.setItem('isAuthenticated', 'true');
  // Force a page reload to update the Layout component's auth state
  window.location.href = '/bookshelf';
} catch (err) {
  setError('Failed to login. Please try again.');
  console.error('Login error:', err);
} finally {
  setIsLoading(false);
}

};

return (

… truncated here…

export default LoginPage;

Attached pic is the dev console on the web. 
I also tried for the baseurl ```const apiUrl = `${import.meta.env.VITE_BACKEND_HOST}/login`;

and const baseUrl = import.meta.env.VITE_API_URL; const apiUrl = baseUrl.startsWith('http') ? `${baseUrl}/login` : `https://${baseUrl}/login`;

Solved

0 Replies

engma-linguisticsHOBBY

5 months ago

I have been getting the errors POST https://bactrian.railway.internal/login net::ERR_NAME_NOT_RESOLVED (cf. the 'hardcoded url' deployment) and POST https://${{bactrian-backend.railway_private_domain}}/login net::ERR_NAME_NOT_RESOLVED ('another attempt' deployment.)

I hope after getting these two services to properly talk to one another it's off to the races, but this far I've been stymied in my attempts to get these things talking to one another.

1319167322040307700


5 months ago

since you are making requests client side you need to be using the public domain


engma-linguisticsHOBBY

5 months ago

I do? Huh. Is there a different way I could set this up to be instead communicating over the internal domain(s)?


engma-linguisticsHOBBY

5 months ago

I'm absolutely open to suggestions and assume that having my services talk privately to one another is more secure. or something.


5 months ago

Looks to me like you're not making requests to the internal network properly. There should never be curly braces in the domain section of a URL


5 months ago

Take a read through these docs and come back if you have any questions



engma-linguisticsHOBBY

5 months ago

I was trying to reference the variable set by Railway, must have been doin' that wrong. I have indeed taken a look a the private-networking walkthrough, and must surely just be doing something wrong.


5 months ago

Additionally just to alleviate some confusion, the private network will never work locally. Queries done locally with the private network value will not work


engma-linguisticsHOBBY

5 months ago

Locally as in "on my computer"? No worries there I guess.


engma-linguisticsHOBBY

5 months ago

Progress? I swapped the environment variables to use the public domain instead.

1319170804063146000
1319170804558069800
1319170804885491700


engma-linguisticsHOBBY

5 months ago

Speaking of confusion, on the private networking page I see this; does that mean that the Uvicorn code is insufficient or the code there will fix it?

1319171102651453400


engma-linguisticsHOBBY

5 months ago

my backend railway.toml has: startCommand = "uvicorn app:app --host :: --port ${PORT-3000}"


5 months ago

The page is explaining that uvicorn binds to ipv4 by default, but you need to update your start command using the provided code to allow your serivce to bind to ipv6


engma-linguisticsHOBBY

5 months ago

If I go to https://bactrian-production.up.railway.app/api/auth/login I also see it's a 'not allowed' so maybe it is talking to the backend and "properly" being told "no"?


5 months ago

This is more likely a general code issue than a Railway configuration issue. Seems like you're duplicating the URL?


engma-linguisticsHOBBY

5 months ago

Oh huh, weird, let me try… hardcoding it or something, one min.


5 months ago

Navigating to a URL of an API should usually not return a result. In this case, since you're accessing /login, you're not providing the form answers your app requires when accessing in a browser


engma-linguisticsHOBBY

5 months ago

Yeah, I just wanted to make sure I at least… got… told "no" by my backend 😅


5 months ago

Fair enough!


engma-linguisticsHOBBY

5 months ago

Progress! A fun and new and exciting error.

1319172912011542500


5 months ago

Ah CORS, everyone’s favourite error


engma-linguisticsHOBBY

5 months ago

Not one of mine so far, lol


engma-linguisticsHOBBY

5 months ago

I added this to the Caddyfile, to no avail


engma-linguisticsHOBBY

5 months ago

```# Add CORS headers for all routes
header {
Access-Control-Allow-Origin https://bactrian-production.up.railway.app
Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS"
Access-Control-Allow-Headers "Content-Type"
Access-Control-Allow-Credentials true
# Required for preflight requests
Access-Control-Max-Age 3600
defer
}

# Handle preflight OPTIONS requests
@options {
    method OPTIONS
}
respond @options 204```

engma-linguisticsHOBBY

5 months ago

I'm at something of a loss, and very open to any suggestions.


engma-linguisticsHOBBY

5 months ago

OK, added CORS… stuff… to my backend and now I get a 400 error. Progress again!

1319177587754537000


engma-linguisticsHOBBY

5 months ago

And it's gone, even when I revert to those deployments. 🤔


engma-linguisticsHOBBY

5 months ago

Got it working with some wildly over provisioned permissions from my backend.


5 months ago

Glad to hear it!


5 months ago

!s


Status changed to Solved adam 5 months ago


How to get two services actually to talk to each other - Railway Help Station