1. GraphQL介绍

GraphQL 生来是要取代 REST 的,它是一种全新的 API 标准.随着软件行业的不断发展, API 已经无处不在,简而言之, API 定义了(说的是)客户端从服务器加载/读取数据的方式

现如今大多数系统或者软件都需要从服务器获取数据,那台服务器上有个数据库,数据全存在数据库里面。API 就是存储在数据库里的数据和应用程序需求之间的那座桥梁。

很多人都会误以为 GraphQL 是一种数据库技术,这是不准确的,事实上,它是专门针对 API 的一种查询语言,也就是说和数据库不是绑定的,且可以应用在任何会要到 API 的场景中。

官方定义:来自 facebookAPI 查询语言。通过使用GraphQL,你可以基于 模式定义你的后端。随后,客户端就可以根据需求查询你的数据集了。

GraphQL 的核心在于实现了声明式的数据获取(需要啥给啥 别一股脑给一大推我还得挑),客户端可以通过 API 规定那些它真正需要的数据。原来可能要好几个接口才能拼成一个你想要的数据结构,现在只需要一次调用一个url即可获取客户端需要的内容。

非官方表述:不同于 SOAP 或者 REST 接口,GraphQL 最大的好处在于你可以无限灵活地在接口请求中按照你想要的方式按需获取你想要的数据。接口提供方无需重新创建新的接口,一个接口就可以当做多个接口来使用。

常规接口胜在定义很完善,且是为了特定的场景针对性的定义的。接口一般都有设定好的请求和响应参数,而不管是否满足你使用的方式。而 GraphQL 则可以让你掌控这一切,你可以随心所欲的按照你想要的方式来请求和使用数据。

这既是优点,也是缺点。如果你的应用场景下不需要所有的数据, GraphQL 能够提升数据请求速度,因为服务端的工作量更少了。相反,如果你在一次请求中需要所有的数据,数据请求速度会由于工作量增加而降低。

在 Facebook 这样的互联网公司 GraphQL 解决了一个后台对多个客户端的情形,解决的是后台接口一次开发,如何应对多种客户端不同的个性化展现请求。如果把问题域类比到异构系统的互联互通中,原来不论是集成平台,HIS也好,所号称的开发接口一次,多次使用的口号其实是在扯淡,即就是提供接口的系统数据库表结构保持不变,当使用接口的系统想要获取比之前更多返回字段时,接口提供方是需要人工改造来得到新的接口。但有了 GraphQL ,作为不同系统间的代理,对比其中一个作为接口提供方角色的系统,只要你数据库表结构保持不变,就完全可以只开发一次接口。可以这么理解 只要HIS的数据库表结构设计的足够完善和稳定 那么医院想在原有数据基础上自己开发新的 HIS 系统或者其他应用是完全有可能的。

GraphQL Overview

因此,您不必因为客户端数据需求的变更而去修改你的后端。这从根本上解决了管理REST API中的一个最大的问题,这也是HL7 从2011年一直在提倡的 FHIR 标准难以回避的难题。

画外音:REST 曾火极一时,或许现在仍然是。在 REST 提出之时,由于客户端相对现在的较简单,开发周期和速度也没有现如今这么快,它还是很适合那个年代。但最近这几年 如下这三个原因不断在挑战 API 设计的方式:

  • 1.越来越多的移动端应用要求数据加载更快高效 移动设备用的越来越多,耗电量、网络交叉的情况 等等是 GraphQL 诞生的初音。如何最大化的减少网络传输过程中数据的大小,不会过多,也不会过少,刚刚好,提升应用的用户体验, GrapQL 正是为此而生
  • 2.前端平台和框架的喷涌 如果只构建和维护一个API的话 没法满足那些运行客户端程序的异构前端平台和框架的需求。而有了 GraphQL 每种客户端都可以只访问自己需要的数据。
  • 3.快速开发和功能迭代 很多公司现在都把持续部署作为一个标准来做。REST API 如果客户端的设计变化或者提了一些特殊的需求 后端就得紧跟着改。这阻碍了产品迭代和快速开发。

以某移动医疗公司开发的预约挂号请求为例进行说明

