写点什么

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

  • 2019 年 12 月 11 日
  • 本文字数:4985 字

    阅读完需:约 16 分钟

借助 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:36353

评论

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

堪称完美!中国Java之父纯手打,阿里首发:多线程与高并发

Java架构之路

Java 程序员 架构 面试 编程语言

2021金三银四春招来袭,掌握这份成长宝典,offer岂不是随便拿?

Java架构之路

Java 程序员 架构 面试 编程语言

高质量、高并发的实时通信架构设计与探索

融云 RongCloud

冰河公开了其总结的一项重要的编程技能!

冰河

Java 正则表达式 程序员

全票通过!微众开源项目EventMesh进入Apache孵化器

微众开源

Apache 开源 Event 事件网格 eventmesh

电子门锁没电的解决办法

孙叫兽

生活 程序人生 电子锁

2021 年要了解的 34 种 JavaScript 简写优化技术

LeanCloud

JavaScript 面试 大前端

xshell不能输入命令字符了?

Geek_6370d5

xshell 错误集锦

EGG Network公链技术创新,EFTalk打造高效全能公链

币圈那点事

区块链

炸了,炸了!阿里P9纯手打Java面试小抄(21版)在GitHub上已获80万star

Java架构之路

Java 程序员 架构 面试 编程语言

我看 JAVA 之 引用类型(Reference)

awen

Java ThreadLocal Reference

GO训练营第11周——DNS&CDN&多活架构

Glowry

GO训练营第12、13周—— runtime

Glowry

看了上百份面经,发现每次面试的问题都逃不过这几方面

yes

面试

微信小程序开发:如何快速实现添加一条分割线的项目需求

三掌柜

微信小程序 3月日更

FFmpeg入门篇

Changing Lin

ffmpeg media

拍乐云推出“实时标注”,助力少儿编程、在线金融等行业场景实现高效互动

拍乐云Pano

音视频 在线教育 互动白板 实时标注 在线金融

寻找被遗忘的勇气(十九)

Changing Lin

3月日更

产品调研报告

Ashley.

安全高可用通信背后的 MySQL 优化实践

融云 RongCloud

如何批量下载YouTube视频到本地

科技猫

软件 音视频 经验分享 资源分享 工具分享

打卡学习 VBA 和 PYTHON week01-02

小怪兽

3月日更 【IT蜗壳教学】 【自我错误点总结】

如何在 Spring 生态中玩转 RocketMQ?

阿里巴巴云原生

Java Serverless 微服务 云原生 中间件

【得物技术】TDengine在得物的落地应用

得物技术

数据库 tdengine 数据 sentinel 得物技术

白话讲解,拜占庭将军问题

架构精进之路

分布式 算法 3月日更

冲击大厂!阿里P9纯手打Java面试小抄(21版)在GitHub上已获80万star

Java 程序员 面试

【前端面试题】关于一些js的一些面试题(金融行业),我和面试官扯了三个小时

孙叫兽

JavaScript 大前端 金融 笔试题

标准引领 浪潮工业互联网助力澳门质量品牌国际认证联盟成立

浪潮云

工业互联网

2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)

比伯

Java 编程 架构 面试 程序人生

借助 GraphQL 简化离线应用程序的开发_语言 & 开发_亚马逊云科技 (Amazon Web Services)_InfoQ精选文章