0
votes

I have a settings page where the user can update their email, first name, and last name. I am wrapping the entire settings component with a User component which contains all the data props for a user. Data is being pulled in and displayed in the input fields just fine, but I am getting the following error when I submit the data from the settings component:

[GraphQL error]: Message: You provided an invalid argument for the where selector on User., Location: , Path: updateUser

Uncaught (in promise) Error: GraphQL error: You provided an invalid argument for the where selector on User.

schema.graphql

...
# import * from './generated/prisma-client/prisma.graphql'

updateUser(email: String, firstName: String, lastName: String, password: String): User!
...

Mutation.js

async updateUser(parent, args, ctx, info) {
    // First check if there is a user with that email
    const user = await ctx.db.query.user({ where: { email: args.email } });
    if (user) {
      throw new Error(`The email ${args.email} is already being used`);
    }
    const updatedUser = await ctx.db.mutation.updateUser({
      where: { id: args.id },
      data: {
        email: args.email,
        firstName: args.firstName,
        lastName: args.lastName
      }
    });
    return updatedUser;
  }

Frontend Mutation:

const UPDATE_USER_MUTATION = gql`
  mutation UPDATE_USER_MUTATION(
    $email: String!
    $firstName: String!
    $lastName: String!
  ) {
    updateUser(email: $email, firstName: $firstName, lastName: $lastName) {
      id
      email
      firstName
      lastName
    }
  }
`;

If it helps, I can also provide the full Settings.js component. I'm still learning GraphQL/Apollo, so I'm sure I'm making an obvious mistake, but can't find where. Any help would be greatly appreciated! Thanks

EDIT: Adding the Settings.js file:

import React from "react";
import { Formik } from "formik";
import * as Yup from "yup";
import styled, { keyframes } from "styled-components";
import { Mutation, Query } from "react-apollo";
import { Button, Flex, Card, Box } from "rebass";
import gql from "graphql-tag";
import Link from "./Link";
import Text from "./Text";
import Error from "./ErrorMessage";
import User, { CURRENT_USER_QUERY } from "./User";

const UPDATE_USER_MUTATION = gql`
  mutation UPDATE_USER_MUTATION(
    $id: ID!
    $email: String
    $firstName: String
    $lastName: String
  ) {
    updateUser(
      id: $id
      email: $email
      firstName: $firstName
      lastName: $lastName
    ) {
      id
      email
      firstName
      lastName
    }
  }
`;

const StyledInput = styled(Box).attrs({
  as: "input",
})`
  font-size: 1em;
  border-radius: 3px;
  width: 15em;
  border: 1px solid ${props => props.theme.colors.greys[1]};
  padding: 0.5em;
`;

const UpdateSchema = Yup.object().shape({
  email: Yup.string()
    .email("Invalid email address")
    .required("You must enter an email address"),
});

const Settings = props => (
  <User>
    {({ data: { currentUser } }) => (
      <Mutation
        mutation={UPDATE_USER_MUTATION}
        refetchQueries={[{ query: CURRENT_USER_QUERY }]}

      >
        {(updateUser, { error, loading, called }) => (
          <Formik
            initialValues={{
              id: props.userId,
              email: currentUser.email,
              firstName: currentUser.firstName,
              lastName: currentUser.lastName,
            }}
            validationSchema={UpdateSchema}
            onSubmit={values => {
              updateUser({
                variables: {
                  email: values.email,
                  firstName: values.firstName,
                  lastName: values.lastName,
                },
              });
            }}
            render={({
              values,
              errors,
              touched,
              handleChange,
              handleBlur,
              handleSubmit,
            }) => (
              <Flex
                flexDirection={["column"]}
                mb={[2, 0]}
                width={1}
                alignItems="center"
                height={1}
              >
                <form onSubmit={handleSubmit} method="post">
                  <fieldset disabled={loading} aria-busy={loading}>
                    <Box>
                      <Error error={error} />
                      <Box mb="30px">
                        <label htmlFor="email">
                          <Text my={2}>Email</Text>
                          <StyledInput
                            placeholder="Enter your email address"
                            type="email"
                            name="email"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.email}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              Email successfully updated!
                            </Text>
                          )}

                          {touched.email && errors && errors.email && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.email}
                            </Text>
                          )}
                        </label>
                      </Box>

                      <Box mb="30px">
                        <label htmlFor="First Name">
                          <Text my={2}>First Name</Text>
                          <StyledInput
                            placeholder="Please enter your first name"
                            type="text"
                            name="firstName"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.firstName}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              First name updated!
                            </Text>
                          )}

                          {touched.firstName && errors && errors.firstName && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.firstName}
                            </Text>
                          )}
                        </label>
                      </Box>

                      <Box mb="30px">
                        <label htmlFor="Last Name">
                          <Text my={2}>Last Name</Text>
                          <StyledInput
                            placeholder="Please enter your last name"
                            type="text"
                            name="lastName"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.lastName}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              Last name updated!
                            </Text>
                          )}

                          {touched.lastName && errors && errors.lastName && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.lastName}
                            </Text>
                          )}
                        </label>
                      </Box>
                      <Box>
                        <Button type="submit" width={1} primary>
                          Update
                        </Button>
                      </Box>
                    </Box>
                  </fieldset>
                </form>
              </Flex>
            )}
          />
        )}
      </Mutation>
    )}
  </User>
);

