Search code examples
reactjsapigraphqlapolloregistration

ApolloError: Cannot read properties of undefined (reading 'firstName') on the client


Description of Problem


I'm trying to implement user regsitration for a facebook clone. I'm using apollo server and apollo client. The mutation is working on the backend and is succesfully creating a new user and adding them to the database. However when I'm trying to implement this on the client side the data is returning as undefined.

I managed to get the login working on the client and the code is mostly indentical (except for using an input type for the addUser mutation), so I'm struglling to see why this isn't working.

I'm also using React for the front end.

Server Code


Type Definitions

input AddUserInput {
        firstName: String!
        lastName: String!
        email: String!
        password: String!
        confirmPassword: String!
        pronoun: String
        genderIdentity: String!
        birthYear: Int!
        birthMonth: Int!
        birthDay: Int!
    }

type Mutation {
        addUser(addUserInput: AddUserInput): Auth!
        login(email: String!, password: String!): Auth!
    }

Resolver

module.exports = {
    Mutation: {
        // User registration
        async addUser(
            _,
            {
                addUserInput: {
                    firstName,
                    lastName,
                    email,
                    password,
                    confirmPassword,
                    pronoun,
                    genderIdentity,
                    birthYear,
                    birthMonth,
                    birthDay,
                },
            }
        ) {
            // Validate user data
            const { errors, valid } = validateAddUserInput(
                firstName,
                lastName,
                email,
                password,
                confirmPassword,
                pronoun,
                genderIdentity,
                birthYear,
                birthMonth,
                birthDay
            );

            if (!valid) {
                console.log('bonk on user registration');
                throw new UserInputError('Errors', { errors });
            }

            // Make sure user doesn't already exist
            const user = await User.findOne({ email });
            if (user) {
                throw new UserInputError('Email address is taken', {
                    errors: {
                        email: 'This email address is taken',
                    },
                });
            }

            // hash the password and create an auth token
            const hashedPassword = await bcrypt.hash(password, 12);

            // save birthday as ISO 8601 string
            const birthday = dayjs(`${birthYear}-${birthMonth}-${birthDay}`).toISOString();

            const newUser = new User({
                createdAt: new Date().toISOString(),
                firstName,
                lastName,
                email,
                password: hashedPassword,
                gender: {
                    pronoun,
                    genderIdentity,
                },
                birthday,
            });

            const res = await newUser.save();
            const { _id } = res;
            const token = signToken({
                email,
                firstName,
                lastName,
                _id,
            });

            return {
                user: res,
                id: _id,
                token,
            };
        },
    },
}

Client Code


Client Mutation

import { gql } from '@apollo/client';

export const REGISTER_USER = gql`
    mutation AddUser($registerInput: RegisterInput) {
        addUser(registerInput: $registerInput) {
            token
            user {
                _id
                firstName
                lastName
            }
        }
    }
`;

Sign-Up Component

Only showing the code that is relavent to the addUser mutation

// Mutation
const [addUser, { data, loading, error }] = useMutation(REGISTER_USER);

// Registration form submit function
    const handleRegistrationFormSubmit = async (event) => {
        event.preventDefault();

        // Make a copy of the registration form inputs
        const registrationFormInputs = { ...registrationForm };

        // Format gender inputs for the API
        if (registrationForm.customGender === '') {
            delete registrationFormInputs.customGender;
        } else {
            registrationFormInputs.genderIdentity = registrationFormInputs.customGender;
            delete registrationFormInputs.customGender;
        }

        try {
            const { data } = await addUser({
                variables: { ...registrationFormInputs },
            });

            // Get login token from response
            const registrationToken = data.register.token;

            Auth.login(registrationToken);
            // Clear the register form
            setRegistrationForm({
                firstName: '',
                lastName: '',
                emailAddress: '',
                password: '',
                confirmPassword: '',
                birthDay: dayCurrent,
                birthMonth: monthCurrent,
                birthYear: yearCurrent,
                gender: '',
                pronoun: '',
                customGender: '',
            });
            return <Navigate to="/" />;
        } catch (e) {
            ...
        }
    };

What I've tried


I've tried changing the name of the mutation and adding each individual type to the client mutation but nothing is working.


Solution

  • Figured it out.

    There were two issues both in the Sign-Up Component.

    First off all the emailAddress property was renamed to email as the type was mismatched with property.

        const [registrationForm, setRegistrationForm] = useState({
            ...
            email: '',
            ...
        });
    

    Secondly I wasn't inputting the variables correctly for the addUser mutation. It should be

    const { data } = await addUser({
      variables: {
        addUserInput: { ...registrationFormInputs },
      },
    });
    

    Hopefully this helps someone else out.