前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >大厂都在实践的GraphQL,你了解吗?

大厂都在实践的GraphQL,你了解吗?

作者头像
灬沙师弟
发布2023-03-07 09:33:12
2.2K0
发布2023-03-07 09:33:12
举报
文章被收录于专栏:Java面试教程Java面试教程

前言

最近,GraphQL 在构建后端 API 方面获得越来越多大公司的青睐, 如 PayPal、Facebook、Hasura、去哪儿等公司都做了大量实践。

它为客户端提供了一种灵活的方式来请求它需要的数据,提供严格类型的接口来查询数据,以及比 REST 更好的错误处理。

与 REST API 相比,GraphQL 有其自身的优势,例如

  • 只请求所需的内容,而不是所有内容。
  • 防止为获取所需数据而进行的级联调用。
  • 客户端不需要选择 REST 路径来获取不同的资源数据。
  • 它有助于减少传输的数据量。

这些只是其中的一些优势。今天本文将介绍如何在 springboot 项目中实践 GrapQL API.

创建应用

首先可以转到 https://start.spring.io 并创建一个具有以下依赖项的应用程序:

  • Spring Boot Starter GraphQL
  • Spring Boot Starter Data JPA.(仅用于存储数据)
  • H2 数据库。

定义一个 GraphQL 模型

接下来为一个简单查询 API 定义一个 GraphQL 模型。

代码语言:javascript
复制
type Person {
  id: Int!
  name: String!
  address: [Address]
  phone: String
}

type Address {
  type: AddressType!
  street: String
}

enum AddressType {
  PRIMARY
  SECONDARY
}

type Query {
  person(id: Int!): Person
}

在这里,我们有一个类型Person,该类型具有嵌套类型Address .然后,我们提供了一个简单的查询 API,用于根据 id 获取一个Person对象数据。!表示将参数定义为必需属性。

接下来,让我们使用mutation来添加编辑数据的功能。

代码语言:javascript
复制
input AddressInput {
  id: String!
  personId: Int!
  type: AddressType!
  street: String!
}
input PersonInput {
  id: String!
  name: String!
}

type Mutation {
  createPerson(person: PersonInput): Person
  createAddress(address: AddressInput): Address
}

在这里,我们将创建地址与创建人员分开。这只是为了简单起见, 也可以将人员的创建与地址相结合。为了修改数据,这里必须使用类型定义address字段 为AddressInput类型,这是因为 mutation 仅适用于输入类型。

定义模型后,让我们将其放在资源目录中的文件夹 graphql 下,文件名为 schema.graphqls 。Spring 会自动读取扩展名为 *.graphqlss 的模型文件。

接下来开始创建对应类。

定义领域类和存储库

为了简单起见,我们将直接在 GraphQL 接口中使用领域类。

代码语言:javascript
复制
@Entity
public class Person {

    @Id
    private Integer id;
    private String name;

// Getters and setters omitted

}

@Entity
public class Address {

    @Id
    private Integer id;
    private Integer personId;

    private String street;
    private AddressType type;

// Getters and setters omitted
}

public enum AddressType {
    PRIMARY,
    SECONDARY
}

最后,我们再实现两个存储库,如下所示。

代码语言:javascript
复制
public interface PersonRepository extends JpaRepository<Person, Integer> {
}

public interface AddressRepository extends JpaRepository<Address, Integer> {

    List<Address> findByPersonId(Integer personId);
}

对于地址存储库,我们定义一个额外的方法findByPersonId来获取与人员相关的地址。

完成上述工作后,让我们来实现 GraphQL API!

实现 GraphQL 查询 API

让我们定义用于查询数据的 API。

代码语言:javascript
复制
@Controller
public class GraphQLController {

    @Autowired
    private PersonRepository personRepository;

    @Autowired
    private AddressRepository addressRepository;

    @QueryMapping(value = "person")
    public Optional<Person> getPerson(@Argument(name = "id") Integer id) {
       return personRepository.findById(id);
    }