export default Settings;
export { UPDATE_USER_MUTATION };
1
How can updateUser mutation know which user to update? Lets try to pass id - magnat
I added the Settings.js file. So I should add variables={{ id: props.id }} to my Mutation so that it gets passed? - Jon Leopard

1 Answers

1
votes

You are not providing an id in the frontend mutation so backend is receiving null and throwing this exception.

Please provide it in the frontend mutation.

const UPDATE_USER_MUTATION = gql`
  mutation UPDATE_USER_MUTATION(
    $id: ID!
    $email: String!
    $firstName: String!
    $lastName: String!
  ) {
    updateUser(id: $id, email: $email, firstName: $firstName, lastName: $lastName) {
      id
      email
      firstName
      lastName
    }
  }
`;

EDIT: Also update the schema.graphql file

...
# import * from './generated/prisma-client/prisma.graphql'

updateUser(id: ID!, email: String, firstName: String, lastName: String, password: String): User!
...

EDIT: Also update the react component like so:


import React from "react";
import { Formik } from "formik";
import * as Yup from "yup";
import styled, { keyframes } from "styled-components";
import { Mutation, Query } from "react-apollo";
import { Button, Flex, Card, Box } from "rebass";
import gql from "graphql-tag";
import Link from "./Link";
import Text from "./Text";
import Error from "./ErrorMessage";
import User, { CURRENT_USER_QUERY } from "./User";

const UPDATE_USER_MUTATION = gql`
  mutation UPDATE_USER_MUTATION(
    $id: ID!
    $email: String
    $firstName: String
    $lastName: String
  ) {
    updateUser(
      id: $id
      email: $email
      firstName: $firstName
      lastName: $lastName
    ) {
      id
      email
      firstName
      lastName
    }
  }
`;

const StyledInput = styled(Box).attrs({
  as: "input",
})`
  font-size: 1em;
  border-radius: 3px;
  width: 15em;
  border: 1px solid ${props => props.theme.colors.greys[1]};
  padding: 0.5em;
`;

const UpdateSchema = Yup.object().shape({
  email: Yup.string()
    .email("Invalid email address")
    .required("You must enter an email address"),
});

const Settings = props => (
  <User>
    {({ data: { currentUser } }) => (
      <Mutation
        mutation={UPDATE_USER_MUTATION}
        refetchQueries={[{ query: CURRENT_USER_QUERY }]}

      >
        {(updateUser, { error, loading, called }) => (
          <Formik
            initialValues={{
              id: props.userId,
              email: currentUser.email,
              firstName: currentUser.firstName,
              lastName: currentUser.lastName,
            }}
            validationSchema={UpdateSchema}
            onSubmit={values => {
              updateUser({
                variables: {
                  id: currentUser.id,
                  email: values.email,
                  firstName: values.firstName,
                  lastName: values.lastName,
                },
              });
            }}
            render={({
              values,
              errors,
              touched,
              handleChange,
              handleBlur,
              handleSubmit,
            }) => (
              <Flex
                flexDirection={["column"]}
                mb={[2, 0]}
                width={1}
                alignItems="center"
                height={1}
              >
                <form onSubmit={handleSubmit} method="post">
                  <fieldset disabled={loading} aria-busy={loading}>
                    <Box>
                      <Error error={error} />
                      <Box mb="30px">
                        <label htmlFor="email">
                          <Text my={2}>Email</Text>
                          <StyledInput
                            placeholder="Enter your email address"
                            type="email"
                            name="email"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.email}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              Email successfully updated!
                            </Text>
                          )}

                          {touched.email && errors && errors.email && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.email}
                            </Text>
                          )}
                        </label>
                      </Box>

                      <Box mb="30px">
                        <label htmlFor="First Name">
                          <Text my={2}>First Name</Text>
                          <StyledInput
                            placeholder="Please enter your first name"
                            type="text"
                            name="firstName"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.firstName}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              First name updated!
                            </Text>
                          )}

                          {touched.firstName && errors && errors.firstName && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.firstName}
                            </Text>
                          )}
                        </label>
                      </Box>

                      <Box mb="30px">
                        <label htmlFor="Last Name">
                          <Text my={2}>Last Name</Text>
                          <StyledInput
                            placeholder="Please enter your last name"
                            type="text"
                            name="lastName"
                            onChange={handleChange}
                            onBlur={handleBlur}
                            value={values.lastName}
                          />

                          {!error && !loading && called && (
                            <Text fontSize={1} color="green" pt={1}>
                              Last name updated!
                            </Text>
                          )}

                          {touched.lastName && errors && errors.lastName && (
                            <Text fontSize={1} color="red" pt={1}>
                              {errors.lastName}
                            </Text>
                          )}
                        </label>
                      </Box>
                      <Box>
                        <Button type="submit" width={1} primary>
                          Update
                        </Button>
                      </Box>
                    </Box>
                  </fieldset>
                </form>
              </Flex>
            )}
          />
        )}
      </Mutation>
    )}
  </User>
);