InfoQ技术大会双节活动折上折,满10000-1000>> 了解详情
写点什么

借助 GraphQL 简化离线应用程序的开发

2019 年 12 月 11 日

借助 GraphQL 简化离线应用程序的开发

开源 Amplify Framework 是一种命令行工具和库,可让 Web 和移动开发人员轻松预置和访问基于云的服务。例如,如果我想为我的移动应用程序创建一个 GraphQL API,我会在我的开发计算机上使用 amplify add api 来配置后端 API。在回答几个问题之后,我键入 amplify push 以在云中创建一个 AWS AppSync API 后端。Amplify 会生成相应的代码,从而让我的应用程序可以轻松访问新创建的 API。Amplify 支持 AngularReactVue 等流行的 Web 框架。它还支持使用 React Native、适用于 iOSSwift 或适用于 AndroidJava 开发的移动应用程序。如果您想了解更多有关将 Amplify 用于移动应用程序的的信息,请报名参加我们为 re:Invent 2019 大会准备的一个研讨会(iOSReact Native)。


AWS 客户告诉我们,开发 Web 和移动应用程序时最难的任务是跨设备同步数据以及处理离线操作。理想情况下,当一台设备离线时,您的客户应该能够继续使用您的应用程序,不仅能够访问数据,而且可以创建和修改数据。当设备在此上线时,该应用程序必须重新连接到后端,同步数据并解决冲突(如有)。即使是使用 AWS AppSync 软件开发工具包的设备内缓存以及离线变异增量同步功能,要正确处理各种边缘案例,仍需要使用许多非差异化的代码。


今天,我们推出 Amplify DataStore,这是一款持久的设备内存储库,方便开发人员写入、读取和观察数据更改。Amplify DataStore 允许开发人员编写利用分布式数据的应用程序,无需为离线或在线场景编写额外的代码。Amplify DataStore 可作为没有连接到云或需要使用 AWS 账户的 Web 和移动应用程序的独立本地数据存储。但在与云后端结合使用时,Amplify DataStore 会在网络连接可用时使用 AWS AppSync API 透明地同步数据。Amplify DataStore 会自动设置数据版本,并在云中使用 AppSync 检测和解决冲突。此工具链还会根据开发人员提供的 GraphQL schema,为我的编程语言生成对象定义。


下面我们来看它的工作原理。


我首先会安装 Amplify CLI创建一个 React 应用程序。这是一个标准的 React 应用程序,您可以在我的 git 存储库中找到脚本。我会使用 npx amplify-app 将 Amplify DataStore 添加到该应用程序。npx 是专用于 NodeJS 的,Amplify DataStore 还集成了多种原生移动工具链,例如适用于 Android StudioGradle 插件,以及会创建适用于 iOS 的自定义 XCode 构建阶段的 CocoaPods


现在我的应用程序框架已经完成,我将添加一个 GraphQL schema,它代表两个实体:即这些博文的 PostsComments。我会安装依赖项并使用 AWS Amplify CLI 为 GraphQL schema 中定义的对象生成源代码。


Bash


# 添加一个 graphql schema 到 amplify/backend/api/amplifyDatasource/schema.graphqlecho "enum PostStatus {  ACTIVE  INACTIVE}
type Post @model { id: ID! title: String! comments: [Comment] @connection(name: "PostComments") rating: Int! status: PostStatus!}type Comment @model { id: ID! content: String post: Post @connection(name: "PostComments")}" > amplify/backend/api/amplifyDatasource/schema.graphql
# 安装依赖项 npm i @aws-amplify/core @aws-amplify/DataStore @aws-amplify/pubsub
# 生成代表模型的源代码 npm run amplify-modelgen
# 在云中创建 API npm run amplify-push
复制代码


@model@connectionAmplify GraphQL Transformer 将用于生成代码的指令。 标注 @model 的对象是您的 API 中的顶层对象,它们存储在 DynamoDB 中,您可以使其可搜索、为它们建立版本控制将对它们的访问权限制为授权用户。@connection 指令可用于表达对象之间的 1-n 关系,这与您在使用关系数据库时定义的关系类似(您可以使用 @key 指令来建立 n-n 关系模型)。


