在spring-boot中使用graphql
首先構建spring-boot項目,pom.xml文件中加入
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.4.0</version>
</dependency>
<!-- graphql -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
<!-- 這個是graphiql,跟上面的不一樣 -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphiql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
接着定義graphql的schema,在resources目錄下任意位置的*.graphqls文件都會被掃描到,作為graphql 的schema。
這里在resources下先建一個文件夾/graphql,建立schema文件
- schema.graphqls
type Query {
user(nickname: String): User
users: [User]
article(title: String!): Article
}
type Mutation {
addUser(mail: String!, nickname: String!, password: String!): User
addArticle(title: String!, content: String!, authorId: String!): Article
}
type User {
id: String!
mail: String!
nickname: String!
password: String!
description: String
}
type Article {
id: String!
author: User!
title: String!
content: String!
createBy: String
thumbUp: Int
}
Resolver 和數據類
GraphQL Java Tools可以將schema中定義的類型的屬性與java對象的屬性或方法對應起來。即上面的User類型,可以使用一個類與之對應
public class User {
@Id
private String id;
private String nickname;
private String mail;
private String password;
private String description;
// 構造器,getter和setter
}
但是在Article類型中author的屬性是User,我們該如何解決呢?
這時我們可以使用一個GraphQLResolver指定某個類型的解析
@Component
public class ArticleResolver implements GraphQLResolver<Article> {
private final UserRepository userRepository;
public ArticleResolver(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User author(Article article) {
return userRepository.findById(article.getAuthorId()).get();
}
}
指定Article類型的解析方式,其中標量類型可以直接從Aritcle的java類屬性中獲取,所以不必寫方法,對於author這個屬性,其類型為User,我們指定author()方法進行解析。
那么到底type Article {...}schema里面的屬性怎么解析呢?是與數據類進行映射,還是與resolver中的方法進行映射?
官網上給出了映射的優先級:
首先是resolver
- method 屬性名(...)
- method is屬性名(...)
- method get屬性名(...)
- method getField屬性名(...)
其次是Data Class(即與類型對應的java類)
- method 屬性名(...)
- method is屬性名(...)
- method get屬性名(...)
- method getField屬性名(...)
- field 屬性名
所以首先在ArticleResolver中查找author的映射,找不到的屬性則一致向下找到屬性的java類的get方法/屬性本身。
另外,要給Query和Mutation至少創建一個Resolver
@Component
public class QueryResolver implements GraphQLQueryResolver {
private final UserRepository userRepository;
private final ArticleRepository articleRepository;
public QueryResolver(UserRepository userRepository, ArticleRepository articleRepository) {
this.userRepository = userRepository;
this.articleRepository = articleRepository;
}
public Article article(String title) {
return articleRepository.findArticleByTitle(title);
}
public User user(String nickname) {
return userRepository.findUserByNickname(nickname);
}
public List<User> users() {
return userRepository.findAll();
}
}
@Component
public class MutationResolver implements GraphQLQueryResolver, GraphQLMutationResolver {
private final ArticleRepository articleRepository;
private final UserRepository userRepository;
private final BCryptPasswordEncoder encoder;
public MutationResolver(ArticleRepository articleRepository, UserRepository userRepository, BCryptPasswordEncoder encoder) {
this.articleRepository = articleRepository;
this.userRepository = userRepository;
this.encoder = encoder;
}
public User addUser(String mail, String nickname, String password) {
if(userRepository.findUserByNickname(nickname) != null){
return null;
}
return userRepository.save(User.builder()
.nickname(nickname)
.mail(mail)
.password(encoder.encode(password))
.build());
}
public Article addArticle(String title, String content, String authorId) {
if(!userRepository.findById(authorId).isPresent()){
return null;
}
return articleRepository.save(Article.builder()
.authorId(authorId)
.title(title)
.content(content)
.createBy(new Date())
.thumbUp(0)
.build());
}
}
這里使用mongodb存儲數據。
運行,在http://localhost:8080/graphiql 中進行操作