    @SchemaMapping
    public List<Address> address(Person person) {
        return addressRepository.findByPersonId(person.getId());
    }

在这里,我们定义了用于处理查询请求的 API。带有@QueryMapping注释的函数将成为查询的处理程序。

查询名称会自动映射到函数名称,或者使用 @QueryMapping接口的value参数(在本例中为person)显式定义它。

然后,我们使用指定参数名称@Argument注释来定义输入。

@SchemaMapping将成为查询嵌套字段的处理程序。映射基于函数名称本身(如上所示)或通过设置值参数(如下所示)。

代码语言:javascript
复制
@SchemaMapping(value = "address")
public List<Address> getAddress(Person person) {
    System.out.println("Fetching address");
    return addressRepository.findByPersonId(person.getId());
}

定义 GraphQL Mutation API

让我们看看如何定义修改操作。

代码语言:javascript
复制
@MutationMapping(name = "createPerson")
public Person addPerson(@Argument(name = "person") Person person) {
    return personRepository.save(person);
}

@MutationMapping(name = "createAddress")
public Address addAddress(@Argument(name = "address") Address address) {
    return addressRepository.save(address);
}

在这里,我们遵循与@QueryMapping注释相同的原则。我们使用@MutationMapping注释将修改操作名称映射到处理程序。

完成了这一步骤,我们就编写完成了所有处理程序。

启用 GraphiQL UI

为了调试 GraphQL 请求,Spring Boot GraphQL 提供了一个已经内置的 GraphiQL UI,我们可以使用它来测试我们的 API。

要启用它,我们需要设置以下属性。

代码语言:javascript
复制
spring:
  graphql:
    graphiql:
      enabled: true

有了这个,就可以访问 /graphiql 路径上的 UI .它将自动扫描资源目录中的模型,以帮助开发人员验证 GraphQL 请求。

UI 提供了不错的功能,例如自动完成和模型文档,有助于大家轻松创建请求。

现在让我们启动应用程序并查询数据。

使用 GraphIQL 查询数据

我们可以在路径 /graphiql 访问 GraphiQL 接口。

在查询数据之前,让我们先使用 mutation 操作存储一些数据。

代码语言:javascript
复制
mutation create_person {
  createPerson(person: { id: 1, name: "amrut" }) {
    id
    name
  }
}

mutation create_Address {
  createAddress(address: { id: 1, personId: 1, type: SECONDARY, street: "some street" }) {
    type
    street
  }
}

在这里,我们创建了两个修改请求,一个用于创建一个人,一个用于创建一个地址。第一个操作创建一个人,只返回 id 和 name,因为我们只对这两个属性感兴趣。同样,创建地址接口返回地址的类型和街道。

现在,让我们查询刚刚存储的数据。

代码语言:javascript
复制
query get_person {
  person(id: 1) {
    id
    name
    address {
      type
      street
    }
  }
}

我们看到了如何使用查询和修改操作使用 GraphQL 添加和查询数据。

GraphiQL 在内部对端点 /graphql 进行 POST 调用以发送查询。因此也可以使用 curl 命令执行此操作。

代码语言:javascript
复制
curl --location --request POST 'http://localhost:8080/graphql' \
--header 'Content-Type: application/json' \
--data-raw '{"query":"query get_person{ person(id : 1){ id  name }}"}'

我们可以使用属性spring.graphql.path更改此默认路径

接下来让我们看看如何处理错误。

GraphQL 错误处理

首先看看当我们遇到错误时会发生什么。

为此,我们在调用获取电话字段时,抛出一个异常。

代码语言:javascript
复制
@SchemaMapping(value = "phone")
public String getPhone(Person person) {
    throw new RuntimeException("Did not find phone data");
}

然后,让我们调用查询电话。

代码语言:javascript
复制
query get_person {
  person(id: 1) {
    id
    name
    phone
  }
}

当我们发送请求后,我们会得到以下输出。

代码语言:javascript
复制
{
  "errors": [
    {
      "message": "INTERNAL_ERROR for e7f58f69-e800-0622-1374-046bff96d0cb",
      "locations": [
        {
          "line": 29,
          "column": 7
        }
      ],
      "path": ["person", "phone"],
      "extensions": {
        "classification": "INTERNAL_ERROR"
      }
    }
  ],
  "data": {
    "person": {
      "id": 1,
      "name": "amrut",
      "phone": null
    }
  }
}

errors 数组包含来自各种程序问题的错误列表。每个错误都有一个错误消息、一个路径和一个查询中发生错误的位置,指示哪个字段导致了错误。

出现错误时,默认程序返回上述值。

我们可以通过创建自己的错误处理程序解析器来自定义返回错误的方式。

代码语言:javascript
复制
@Component
public class ErrorHandlerResolver extends DataFetcherExceptionResolverAdapter {

    @Override
    protected List<GraphQLError> resolveToMultipleErrors(Throwable ex, DataFetchingEnvironment env) {
        GraphQLError build = GraphqlErrorBuilder.newError(env)
                .message(ex.getMessage())
                .build();
        return Arrays.asList(build);
    }
}

我们新建了一个自定义错误处理程序,其中包含异常和对数据获取环境元数据的引用,我们可以使用它获取其他信息,例如发生错误的位置和路径。

有了这个处理程序,错误现在看起来像这样:

代码语言:javascript
复制
{
  "errors": [
    {
      "message": "Did not find phone data",
      "locations": [
        {
          "line": 29,
          "column": 7
        }
      ],
      "path": ["person", "phone"],
      "extensions": {
        "classification": "DataFetchingException"
      }
    }
  ],
  "data": {
    "person": {
      "id": 1,
      "name": "amrut",
      "phone": null
    }
  }
}

小结

本文介绍了如何使用 GraphQL 来查询数据、更改数据以及定义自定义错误处理程序。

对 GranphQL有兴趣的同学可以在官网https://graphql.org/ 进一步了解更深入的知识。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-12-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java面试教程 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 创建应用
  • 定义一个 GraphQL 模型
  • 定义领域类和存储库
  • 实现 GraphQL 查询 API
  • 定义 GraphQL Mutation API
  • 启用 GraphiQL UI
  • 使用 GraphIQL 查询数据
  • GraphQL 错误处理
  • 小结
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档