最后一步是创建 React 应用程序本身。我建议下载一个非常简单的示例应用程序以快速入门:


Bash


# 下载简单的 react 应用程序curl -o src/App.js https://raw.githubusercontent.com/sebsto/amplify-datastore-js-e2e/master/src/App.js
# 启动应用程序 npm run start
复制代码


我通过浏览器连接到应用程序 [](http://localhost:8080/),然后开始测试该应用程序。



示例应用程序提供了一个基本的 UI(正如大家可以猜到,我不是图形设计师!)来创建、查询和删除项目。Amplify DataStore 为开发人员提供了一种方便易用的 API,用于存储、查询和删除数据。读取和写入都将在后台填充到您在云中的 AppSync 终端节点。Amplify DataStore 通过存储适配器来使用本地数据存储,我们为 Web 应用程序使用 IndexedDB,为移动应用程序使用 SQLite。Amplify DataStore 是开源的,因此您可以根据需要添加对其他数据库的支持。


从代码的角度看,与数据的交互十分简单,只需调用 DataStore 对象的 save()delete()query() 操作即可(这是一个 Javascript 示例,您需要为 Swift 或 Java 编写类似的代码)。请注意 query() 操作接受基于 Predicates 表达式的筛选条件,例如 item.rating("gt", 4)Predicates.All


JavaScript


function onCreate() {  DataStore.save(    new Post({      title: `New title ${Date.now()}`,      rating: 1,      status: PostStatus.ACTIVE    })  );}
function onDeleteAll() { DataStore.delete(Post, Predicates.ALL);}
async function onQuery(setPosts) { const posts = await DataStore.query(Post, c => c.rating("gt", 4)); setPosts(posts)}
async function listPosts(setPosts) { const posts = await DataStore.query(Post, Predicates.ALL); setPosts(posts);}
复制代码


我连接到 Amazon DynamoDB 控制台并观察到项目已在后端存储:



支持离线模式无需对我的代码作出任何更改。为模拟离线模式,我关闭了我的 WIFI。我在应用程序中添加了两个项目,然后再次打开 WIFI。此应用程序在离线期间继续正常运行。唯一明显的变化是 _version 字段在离线时没有更新,因为它是在后端填充的。



当网络连接恢复后,Amplify DataStore 会与后端透明地同步。我验证了现在 DynamoDB 中有 5 个项目(每次部署的表名称都不相同,因此请务必调整下面的表名称):


Bash


aws dynamodb scan --table-name Post-raherug3frfibkwsuzphkexewa-amplify \                   --filter-expression "#deleted <> :value"            \                   --expression-attribute-names '{"#deleted" : "_deleted"}' \                   --expression-attribute-values '{":value" : { "BOOL": true} }' \                   --query "Count"
5 // <= there are now 5 non deleted items in the table !
复制代码


Amplify DataStore 利用 GraphQL 订阅来跟踪后端出现的更改。您的客户可以从其他设备修改数据,Amplify DataStore 将负责透明地同步本地数据存储。无需掌握任何 GraphQL 知识,Amplify DataStore 将自动为您解决底层 GraphQL API 调用问题。实时数据、连接、可扩展性、扇出和广播等等,全部由 Amplify 客户端和 AppSync 使用 WebSocket 协议在背后处理。


我们实际上是将 GraphQL 作为一个网络协议使用,从而通过 HTTPS 动态地将模型实例转换为 GraphQL 文档。


为了在后端发生更改时刷新 UI,我在 useEffect() React 挂钩 中添加了以下代码。它使用 DataStore.observe() 方法来注册回调函数 ( msg => { ... } )。Amplify DataStore 会在 Post 实例在后端发生更改时调用此函数。


JavaScript


const subscription = DataStore.observe(Post).subscribe(msg => {  console.log(msg.model, msg.opType, msg.element);  listPosts(setPosts);});
复制代码


现在我打开 AppSync 控制台。我查询现有的博文来检索 Post ID。


query ListPost {  listPosts(limit: 10) {    items {      id      title      status      rating      _version    }  }}
复制代码


我选择了我的应用程序中的第一篇博文,也就是以 7d8… 开头的博文,然后我发送了以下 GraphQL 变异:


mutation UpdatePost {  updatePost(input: {    id: "7d80688f-898d-4fb6-a632-8cbe060b9691"    title: "updated title 13:56"    status: ACTIVE    rating: 7    _version: 1  }) {    id    title    status    rating    _lastChangedAt    _version    _deleted      }}
复制代码


我立即看到应用程序收到了通知并刷新了它的用户界面。



最后我使用多台设备进行了测试。我首先使用 amplify add hostingamplify publish 为我的应用程序创建了一个托管环境。在应用程序发布后,我同时打开 iOS Simulator 和 Chrome。这两个应用程序最初显示相同的项目列表。我在这两个应用程序中创建新的项目,观察到应用程序在实时刷新 UI。在测试结束时,我删除了所有的项目。



我验证了 DynamoDB 中不再有任何项目(每次部署的表名称都不相同,因此请务必调整下面的表名称):


Bash


aws dynamodb scan --table-name Post-raherug3frfibkwsuzphkexewa-amplify \                   --filter-expression "#deleted <> :value"            \                   --expression-attribute-names '{"#deleted" : "_deleted"}' \                   --expression-attribute-values '{":value" : { "BOOL": true} }' \                   --query "Count"
0 // <= all the items have been deleted !
复制代码


在与后端同步本地数据时,AWS AppSync 会跟踪版本号以检测冲突。如果存在冲突,默认的解决策略是自动在后端合并更改。自动合并是一种十分简单的冲突解决策略,无需编写客户端侧代码。例如,假设我有一个初始 Post,而 Bob 和 Alice 同时更新了该博文:


col 1col 2


原始项目:


Json


{   "_version": 1,   "id": "25",   "rating": 6,   "status": "ACTIVE",   "title": "DataStore is Available"}```         | Alice 更新了 `rating`:
Json

复制代码


{


“_version”: 2,


“id”: “25”,


“rating”: 10,


“status”: “ACTIVE”,


“title”: “DataStore is Available”


}


同时 Bob 更新了 `title`:
Json

复制代码


{


“_version”: 2,


“id”: “25”,


“rating”: 6,


“status”: “ACTIVE”,


“title”: “DataStore is great !”


}



Json

复制代码


{


“_version”: 3,


“id”: “25”,


“rating”: 10,


“status”: “ACTIVE”,


“title”: “DataStore is great !”


}



自动合并会根据 GraphQL schema 中定义的类型信息,严格定义字段级别的合并规则。例如,`List` 和 `Map` 将会合并,相互冲突的标量更新(例如数值与字符串)将会导致保留服务器上现有的值。开发人员可以选择其他冲突解决策略:开放式并发(冲突的更新将被拒绝)或自定义(调用 [](https://amazonaws-china.com/lambda/) 函数来决定哪种版本是正确版本)。您可以使用 `amplify update api` 来选择冲突解决策略。有关这些不同策略的更多信息,请参阅 AppSync [](http://docs.aws.amazon.com/appsync/latest/devguide/conflict-detection-and-sync.html#conflict-detection-and-resolution)。
此实例的完整源代码可从[](https://github.com/sebsto/amplify-datastore-js-e2e)获取。此应用程序的代码不到 100 行,其中 20% 仅与 UI 有关。请注意我没有编写任何一行的 GraphQL 代码,这一切都是在 Amplify DataStore 中发生的。
Amplify DataStore 云后端现已在所有提供 [](https://amazonaws-china.com/appsync/) 的 AWS 区域开放,在本文执笔之时包括:美国东部(弗吉尼亚北部)、美国东部(俄亥俄)、美国西部(俄勒冈)、亚太地区(孟买)、亚太地区(首尔)、亚太地区(新加坡)、亚太地区(悉尼)、亚太地区(东京)、欧洲(法兰克福)、欧洲(爱尔兰)和欧洲(伦敦)。
在您的应用程序中使用 Amplify DataStore 不会产生额外的费用,您只需为您使用的后端资源付费,例如 AppSync 和 DynamoDB(有关定价详细信息请参阅[](https://amazonaws-china.com/appsync/pricing/)和[](https://amazonaws-china.com/dynamodb/pricing/on-demand/))。这两种服务都提供[](https://amazonaws-china.com/free),以便您了解和免费试用。
[](https://aws-amplify.github.io/docs/js/datastore) 让您可以专注于应用程序的商业价值,无需编写无差异化价值的代码。我迫不及待想看到大家使用它构建的杰出应用程序。
**本文转载自AWS技术博客。**
**原文链接:https://amazonaws-china.com/cn/blogs/china/amplify-datastore-simplify-development-of-offline-apps-with-graphql/**
复制代码


2019 年 12 月 11 日 15:36209

欲了解 AWS 的更多信息,请访问【AWS 技术专区】

评论

发布
暂无评论
发现更多内容

监控应用,应该监控什么?

小清新同学

云计算 运维 监控

c++基础——杂谈2

菜鸟小sailor 🐕

c++ 语言

华为轮值董事长郭平2020全联接大会主题演讲:永远面向阳光,阴影甩在身后

华为云开发者社区

5G ICT huawei

跟着B站UP主小姐姐去华为坂田基地采访扫地僧

华为云开发者社区

华为 技术 大牛 扫地僧

SpringBoot-技术专题-提升服务吞吐量

李浩宇/Alex

从『用户』到『客户』,企业服务平台如何实现高效转化?

易观大数据

保留时序数据波动细节的一种采样算法

小清新同学

监控 时序数据库

难得干货,揭秘支付宝的2维码扫码技术优化实践之路

JackJiang

支付宝

数据库-技术专题-SQL编写规范

李浩宇/Alex

为什么海外服务器打开网站会卡呢?

德胜网络-阳

什么才是“应用拓扑”?

小清新同学

运维 监控

架构师训练营 1 期第 2 周:框架设计 - 总结

piercebn

极客大学架构师训练营

来不及解释了,快上车!快速开发平台,助力企业搭乘万物互联顺风车

Philips

敏捷开发 企业开发 互联网革命

架构师训练营 1 期 - 第二周 - 设计原则

三板斧

极客大学架构师训练营

一文快速入门分库分表

程序员内点事

Java 分库分表

架构师训练营第 1 期第 2周作业

du tiezheng

极客大学架构师训练营

架构师训练营第二周作业

尹斌

从大数据的角度来谈谈运维监控这件事儿

小清新同学

运维 监控

虚拟卡兑换架构设计

孙志平

传销资金盘挂靠区块链热点 肃清整顿热潮拉开帷幕

CECBC区块链专委会

区块链 金融

框架设计:作业

Nick~毓

2B还是2C,这真是个问题

MavenTalker

SaaS

架构师训练营第 1 期第二周课后练习题

Leo乐

极客大学架构师训练营

刷爆朋友圈的字节跳动编码题,今天把解析思路分享下!

Java架构师迁哥

娱乐圈套路多?看区块链如何来破解

CECBC区块链专委会

网红 娱乐圈

某大厂一位核心技术人员不小心泄漏的公司内部培训以及工作笔记内容,手慢无。

Java架构师迁哥

MySQL varchar类型最大值,原来一直都理解错了

架构精进之路

MySQL varchar

架构师训练营第二周学习总结

尹斌

RN运行项目报错:Unable to resolve module `./debugger-ui/debuggerWorker.js` from ``

凌宇之蓝

ios android React Native

机构进场区块链安全基础设施准备好了么?

CECBC区块链专委会

区块链 数字资产

AI小白必读:深度学习、迁移学习、强化学习别再傻傻分不清

华为云开发者社区

人工智能 学习 迁移

借助 GraphQL 简化离线应用程序的开发-InfoQ