Search code examples
asynchronousnext.jsreact-hooksnext-auth

NextJS Dynamic Route Not Resolving GraphQL Async Request


I have dynamic links like so localhost:3000/[username]. I query against a mongodb. If a username FooBar existed in the database then it would resolve; otherwise, it would say User {slug} Not Found.

I was able to verify my graphQL query is working fine. Requesting Foobar with getUser() returns a user object with a username string set to Foobar. Invalid users return as null. I am confident this isnt the issue. I believe the problem is incorrectly using an asynchronous method in UseEffect. I have console.log(fetchData) set which never logs in my console indicating to me the fetchData() method is never called. Here is A Guide I tried to follow to avoid this issue.

Here is my code:

import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';
import { getUser } from '@/lib/users';
import {subscribe} from "graphql";

const UserPage = () => {
  const router = useRouter();
  const { slug } = router.query;
  const [isValidUser, setIsValidUser] = useState(false);

  useEffect(() => {
    const fetchData = async () => {
      console.log(fetchData)
      let result;
      if (slug === undefined || slug == null) {
        result = null;
      } else {
        result = Array.isArray(slug) ? slug[0] : slug;
        const user = await getUser(result);
        if (user) {
          setIsValidUser(true);
        }
      }
      return result;
    }


    if (slug) {
      fetchData().catch(console.error);
    }
  }, [slug]);

  return (
    <>
      {isValidUser ? (
        <p>User {slug} found!</p>
      ) : (
        <p>User {slug} not found.</p>
      )}
    </>
  );
};

export default UserPage;

Some side notes: I am using NextJS, mongoDb and NextAuth to authenticate user agaisnt a database.

Edit: For good measure here is the gql query:

import { apolloClient } from "@/pages/_app";
import { GET_USER_BY_USERNAME_OR_ADDRESS } from "../db/queries/userQueries";
import { ADD_USER } from "../db/mutations/userMutations";

const GET_USER_BY_USERNAME_OR_ADDRESS = gql`
  query GetUserByUsernameOrAddress($usernameOrAddress: String) {
    getUserByUsernameOrAddress(usernameOrAddress: $usernameOrAddress) {
      address
      bio
      username
    }
  }
`;

const getUser = async (addressOrUsername: string) => {
  try {
    const { data } = await apolloClient.query({
      query: GET_USER_BY_USERNAME_OR_ADDRESS,
      variables: { addressOrUsername },
    });
    if ( data ){
      return data;
    } else {
      return null;
    }
  } catch (error) {
    console.error(error);
  }
  return false;
};

Solution

  • I found out that NextJS can process the slug using GetServerSideProps. I can then pass it into my page as a prop like so. Doing this allowed me to first fetch the asynchronous process on the serverside then pass it to the front end for nextJS to render.

    import { GetServerSideProps } from 'next'
    import { getUser } from '@/lib/users';
    import ProfilePage from "./profile";
    
    interface Props {
      isValidUser: boolean;
      slug: string | null;
      address: string;
    }
    
    const UserPage = ({ isValidUser, address, slug }: Props) => {
      return (
        <>
          {isValidUser && address? (
            <>
              <ProfilePage address={address}/>
            </>
          ) : (
            <p>User {slug} not found.</p>
          )}
        </>
      );
    };
    
    export const getServerSideProps: GetServerSideProps = async (context) => {
      const slug = context.params?.address as string;
      let isValidUser = false;
      let address = "";
    
      if (slug) {
        try {
          const user = await getUser(slug);
          if (user.getUserByUsernameOrAddress != null) {
            address = user.getUserByUsernameOrAddress.address;
            isValidUser = Boolean(user);
          }
        } catch (error) {
          console.error(error);
        }
      }
    
      return {
        props: {
          isValidUser,
          address,
          slug: slug || null
        }
      };
    }
    
    export default UserPage;