apollographql / apollo-client

:rocket:  A fully-featured, production ready caching GraphQL client for every UI framework and GraphQL server.
https://apollographql.com/client
MIT License
19.34k stars 2.66k forks source link

ECONNREFUSED Error When Using Apollo Client with NextJS #11910

Closed jamie0xgitc0decat closed 3 months ago

jamie0xgitc0decat commented 3 months ago

I am encountering a persistent ECONNREFUSED error with Apollo Client in my Next.js application. Despite trying various configurations and troubleshooting steps, including Nginx settings and Docker network settings, the issue persists. Below are the details of my setup and the steps I have taken.

Description I have been facing this issue for around two weeks. I have tried everything I know so far, but I still have no clues about what is going on. I am posting this issue here to seek help from the community.

My problem is that I am deploying my backend application to an AWS Ubuntu instance and using Nginx to route to different Docker containers, including frontend and backend.

I have tested these setups in my local environment without any problems. Even making local frontend network calls to the AWS backend container works fine. However, the AWS frontend container connecting with the backend container does not work as expected, causing any web page to show an "Internal Server Error".

Technical Details

image image

Error: connect ECONNREFUSED 127.0.0.1:<random_port>
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1495:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: <random_port>
}

Nginx Configuration:

server {
    listen 80;
    server_name frontend.mydomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

server {
    listen 80;

    server_name backend.mydomain.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Docker Network Config

{
    "Name": "service-network",
    "Scope": "local",
    "Driver": "bridge",
    "EnableIPv6": false,
    "IPAM": {
        "Driver": "default",
        "Options": {},
        "Config": [
            {
                "Subnet": "172.18.0.0/16",
                "Gateway": "172.18.0.1"
            }
        ]
    },
    "Internal": false,
    "Attachable": false,
    "Ingress": false,
    "ConfigFrom": {
        "Network": ""
    },
    "ConfigOnly": false,
    "Containers": {
        "": {
            "Name": "frontend",
            "IPv4Address": "172.18.0.2/16",
            "IPv6Address": ""
        },
        "": {
            "Name": "backend",         
            "IPv4Address": "172.18.0.3/16",
            "IPv6Address": ""
        }
    },
    "Options": {},
    "Labels": {}
}

Apollo Client File

import { ApolloClient, InMemoryCache, createHttpLink, from } from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import fetch from 'isomorphic-unfetch';

const httpLink = createHttpLink({
  uri: 'http://backend:8080/graphql',
  fetch,
});

const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) => {
      console.error(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`);
    });
  }
  if (networkError) {
    console.error(`[Network error]: ${networkError}`);
  }
});

const client = new ApolloClient({
  ssrMode: typeof window === 'undefined',
  link: from([errorLink, httpLink]),
  cache: new InMemoryCache({ addTypename: false }),
  defaultOptions: {
    watchQuery: {
      errorPolicy: 'all',
    },
    query: {
      errorPolicy: 'all',
    },
    mutate: {
      errorPolicy: 'all',
    },
  },
});

export default client;

_app.tsx

import { ApolloProvider } from '@apollo/client';
import { useRouter } from 'next/router';
import { NextIntlClientProvider } from 'next-intl';

import '@/styles/globals.css';
import '@/styles/colors.css';

import Footer from '@/components/Footer';
import Navbar from '@/components/Navbar';

import apolloClient from '@/utils/apollo-client';

process.on('uncaughtException', function (err) {
  console.log(err);
  console.log('trace stack:', JSON.stringify(err.stack));
});

const withLogging = (Component, componentName) => {
  return (props) => {
    console.log(`Rendering ${componentName} ...`);
    return <Component {...props} />;
  };
};

console.log('Before MyApp function');

function MyApp({ Component, pageProps }) {
  console.log('Rendering MyApp ...', JSON.stringify(pageProps));

  const router = useRouter();

  const IntlProviderWithLogging = withLogging(
    NextIntlClientProvider,
    'NextIntlProvider'
  );
  const ComponentWithLogging = withLogging(Component, 'Page Component');

  console.log('Before return in MyApp');

  return (
    <ApolloProvider client={apolloClient}>
      <IntlProviderWithLogging
        locale={router.locale}
        messages={pageProps.messages}
      >
        <div className='flex min-h-screen flex-col'>
          <div className='flex-grow'>
            <Navbar />
          </div>
          <main className='flex-grow bg-gray-900 pb-14'>
            <ComponentWithLogging {...pageProps} />
          </main>
          <Footer />
        </div>
      </IntlProviderWithLogging>
    </ApolloProvider>
  );
}

console.log('After MyApp function');

export default MyApp;

pages/index.tsx

export async function getServerSideProps(context) {
  console.log('index page getServerSideProps');

  try {
    const { data: upcomingMatches } = await client.query({
      query: gql`
   ....

Backend Server Cors Config

  const corsOptions = {
    origin: [
      "http://localhost:3000",
      "http://localhost:3100",
      "https://studio.apollographql.com",
      "https://domain.com",
      "http://frontend:3000",
      "http://172.18.0.2:3000",
      "*",
    ],
    methods: "*",
    allowedHeaders: "*",
  };

  // Request logging middleware
  app.use((req, res, next) => {
    console.log(`Received ${req.method} request for ${req.url}`);
    console.log("Request headers:", JSON.stringify(req.headers, null, 2));
    next();
  });

  app.use(cors(corsOptions));

  app.use(
    "/graphql",
    cors<cors.CorsRequest>({
      origin: [
        "http://localhost:3000",
        "http://localhost:3100",
        "https://studio.apollographql.com",
        "https://domain.com",
        "http://frontend:3000",
        "http://172.18.0.2:3000",
        "*",
      ],
      methods: "GET,HEAD,PUT,PATCH,POST,DELETE,OPTIONS",
      allowedHeaders: "Content-Type,Authorization",
      credentials: true,
    }),

Execution order on Frontend docker logs

 apolloClientHttpLink:  http://backend:8080/graphql
finish setup http link ApolloLink { request: [Function (anonymous)] }
Error: connect ECONNREFUSED 127.0.0.1:39835
    at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1495:16) {
  errno: -111,
  code: 'ECONNREFUSED',
  syscall: 'connect',
  address: '127.0.0.1',
  port: 39835
}
finish setup client 
Before MyApp function
After MyApp function

that means when I nagivate to domain.com -> domain.com/en-US -> make the execution order on above

  1. _app.tsx
  2. -> import apolloClient from '@/utils/apollo-client';
const httpLink = createHttpLink({
  uri: apolloClientHttpLink,
  fetch: fetchHack,
});

// fetchOptions: {
//   mode: 'cors', // Ensuring CORS requests are made
// },

console.log('finish setup http link', httpLink);

const errorLink = onError(
  ({ graphQLErrors, networkError, operation, response }) => {
    console.log(
      `Error from operation ${operation.operationName} with input data ${operation.variables}`
    );
    if (graphQLErrors) {
      graphQLErrors.forEach(({ message, locations, path }) =>
        console.error(
          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
        )
      );
    }
    if (networkError) {
      console.error(`[Network error]: ${networkError}`);
      console.log(`Response was:`, response);
    }
  }
);

const client = new ApolloClient({
  ssrMode: typeof window === 'undefined',
  link: from([errorLink, httpLink]),
  cache: new InMemoryCache({ addTypename: false }),
  defaultOptions: {
    watchQuery: {
      errorPolicy: 'all', // Include both data and errors in the response
    },
    query: {
      errorPolicy: 'all', // Same policy for one-time queries
    },
    mutate: {
      errorPolicy: 'all', // And for mutations as well
    },
  },
});

console.log('finish setup client', client);

You can see the ECONNREFUSED error inside the new ApolloClient initialization.

The execution reaches the end of apollo-client.ts and then returns back to _app.tsx.

  1. Logs:
  1. The frontend returns an Internal Server Error.

image

What I Have Checked So Far

  1. Local Docker connects to AWS backend: Success - no problems at all.
  2. Docker shell into frontend container: curl http://backend:8080/graphql - Success.
  3. Locale problems:
    • Domain redirects from domain.com to domain.com/en-US.
    • Checked with logs to examine execution order.
    • Not 100% sure, but logs indicate that the request stops before loading locale messages.
  4. Index page is not rendering: It means getServerSideProps is not executed, and consequently, useQuery is not called.
  5. _app.tsx Page Component is not rendering.
  6. CORS origin problems?: It did not make a query request. Not sure if it's a CORS origin issue.
jerelmiller commented 3 months ago

Hey @jamie0xgitc0decat 👋

I believe the issue stems from the fact that you're trying to fetch localhost on your server and your server doesn't understand how to resolve that address. You should also see this error if you use fetch directly without Apollo Client.

I found this issue which might give some hints on how to fetch from localhost on your server. Beyond this though, I'm afraid I won't be able to give much guidance since nginx/docker network configuration is not my area of expertise and is outside the scope of Apollo Client itself. As such, I'm going to close this issue. Please let me know if I've misunderstood the ask here and I'd be happy to reopen if necessary.

github-actions[bot] commented 3 months ago

Do you have any feedback for the maintainers? Please tell us by taking a one-minute survey. Your responses will help us understand Apollo Client usage and allow us to serve you better.

jerelmiller commented 3 months ago

I forgot to mention, feel free to join our discord channel which is a more appropriate place for this kind of question. Other community members there might be able to provide some guidance on what to do.

github-actions[bot] commented 2 months ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. For general questions, we recommend using StackOverflow or our discord server.