0

So Basicly the code below represents my project.

public class Mutation
{
    public Guid CreateBook(
        [Service] IBookService service,
        [UseFluentValidation, UseValidator<CreateBookInputModelValidator>] CreateBookInputModel inputModel
    ) => service.CreateBook(inputModel);

    public void UpdateBook(
        [Service] IBookService service,
        Guid bookId,
        [UseFluentValidation, UseValidator<UpdateBookInputModelValidator>] UpdateBookInputModel inputModel
    ) => service.UpdateBook(bookId, inputModel);
}


public class BookService : IBookService
{
    private readonly IRepository _repository;
    public BookService(IRepository bookRepository)
    {
        _repository = bookRepository;
    }

    public Guid CreateBook(CreateBookInputModel inputModel)
    {
        var authorExists = _repository.AuthorExists(inputModel.AuthorId);
        if (!authorExists) 
        {
            throw new AuthorDoesNotExistException();
        }

        var book = new Book
        {
            Id = Guid.NewGuid(),
            Title = inputModel.Title,
            AuthorId = inputModel.AuthorId
        };

        _repository.Save(book);

        return book.Id;
    }

    public void UpdateBook(Guid bookId, UpdateBookInputModel inputModel)
    {
        var book = _repository.Get(bookId);
        if (book == null)
        {
            throw new BookDoesNotExistException();
        }

        book.Title = inputModel.Title;
        _repository.Save(book);
    }
}



public class UpdateBookInputModelValidator: AbstractValidator<UpdateBookInputModel>
{
    public UpdateBookInputModelValidator()
    {
        // Rule to check the book exists

        RuleFor(m => m.Title)
            .MinimumLength(1)
            .MaximumLength(30);
    }
}



public class CreateBookInputModelValidator: AbstractValidator<CreateBookInputModel>
{
    public CreateBookInputModelValidator(IRepository repository)
    {
        RuleFor(m => m.AuthorId)
            .Must(m => repository.AuthorExists(m))
            .WithMessage("Author doesnt exist");
        
        RuleFor(m => m.Title)
            .MinimumLength(1)
            .MaximumLength(30);
    }
}

I want to create multiple books at once with the following Query.

mutation {
  book1:createBook(inputModel: {
    title: "C# is great",
    authorId: "79827c91-5523-48f2-95b2-c8a181e73760"
  })
  
  book2: createBook(inputModel: {
    title: "GraphQL is awesome",
    authorId: "baac5788-0c30-422c-a910-97fb44f2838a"
  })
}

When the author of both books exists they get created and it returns 2 guids as below

{
  "data": {
    "book1": "8611b832-8a0e-4d5c-b956-b395e3f4e5f2",
    "book2": "1131e3ed-b69c-4698-ba74-85048968f089"
  }
}

When the second author doesn't exist it returns

{
  "errors": [
    {
      "message": "Unexpected Execution Error",
      "locations": [
        {
          "line": 7,
          "column": 3
        }
      ],
      "path": [
        "book2"
      ]
    }
  ]
}

The biggest problem i now have is the first book is created but it doesn't return the id because of the error.

My question is how can i tell the user the first book is created and return the id and the author of the second book can't be found.

1 Answers1

0

You get Unexpected Execution Error due to a contract violation. When the validation error happens, mutation is trying to return null, but fails because it must return non-nullable guid, as specified in the contract.

You should mark the return value as nullable to fix it:

public Guid? CreateBook(...)

So your contract will declare that the mutation may not return a value and it will allow HotChocolate to return partial data:

{
  "data": {
    "book1": "8611b832-8a0e-4d5c-b956-b395e3f4e5f2",
    "book2": "null"
  }
}

After fixing you will start get both data and the validation message.

Eugene
  • 169
  • 1
  • 6