{
    "AppointmentId": "id",
    "AppointmentType": "预约方式编码 第三方预约",
    "AppointmentReason": {
        "code": "预约原因-疾病编码",
        "text": "疾病编码的非结构化描述"
    },
    "AppointmentType": "号别 专家",
    "AppointmentVisitDate": "预约的就诊日期",    
    "AppointmentVisitTime":{
        "AppointmentStart": "该号别的门诊开放时间开始时间",
        "AppointmentEnd": "该号别的门诊开放时间结束时间"
    },
    "AppointmentMadeDate": "进行预约的日期",   
    "AppointmentStatus": "预约状态 pending 等待确认中、booked 已完成预约、arrived 患者在预约日期时间内出现、fulfilled 患者完成预约、cancelled 预约被取消、noshow 患者爽约)",
    "AppointmentDescription": "预约描述",
    "SlotId": "<本次预约选择的可选时间段的系统id>",
    "PatientId": "<患者的系统id>",
    "PatientIdentifiers": [
      {
        "cardType": "<卡类型>",
        "cardNo": "卡号 患者的身份标识",
        "cardName": "卡名称"
      },
      {
        "cardType": "<卡类型>",
        "cardNo": "卡号 患者的身份标识",
        "cardName": "卡名称"
      }      
    ]
    "PatientName": "<患者姓名>",
    "PatientConfirmStatus": "患者是否确认预约的状态 初始值 needs-action",
    "PractitionerId": "<医生的系统id>",
    "PractitionerIdentifier": "<医生的工号>",
    "PractitionerName": "<医生姓名>",
    "PractitionerConfirmStatus": "医生是否确认预约的状态 初始值 needs-action"
}


* 预约方式, 取值范围在值域 `http://moh.gov/fhir/appointmentmethod` 中:

| 编码               | 描述                 |
|:-------------------|:---------------------|
| hospital-tel       | 医院电话预约         |
| hospital-web       | 医院门户网站预约     |
| hospital-msg       | 医院短信预约         |
| hospital-his       | 医生工作站预约       |
| hospital-bianmin   | 便民服务中心预约     |
| hospital-zizhuji   | 自助机预约           |
| hospital-other     | 医院内部其他方式预约 |
| hospital-yilian    | 医联预约             |
| hospital-thirdpart | 第三方预约           |


* 预约状态, 取值范围在值域 `http://hl7.org/fhir/appointmentstatus` 中:

| 编码      | 描述                     |
|:----------|:-------------------------|
| pending   | 等待确认中               |
| booked    | 已完成预约               |
| arrived   | 患者在预约日期时间内出现 |
| fulfilled | 患者完成预约             |
| cancelled | 预约被取消               |
| noshow    | 患者爽约                 |

* 预约类型, 取值范围在值域 `http://moh.gov/fhir/appointmenttype` 中:

| 编码       | 描述 |
|:-----------|:-----|
| expert     | 专家 |
| department | 专科 |
| disease    | 专病 |
| special    | 特需 |
| normal     | 普通 |

* 参与者是否确认预约的状态, 取值范围在值域 `http://moh.gov/fhir/appointmenttype` 中:

| 编码         | 描述                 |
|:-------------|:---------------------|
| needs-action | 需要患者确认本次预约 |
| tentative    | 待定                 |
| declined     | 拒绝本次预约         |
| accepted     | 接受本次预约         |

GraphQL还能让其他系统高效地批量获取数据。看一看下面GraphQL请求的🌰:

$ curl -X POST -H "Authorization: Bearer ACCESS_TOKEN" https://api.hit.com/v1/graphql --data '
{
  appointmentRequest {
    _id,
    type,
    reason,
    status,
    patient {
      id,
      identifier,
      name,
      confirmStatus
    },
    practitioner {
      id,
      identifier,
      name,
      confirmStatus 
    }
  } 
}'

这次 GraphQL 查询用于获取预约挂号请求以及相应患者 医生的相关信息。下面是请求返回结果:

{
  "data": {
    "appointmentRequest": [
      {
      "_id": "03390abb5570ce03ae524397d215713a",
      "type": "thirdpart 第三方预约平台",
      "reason": "甲亢",      
      "status": "pending 等待确认中",
      "patient": {
        "id": "03390abb5570ce03ae524397xxxxxx",
        "identifier": "身份证件号码",
        "name": "Peter James1",      
        "confirmStatus": "accepted"
      },      
      "practitioner": {
        "id": "03390abb5570ce03ae5243971xxxxx",
        "identifier": "医生的工号",
        "name": "Bob1",      
        "confirmStatus": "accepted"
      }
    },
    {
      "_id": "03390abb5570ce03ae524397d215713b",
      "type": "thirdpart 第三方预约平台",
      "reason": "甲亢",      
      "status": "pending 等待确认中",
      "patient": {
        "id": "03390abb5570ce03ae524398xxxxxx",
        "identifier": "身份证件号码",
        "name": "Peter James2",      
        "confirmStatus": "accepted"
      },      
      "practitioner": {
        "id": "03390abb5570ce03ae5243972xxxxx",
        "identifier": "医生的工号",
        "name": "Bob1",      
        "confirmStatus": "pending"
      }
    }
   ]

  }
}

如果你使用的是FHIR,那么你需要

  GET [base]/Appointment?_list=$current-appointments

或者

POST  [base]/Appointment/_search?_list=$current-appointments

GraphQL 是一种规范。

因此,它可以应用于任何平台或者语言。 它有一个由Facebook维护的基于JavaScript的参考实现。 还有由社区维护的其他语言的实现

这是规范文档: <http://facebook.github.io/graphql/>

你一旦已经尝试过GraphQL,你会想要将他应用到你的每一个项目中。

results matching ""

    No results matching ""