Simon Balfe
Simon Balfe

Reputation: 55

GraphQL Cannot return null for non-nullable field User.createdAt

@Mutation (() => UserResponse)
    async register(
        @Arg('options', () => UsernamePasswordInput ) options: UsernamePasswordInput,
        @Ctx() {em, req}: MyContext
    ): Promise<UserResponse>{

        const errors = validateRegister(options);
 

        
        const hashedPassword = await argon2.hash(options.password)
        let user;
     
        try{
            /* cast to this type, em as entity manager  */
          
            const result = await (em as EntityManager).createQueryBuilder(User).getKnexQuery().insert({
                username: options.username,
                password: hashedPassword,
                email: options.email,
                created_at: new Date(),
                updated_at: new Date()
            }).returning("*");
            /* returns all fields back from the user  */
            user  = result[0];

        } catch(err: any ) {
            if (err.code === '23505' || err.detail.includes("already exists")){
                // duplicate name
                /* returning errors object */
               return {
                   errors: [{
                       field: 'username or email',
                       message: 'that username or email already exists'
                   }]
               }
            }    
            
        }
        req.session.userId = user.id;
        return {user};
    }
import { Entity, PrimaryKey, Property } from "@mikro-orm/core";
import { Field, Int, ObjectType } from "type-graphql";

@ObjectType() 
@Entity()
export class User {
  @Field(() => Int) 
  @PrimaryKey()
  id!: number;

  @Field(() => String )
  @Property({type: "date", default: "Now()", nullable: true})
  createdAt = new Date();

  @Field(() => String )
  @Property({type: "date",onUpdate: () => new Date()})
  updatedAt = new Date();

  @Field(() => String)
  @Property({type: 'text', unique: true}) 
  username!: string;

  @Field(() => String)
  @Property({type: "text", unique: true})
  email!: string;
  /* this password has no graphql endpoint field */
  @Property({type: 'text'}) 
  password!: string;

}
const Register: React.FC<registerProps> = ({}) => {
     const router = useRouter();
     const [, register] = useRegisterMutation();
     return (
        <Wrapper variant = "small">
            <Formik 
                initialValues = {{email: "", username: "", password: ""}}
                onSubmit = {
                    
                    async (values, {setErrors}) => { 
                        /* this is  a promise retruned therefore just return */

                         /* maps to the graphql mutation specified above */
                    
                         const response = await register({options: values});
                         console.log("user: ", response)
                         /* code gen is detecting the types of this response from graphql */

                         /* check if the errors object has any errors */
                         if (response.data?.register.errors){
                           
                            /* map then into a key:value pair for use in the formik set errors to nicely display the errors */
                            setErrors(toErrorMap(response.data.register.errors));
                         } else if(response.data?.register.user){
                             // worked

                             /* use next js hook to return to homepage  */
                             router.push('/');
                         }
                         
                    }}>

                {/* 
                    this is a render prop, we are taking values state and handleChange function from Formik , and passing our logic in
                    using the handles they gave us.
                */}
                {({isSubmitting}) => (
                   <Form>
                       <InputField 
                            name = "username" 
                            placeholder = "username" 
                            label = "Username" 
                       />
                        <Box mt = {4}>
                            <InputField 
                                name = "email" 
                                placeholder = "email" 
                                label = "Email" 
                            />
                       </Box>
                       <Box mt = {4}>
                            <InputField 
                                name = "password" 
                                placeholder = "password" 
                                label = "Password" 
                                type = "password"
                            />
                       </Box>
                       <Button 
                            _hover = {{
                                backgroundColor: "purple", 
                                color: "white",
                            }}
                            ml = "auto"
                            mr = "auto"
                            mt = {4}
                            display = "block"
                            type = "submit"
                            backgroundColor = "black"
                            /* this loads whenever we are submittting, a cool loading symbol */
                            isLoading = {isSubmitting}
                        > 
                         register
                        </Button>
                    </Form>
                )}  
            </Formik>
        </Wrapper>
    )
}

export default withUrqlClient(createUrqlClient, {ssr: true})(Register);

On my page when I pass an email / password / username to register it returns this error from graphql

{
    "errors": [
        {
            "message": "Cannot return null for non-nullable field User.createdAt.",
            "locations": [
                {
                    "line": 10,
                    "column": 7
                }
            ],
            "path": [
                "register",
                "user",
                "createdAt"
            ],
            "extensions": {
                "code": "INTERNAL_SERVER_ERROR",
                "exception": {
                    "stacktrace": [
                        "Error: Cannot return null for non-nullable field User.createdAt.",
                        "    at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:559:13)",
                        "    at resolveField (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:472:19)",
                        "    at executeFields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:292:18)",
                        "    at collectAndExecuteSubfields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:748:10)",
                        "    at completeObjectValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:738:10)",
                        "    at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:590:12)",
                        "    at resolveField (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:472:19)",
                        "    at executeFields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:292:18)",
                        "    at collectAndExecuteSubfields (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:748:10)",
                        "    at completeObjectValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:738:10)",
                        "    at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:590:12)",
                        "    at completeValue (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:556:21)",
                        "    at D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\graphql\\execution\\execute.js:469:16",
                        "    at processTicksAndRejections (node:internal/process/task_queues:96:5)",
                        "    at async execute (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:205:20)",
                        "    at async Object.processGraphQLRequest (D:\\Coding\\WebDevelopment\\Projects\\lireddit\\server\\node_modules\\apollo-server-core\\dist\\requestPipeline.js:139:28)"
                    ]
                }
            }
        }
    ],
    "data": {
        "register": {
            "errors": null,
            "user": null
        }
    }
}

This happens for no known reason , and I really am not sure what to even say at this point I have been confused on this issue for ages

......................... . .. . . . . . . ...................................................................................................................................................................

Upvotes: 3

Views: 16007

Answers (2)

El Anonimo
El Anonimo

Reputation: 1870

Same tutorial. In src/resolvers/user.ts at the end of the try clause of the register mutation replace returning('*') with

.returning([
  '*',
  'created_at as createdAt',
  'updated_at as updatedAt'
])

That shapes the server response to

result: [
  {
    id: 00,
    username: 'jonh_d',
    createdAt: 1985-10-29T03:17:23.000Z,
    updatedAt: 1985-10-29T03:17:23.000Z
  }
]

Upvotes: 1

Emmanuel Ponnudurai
Emmanuel Ponnudurai

Reputation: 1074

This usually always happens when a field, which is set up as non-nullable, is being returned as null from the server.

Please check the object which is returned from the server and verify if all the fields which you marked as per your set up, are being returned with some value.

One way to quickly check is to make all as { nullable: true } and see if that works, and then you can see which one is the offender.

Upvotes: 1

Related Questions