New to Apollo, doing something wrong

I’m new to Apollo and trying to build a UI with React, using Keycloak for user login. I have defined the following React component:

const UserComponent = ({ embeddedKeycloak }) => {
  const email = embeddedKeycloak.idTokenParsed.email || embeddedKeycloak.idTokenParsed.preferred_username;
  console.log(email);
  const { loading, error, data } = useQuery(GET_USER, { variables: { email: email } });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error : {error.message}</p>;

  console.log(data);
  return data.account[0];
};

UserComponent.propTypes = {
  embeddedKeycloak: PropTypes.shape({
    idTokenParsed: PropTypes.shape({
      email: PropTypes.string,
      preferred_username: PropTypes.string
    }).isRequired
  }).isRequired
};

When I run the code, I get

Uncaught Invariant Violation: An error occurred!

with a long stack trace that includes the line with useQuery in it. Note that I do get the user’s email address printed to the console so I know Keycloak is working correctly. Here is the GET_USER query:

const GET_USER = gql`
  query getUser($email: String) {
    account(where: {email: $email}) {
      company {
        name
      }
      company_id
      date_created
      email
      first_name
      id
      is_admin
      last_name
    }
  }
`;

Any help is appriciated as I’ve been banging my head on this for a while.

Hey @cbwilliamsme :wave:

Its difficult to tell what’s happening without more info on the error you’re seeing. Does that invariant violation have a URL thats attached that starts with https://go.apollo.dev/c/err? If so, please open that URL as it will give you more error details on what is happening.

It does, but reading the page didn’t help.

Would you mind either posting the link or the error text here? Perhaps I can help decipher it.

Here’s the link: Error code details - Apollo GraphQL Docs

And here’s the full code. I’m trying to use Keycloak for Authorization (that part is working as I get my email address printed in the console). It’s the code after the email is logged that’s causing the error and I can’t figure out why.

/* eslint-disable no-unused-vars */
/* eslint-disable react-refresh/only-export-components */
import { ApolloClient, ApolloProvider, InMemoryCache, createHttpLink, useQuery, gql } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import React, { useState, useEffect } from 'react';
import * as ReactDOM from 'react-dom/client';
import App from './App';
import keycloak from './Keycloak';
import PropTypes from 'prop-types';


const httpLink = createHttpLink({
  uri: import.meta.env.VITE_APP_HASURA_URL,
});

const authLink = setContext((_, { headers }) => {
  return {
    headers: {
      ...headers,
      'content-type': 'application/json',
      'company-id': 1,
      'x-hasura-admin-secret': import.meta.env.VITE_HASURA_ADMIN_SECRET
    }
  };
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache()
});

const GET_USER = gql`
  query getUser($email: String) {
    account(where: {email: $email}) {
      company {
        name
      }
      company_id
      date_created
      email
      first_name
      id
      is_admin
      last_name
    }
  }
`;

const UserComponent = ({ embeddedKeycloak }) => {
  const email = embeddedKeycloak.idTokenParsed.email || embeddedKeycloak.idTokenParsed.preferred_username;
  console.log(email);
  const { loading, error, data } = useQuery(GET_USER, { variables: { email: email } });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error : {error.message}</p>;

  console.log(data);
  return data.account[0];
};

UserComponent.propTypes = {
  embeddedKeycloak: PropTypes.shape({
    idTokenParsed: PropTypes.shape({
      email: PropTypes.string,
      preferred_username: PropTypes.string
    }).isRequired
  }).isRequired
};

function Root() {
  const [keycloakReady, setKeycloakReady] = useState(false);
  const [keycloakInstance, setKeycloakInstance] = useState(null);

  useEffect(() => {
    keycloak.init({ onLoad: 'login-required' }).then((authenticated) => {
      if (authenticated) {
        setKeycloakInstance(keycloak);
        setKeycloakReady(true);
      } else {
        console.error("User not authenticated");
      }
    });
  }, []);

  // Render nothing until keycloak is initialized
  if (!keycloakReady) return <div>Loading Keycloak...</div>;

  return (
    <ApolloProvider client={client}>
      <App user={UserComponent({ embeddedKeycloak: keycloakInstance })} keycloak={keycloakInstance} />
    </ApolloProvider>
  );
}

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(<Root />);

Your code user={UserComponent({ embeddedKeycloak: keycloakInstance })} is not actually rendering the UserComponent function as a component, but executes it as a function - as part of your Root component.

Your Root component is not wrapped in ApolloProvider (only it’s children are), so you get this error message.

You would need to actually render your UserComponent, like <UserComponent embeddedKeycloak={keycloakInstance} /> - passing that as a prop to App should be fine:

user={<UserComponent embeddedKeycloak={keycloakInstance} />}

I’m confused as I thought this code wrapped the Root in an ApolloProvider:

  return (
    <ApolloProvider client={client}>
      <App user={UserComponent({ embeddedKeycloak: keycloakInstance })} keycloak={keycloakInstance} />
    </ApolloProvider>
  );

But it is not called as a component.

You call UserComponent as a function while Root is evaluated.

It’s the difference between UserComponent(...) (function call) and <UserComponent ... (component render).

So as you use it, UserComponent is not a child component. It’s just a method call that happens while Root renders.

Ah ha! I see said the blind man when he picked up his hammer and saw!

Problem solved! Thank you! Onto the next!

1 Like