はじめに
前回は、JavaでのQueryの実装例を紹介したので、今回はMutationを紹介します。
ただ、JavaでGraphQLの実装してもらうときのポイントとなるところを紹介するのがメインなので、ソースコードを作り込んでいるわけではないので、実務でそのまま使用するのは避けてくださいmm
(時短で結構適当にロジックや例外処理書いてあります)
環境
・SpringBoot:3.3.2
・Java:17
・GraphQL:22.1
ソースコード
実装
1データ登録
スキーマ
type Mutation {
    registerBook(title: String!, author: AuthorInput!): Book!
}
type Book {
    id: ID!
    title: String!
    author: Author
}
input AuthorInput {
    id: ID!
    firstName: String!
    lastName: String!
}
コントローラ
@Controller
public class BookController {
    private final BookService bookService;
    public BookController(BookService bookService) {
        this.bookService = bookService;
    }
    @MutationMapping
    public BookResponse registerBook(@Argument("title") String title, @Argument("author") Author author) {
        Book book = bookService.registerBook(title, author);
        return new BookResponse(book.id().getId().toString(), book.title(), book.author());
    }
}
ユースケース
@Service
public class BookService {
   private final BookRepository bookRepository;
   private final AuthorRepository authorRepository;
   public BookService(BookRepository bookRepository, AuthorRepository authorRepository) {
       this.bookRepository = bookRepository;
       this.authorRepository = authorRepository;
   }
   public Book registerBook(String title, Author author) {
       UUID uuid = UUID.randomUUID();
       Book book = new Book(new BookId(uuid.toString()), title, author);
       if (!authorRepository.existById(author.id())) {
           authorRepository.register(author);
       }
       bookRepository.registerBook(book);
       return book;
   }
}
リポジトリ
public interface BookRepository {
   void registerBook(Book book);
}
@Repository
public class BookRepositoryImpl implements BookRepository {
   @Autowired
   DSLContext dslContext;
   @Override
   public void registerBook(Book book) {
       dslContext.insertInto(BOOK)
               .set(BOOK.ID, book.id().getId().toString())
               .set(BOOK.TITLE, book.title())
               .set(BOOK.AUTHORID, book.author().id())
               .execute();
   }
}
クエリ
mutation {
 registerBook(title: "mutationTest", 
 author: {
     id: "author-1",
     firstName: "first-test-name"
     lastName: "last-test-name" 
 }) {
   id 
   title
   author {
     id
     firstName
     lastName
   }
 }
}
実行結果
{
   "data": {
       "registerBookV2": {
           "id": "bf0892e1-5d63-4b42-aae5-50001130af4d",
           "title": "mutationTest",
           "author": {
               "id": "author-1",
               "firstName": "first-test-name",
               "lastName": "last-test-name"
           }
       }
   }
}
・コントローラクラス
Query書いているときは@QueryMappingのアノテーションを付与していたが、Mutationのときは@MutationMappingを付与すればOK
・スキーマ
type mutationでコマンド定義
リクエストでオブジェクト指定したい場合は、inputでデータ構造を定義
その他はQuery定義するときと変わらない。
複数データ登録
スキーマ
type Mutation {
  registerBookV2(bookList: [BookInput]!): Int!
}
input BookInput {
  title: String!
  author: AuthorInput!
}
input AuthorInput {
  id: ID!
  firstName: String!
  lastName: String!
}
クエリ
mutation {
registerBookV2(bookList: [
  {
    title: "title1"
    author: {
      id: "author-1",
      firstName: "first-author1"
      lastName: "last-author1"
    }
  },
  {
      title: "title2"
      author: {
        id: "author-2",
        firstName: "first-author2"
        lastName: "last-author2"
      }
  }
])
}
結果
{
  "data": {
      "registerBookV2": 2
  }
}
リストで受け取るコントローラ用意して、スキーマでは、リストを指定するコマンドを定義すれば、複数データ登録もできます。
ちなみに以下のように複数回コマンド実行して、複数データ登録することもできます。
クエリ
mutation {
book1: registerBookV2(bookList: {
    title: "title1"
    author: {
      id: "author-1",
      firstName: "first-author1"
      lastName: "last-author2"
    }
})
book2: registerBookV2(bookList: {
    title: "title1"
    author: {
      id: "author-1",
      firstName: "first-author1"
      lastName: "last-author2"
    }
})
}
結果
{
  "data": {
      "book1": 1,
      "book2": 1
  }
}
別々のコマンド実行を1度のリクエストで完結したいときは、使用するのだろうが、1度のリクエストで同じコマンドを複数回実行することは、実際の業務ではほとんどないと思います。
さいごに
今回はJavaでのMutation実装例を紹介させてもらいました。
次はエラーハンドリング周りの紹介をしますー
参考文献
