你是否曾想过在应用程序中获取数据是否有比 REST API 更好的方式?在后端开发中,GraphQL已经成为一个强大的替代方案,提供了一种更灵活、高效的数据获取方法。对于熟悉Java的开发人员来说,将GraphQL集成到现代后端可以打开通往各种用例的可伸缩、高性能API之门。
本博客将探讨GraphQL和REST之间的关键区别,突出使用GraphQL进行数据获取的独特优势,并指导您通过一个真实示例实现在Java中创建一个GraphQL API。
什么是GraphQL?
GraphQL是用于API的查询语言和用于执行这些查询的运行时。与REST不同,固定端点返回预定义数据,GraphQL允许客户端请求它们需要的确切数据。这种细粒度使得GraphQL在处理复杂或数据密集型应用程序时非常高效。
GraphQL方法的优势:
- 数据粒度获取:客户端可以查询姓名和职务,而无需检索不必要的字段如部门。
- 嵌套查询:在单个查询中获取经理详情以及员工信息。
- 基于模式驱动开发:模式充当合同,使API演进更加容易。
什么是REST API?
表述性状态转移(REST)是用于构建API的一种架构风格。它使用标准的HTTP方法,如GET、POST、PUT和DELETE来执行CRUD操作。REST以其简单性和广泛应用而闻名。
REST的局限性:
- 数据过度获取或不足获取。
- 需要多个端点和版本控制以适应更改。
- 没有内置的实时能力。
GraphQL与REST API:有何区别?
GraphQL和REST是构建API的两种流行方法,各有其优势。虽然REST多年来一直是标准,但GraphQL在数据检索和前后端团队合作方面提供了更大的灵活性和效率。
主要区别
- 与REST不同,GraphQL将数据检索整合到单个查询中,并减少了对版本控制的需求,因为客户端指定数据需求。
- 虽然REST使用HTTP状态代码指示成功或错误,但GraphQL始终返回200 OK状态,并通过响应主体传达错误。
- GraphQL还支持通过订阅进行实时更新,而REST则缺乏内置的实时支持。
- 尽管 REST 已经得到广泛的建立并有许多工具,但 GraphQL 的环境迅速发展,提供了强大的工具,如 GraphiQL,以便更容易进行开发。
- 最后,虽然 REST 使用标头进行缓存,但由于动态查询,GraphQL 需要更高级的技术,但提供了诸如持久化查询之类的选项,以实现有效的缓存。
核心 GraphQL 概念
1. 模式定义语言(SDL)
GraphQL 有自己的类型系统,用于定义 API 的模式。编写模式的语法称为模式定义语言(SDL)。
2. 查询 vs. 变更 vs. 订阅
- 查询 用于从服务器获取数据。与使用多个固定端点的 REST 不同,GraphQL 使用单个端点,客户端在查询中指定所需的数据,提供了灵活性。
- 变更 用于在服务器上修改数据,例如创建、更新或删除数据。它们允许客户端向后端发送更改,并且对于需要编写数据的应用程序至关重要。
- 订阅 通过保持客户端和服务器之间的稳定连接实现实时更新。当发生订阅的事件时,服务器会向客户端推送更新,提供连续的数据流,不同于查询和变更,后者遵循请求-响应循环。
3. GraphQL 模式
它定义了可以查询或修改的数据结构,充当服务器和客户端之间的合约。它指定可供客户端访问的类型、字段和关系。模式通常包括特殊的根类型:用于数据检索的查询、用于修改数据的变异和用于实时更新的订阅。这些类型共同定义了API的功能以及客户端如何与之交互。
4. 解析器:将GraphQL查询映射到数据
解析器是处理GraphQL服务器中数据获取逻辑的函数。模式中的每个字段都与一个解析器相关联,该解析器确定如何检索或计算该字段的数据。执行查询时,服务器会为请求的字段调用适当的解析器。解析器可以返回标量或对象,如果返回对象,则会继续执行子字段,如果返回标量,则会完成执行。如果返回null,则执行停止。解析器对于将GraphQL查询映射到实际数据源至关重要。
在Java中使用GraphQL的优势
- 精确数据获取:仅查询所需数据,确保可预测和高效的结果。
- 单个请求获取多个资源:在一次查询中获取相关数据,减少多个API调用。
- 类型系统:通过类型和字段组织API,确保查询有效且错误清晰可见。
- 开发工具:使用诸如GraphiQL之类的工具增强生产力,利用类型定义进行更好的查询构建和调试。
- 无版本演进: 添加或弃用字段而不会破坏现有查询,使API易于维护。
- 灵活的数据集成: 创建一个统一的API,覆盖现有数据和代码,兼容各种存储引擎和语言。
在Java中设置GraphQL API
实际示例: 用户和订单
假设您正在为一个大型组织构建员工目录API。目标是允许客户端查询员工姓名、职位、部门,甚至他们的汇报层级关系。
1. 设置项目
使用Spring Tool Suite创建一个新的Spring Boot项目或访问Spring Initialiser。然后,将这些依赖项添加到pom.xml
文件中:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-graphql</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webflux</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.graphql</groupId>
<artifactId>spring-graphql-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2. 创建您的实体
创建Java实体(例如,User
和Order
),用于表示将通过GraphQL查询或更改的数据。例如:
public class User {
strategy = GenerationType.IDENTITY) (
private Long userId;
private String name;
private String email;
private String password;
// Getters and setters...
}
public class Order {
strategy = GenerationType.IDENTITY) (
private Long orderId;
private String orderDetails;
private String address;
private int price;
private User user;
// Getters and setters...
}
3. 创建存储库
创建与数据库交互的存储库:
public interface UserRepository extends JpaRepository<User, Long> {}
public interface OrderRepository extends JpaRepository<Order, Long> {}
4. 创建服务类
创建服务类来处理业务逻辑:
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public User createUser(User user) {
return userRepository.save(user);
}
public User getUser(Long userId) {
return userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
}
public List<User> getAllUsers() {
return userRepository.findAll();
}
public boolean deleteUser(Long userId) {
userRepository.deleteById(userId);
return true;
}
}
5. 创建 GraphQL 控制器
定义 GraphQL 控制器来处理查询和变更:
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
public List<User> getUsers() {
return userService.getAllUsers();
}
public User getUser( Long userId) {
return userService.getUser(userId);
}
public User createUser( String name, String email, String password) {
User user = new User();
user.setName(name);
user.setEmail(email);
user.setPassword(password);
return userService.createUser(user);
}
public boolean deleteUser( Long userId) {
return userService.deleteUser(userId);
}
}
6. 定义您的 GraphQL 模式
在 src/main/resources
目录中创建一个 schema.graphqls
文件:
type User {
userId: ID!
name: String
email: String
password: String
}
type Query {
getUsers: [User]
getUser(userId: ID!): User
}
type Mutation {
createUser(name: String, email: String, password: String): User
deleteUser(userId: ID!): Boolean
}
7. 在 application.properties 中配置 GraphQL
可选地,在 scr/main/resources/application.properties
中配置 GraphQL 设置:
spring.graphql.graphiql.enabled=true
8. 运行您的应用程序
使用 mvn spring-boot:run
或从您的 IDE 运行 SpringBoot 应用程序。一旦运行,您可以访问 /graphiql
端点。
9. 使用 GraphQL 查询进行测试
使用类似 GraphiQL 或 Postman 的工具测试 GraphQL API。
对于变更:
mutation {
createUser(
name:"swetha",
email:"[email protected]",
password:"23sde4dfg43"
){
name,
userId
}
}
输出:
{
"data": {
"createUser": {
"name": "swetha",
"userId": "3"
}
}
}
对于查询:
query{
getUsers{
name
}
}
输出:
{
"data": {
"getUsers": [
{
"name": "Medha"
},
{
"name": "Riya"
},
{
"name": "swetha"
}
]
}
}
高级 GraphQL 功能
1. 使用片段增强可重用性
片段基本上是为特定类型定义的可重用字段集。它是一个有助于改善 GraphQL 代码结构和可重用性的功能。
2. 使用参数化字段
在GraphQL中,字段可以接受参数,使查询更加动态和灵活。这些参数允许您过滤或自定义API返回的数据。
3. 使用GraphQL进行分页和排序
分页
分页在API设计中是一个棘手的话题。在高层次上,关于如何处理它有两种主要方法。
- 限制偏移:通过提供要检索的项的索引来请求列表的特定块(实际上,您主要提供起始索引(偏移)以及要检索的项的计数(限制))。
- 基于游标:这种分页模型稍微更高级一些。列表中的每个元素都与唯一ID(游标)相关联。然后,浏览列表的客户端提供起始元素的游标以及要检索的项的计数。
排序
使用Graphql API设计,可以返回根据特定标准排序的元素列表。有序
使用GraphQL的挑战和考虑
- 复杂性:管理GraphQL模式和查询可能对简单数据模型或经验不足的团队构成挑战。
- 性能问题:如果未经过优化,深度嵌套查询可能会对后端资源造成压力。
- 缓存挑战:标准的基于REST的缓存策略不适用,需要定制解决方案。
- 安全问题: 过度获取和恶意查询需要查询限制和其他保护措施。
- 混合使用: 最适合复杂数据需求,通常与REST结合以实现更简单的操作。
结论
GraphQL为在Java中构建现代API提供了一种灵活高效的方法,使其成为动态和数据密集型应用的理想选择。其单一端点架构和强类型简化了API设计,同时确保了强大的性能。无论您是在创建简单的员工目录还是复杂的分析平台,GraphQL都能使开发人员轻松交付可扩展的解决方案。今天就开始探索GraphQL,使用像Spring Boot和graphql-java
这样的工具,在您的下一个项目中释放其全部潜力。
源代码
您可以在Github上找到本教程的完整源代码。
Source:
https://dzone.com/articles/design-scalable-java-apis-with-graphql