Search code examples
graphqlapolloapollo-clientapollo-servertypegraphql

throw a descriptive error with graphql and apollo


Consider the following class:

// entity/Account.ts
import { Entity, PrimaryGeneratedColumn, Column, BaseEntity, Index, CreateDateColumn, UpdateDateColumn } from 'typeorm'
import { Field, Int, ObjectType } from 'type-graphql'

@ObjectType()
@Entity()
export class Account extends BaseEntity {
  @Field(() => Int)
  @PrimaryGeneratedColumn()
  id: number

  @Field()
  @Column({ length: 50, unique: true })
  @Index({ unique: true })
  accountIdentifier: string

  @Field({ nullable: true })
  @Column({ length: 100 })
  name?: string
}

With it's corresponding resolver:

// AccountResolver.ts
@Resolver()
export class AccountResolver {
  @Mutation(() => Account)
  async addAccount(@Arg('options', () => AccountInput) options: AccountInput) {
    try {
      // if (!options.accountIdentifier) {
      //   throw new Error(`Failed adding account: the accountIdentifier is missing`)
      // }
      return await Account.create(options).save()
    } catch (error) {
      if (error.message.includes('Cannot insert duplicate key')) {
        throw new Error(
          `Failed adding account: the account already exists. ${error}`
        )
      } else {
        throw new Error(`Failed adding account: ${error}`)
      }
    }
  }
}

Jest test file

// AccountResolver.test.ts
describe('the addAccount Mutation', () => {
  it('should throw an error when the accountIdentifier is missing', async () => {
    await expect(
      client.mutate({
        mutation: gql`
        mutation {
          addAccount(
            options: {
              name: "James Bond"
              userName: "James.Bond@contoso.com"
            }
          ) {
            accountIdentifier
          }
        }
      `,
      })
    ).rejects.toThrowError('the accountIdentifier is missing')
  })

The field accountIdentifier is mandatory and should throw a descriptive error message when it's missing in the request. However, the error thrown is:

"Network error: Response not successful: Received status code 400"

What is the correct way to modify the error message? I looked at type-graphql with the class-validators and made sure that validate: true is set but it doesn't give a descriptive error.

EDIT

After checking the graphql playground, it does show the correct error message by default. The only question remaining is how write the jest test so it can read this message:

{
  "error": {
    "errors": [
      {
        "message": "Field AccountInput.accountIdentifier of required type String! was not provided.",

Thank you for any help you could give me.


Solution

  • The ApolloError returned by your client wraps both the errors returned in the response and any network errors encountered while executing the request. The former is accessible under the graphQLErrors property, the latter under the networkError property. Instea dof using toThrowError, you should use toMatchObject instead:

    const expectedError = {
      graphQLErrors: [{ message: 'the accountIdentifier is missing' }]
    }
    await expect(client.mutate(...)).rejects.toMatchObject(expectedError)
    

    However, I would suggest avoiding using Apollo Client for testing. Instead, you can execute operations directly against your schema.

    import { buildSchema } from 'type-graphql'
    import { graphql } from 'graphql'
    
    const schema = await buildSchema({
      resolvers: [...],
    })
    const query = '{ someField }'
    const context = {}
    const variables = {}
    const { data, errors } = await graphql(schema, query, {}, context, variables)