如何在 React TypeScript 前端中使用 AWS Amplify GraphQL API
介绍
AWS Amplify是一个面向移动和 Web 应用的开发平台。它内置于 Amazon Web Services (AWS) 中,并搭建了各种 AWS 服务,例如 Lambda 函数、Cognito 用户池和 AppSync GraphQL API。这省去了手动为移动和 Web 应用设置 AWS 基础设施的繁琐步骤,从而显著提升了开发速度。Amplify 还拥有自己的文档网站,并且是开源的。
本文将向您展示如何使用 TypeScript 代码生成来设置 GraphQL API,以及如何在 React 前端应用程序中使用它。
AWS账户
由于 Amplify 是一项 AWS 服务,因此需要登录AWS 控制台。如果您还没有账户,请创建一个。注意:需要信用卡。但由于疫情影响,AWS推出了 Educate 服务,因此您或许可以无需信用卡即可注册账户。不过,本教程发布到云端后将完全免费。
设置 React 项目
对于 React 前端,我们将使用一个简单的 Create React App (CRA):
运行以下 CLI 命令创建应用并添加 Amplify 库。
npx create-react-app amplify-typescript-demo --template typescript
cd amplify-typescript-demo
npm install --save aws-amplify
设置放大器
请确保已全局安装并配置Amplify CLI
。 官方文档对此有非常详细的说明,甚至还提供了一个视频:安装和配置 Amplify CLI
CLI 配置完成后,我们就可以在项目中初始化 Amplify 了:
amplify init
这条命令会在我们的项目中初始化 Amplify,它需要一些信息。由于我们有一个基本的 CRA 应用,我们可以直接按回车键,使用默认选项继续:
Enter a name for the project `amplifytypescriptdem`
Enter a name for the environment `dev`
Choose your default editor: `Visual Studio Code`
Choose the type of app that you\'re building `javascript`
What javascript framework are you using `react`
Source Directory Path: `src`
Distribution Directory Path: `build`
Build Command: `npm run-script build`
Start Command: `npm run-script start`
Do you want to use an AWS profile? `Yes`
Please choose the profile you want to use `amplify-workshop-use`
添加 GraphQL API
现在可以通过运行以下命令添加 GraphQL API:
amplify add api
首先会问几个问题:
Please select from one of the below mentioned services: `GraphQL`
Provide API name: `DemoAPI`
Choose the default authorization type for the API: `API key`
Enter a description for the API key: My Demo API
After how many days from now the API key should expire (1-365): `7`
Do you want to configure advanced settings for the GraphQL API: `No, I am done.`
Do you have an annotated GraphQL schema? `No`
Do you want a guided schema creation? `Yes`
What best describes your project: `Single object with fields (e.g., “Todo” with ID, name, description)`
Do you want to edit the schema now? `No`
这将生成一个 GraphQL API。打开amplify/backend/api/DemoAPI/schema.graphql即可查看模型。
其中应该包含一个基本的 ToDo 模型:
type Todo @model {
id: ID!
name: String!
description: String
}
模拟和测试 API
API 已准备就绪,可以进行测试了!我们无需手动配置任何 Lambda 函数或 AppSync。一切都由 Amplify 管理。
测试 API 甚至无需将其部署到云端。Amplify 能够模拟整个 API 的本地环境:
amplify mock api
同样,这里也会询问一些问题。接下来是 TypeScript 部分。此调用将为我们的 React 应用自动生成 TypeScript 模型。只需选择typescript默认选项即可:
Choose the code generation language target `typescript`
Enter the file name pattern of graphql queries, mutations and subscriptions `src/graphql/**/*.ts`
Do you want to generate/update all possible GraphQL operations - queries, mutations and subscriptions `Yes`
Enter maximum statement depth [increase from default if your schema is deeply nested] `2`
Enter the file name for the generated code `src/API.ts`
Do you want to generate code for your newly created GraphQL API `Yes`
最后,您应该会收到一条消息,其中包含 API 运行所在的本地地址:
AppSync Mock endpoint is running at http://192.168.0.143:20002
在浏览器中打开该地址,您应该会看到 GraphiQL。
创建并列出待办事项
以下是一些用于创建和测试演示数据的变更和查询:
mutation CreateTodo {
createTodo(
input: { name: "Blog Post", description: "Write a Blog Post about Amplify" }
) {
description
name
}
}
mutation CreateTodo2 {
createTodo(
input: { name: "Dinner", description: "Buy groceries and cook dinner" }
) {
description
name
}
}
query ListTodos {
listTodos {
items {
name
description
}
}
}
在 React 应用中使用 API
第一步是导入 Amplify 并进行配置。该config对象是从 [此处应填写导入路径] 导入的./aws-exports。此文件由 Amplify 生成,请勿手动编辑或将其推送到 GitHub 等平台!
import Amplify from 'aws-amplify';
import config from './aws-exports';
Amplify.configure(config);
封装 Amplify API.graphql
Amplify 提供了调用 GraphQL API 的功能,因此您无需使用像 Apollo-Client 这样的其他 GraphQL 客户端。
只需创建一个小型通用包装器,使其更具类型安全性即可:
import { API, graphqlOperation } from "aws-amplify";
import { GraphQLResult, GRAPHQL_AUTH_MODE } from "@aws-amplify/api";
export interface GraphQLOptions {
input?: object;
variables?: object;
authMode?: GRAPHQL_AUTH_MODE;
}
async function callGraphQL<T>(query: any, options?: GraphQLOptions): Promise<GraphQLResult<T>> {
return (await API.graphql(graphqlOperation(query, options))) as GraphQLResult<T>
}
export default callGraphQL;
该函数callGraphQL<T>是通用的,仅返回结果API.graphql(...)。结果类型为GraphQLResult<T>。如果没有这个小小的包装器,我们就总是需要将其强制转换为GraphQLResult<T>。
查询列表待办事项
创建一个新文件夹src/models,并在其中创建一个文件todo.ts。该文件包含我们待办事项的前端模型以及一个用于映射对象的函数:
import { ListTodosQuery } from "../API";
import { GraphQLResult } from "@aws-amplify/api";
interface Todo {
id?: string;
name?: string;
description?: string;
}
function mapListTodosQuery(listTodosQuery: GraphQLResult<ListTodosQuery>): Todo[] {
return listTodosQuery.data?.listTodos?.items?.map(todo => ({
id: todo?.id,
name: todo?.name,
description: todo?.description
} as Todo)) || []
}
export default Todo;
export { mapListTodosQuery as mapListTodos }
这里发生了什么?首先,我们ListTodosQuery从 '../API' 导入,其中GraphQLResult.API.ts由 Amplify CLI 生成,包含 GraphQL API 类型。GraphQLResult是 GraphQL API 返回的通用接口。
接下来,我们有一个简单的Todo接口和一个函数mapListTodosQuery。该函数将类型中的对象映射GraphQLResult<ListTodosQuery>到我们的ToDo.
使用我们的包装纸
我们终于可以在内部App.tsx使用我们的封装器调用 GraphQL API 了:
import React, { useState, useEffect } from "react";
import { listTodos } from "./graphql/queries";
import { ListTodosQuery } from "./API";
import Todo, { mapListTodos } from "./models/todo";
// omitted Amplify.configure
function App() {
const [todos, setTodos] = useState<Todo[]>();
useEffect(() => {
async function getData() {
try {
const todoData = await callGraphQL<ListTodosQuery>(listTodos);
const todos = mapListTodos(todoData);
setTodos(todos);
} catch (error) {
console.error("Error fetching todos", error);
}
}
getData();
}, []);
return (
<div className="App">
{todos?.map((t) => (
<div key={t.id}>
<h2>{t.name}</h2>
<p>{t.description}</p>
</div>
))}
</div>
);
}
我们使用 Hook 创建一个包含待办事项的状态useState<Todo[]>。
然后,useEffect它用于初始调用 API。由于 API 调用是异步的,因此async function getData()定义了一个异步函数。该函数使用我们之前创建的包装器callGraphQL(),并将泛型类型定义为ListTodosQuery从自动生成的 API.ts 文件中导入的类型。作为参数listTodos传递。这是实际的 GraphQL 查询,也是由 Amplify 自动生成的。结果传递给函数,mapListTodos该函数将以数组形式返回待办事项。之后,状态将被更新。
创建待办事项变更
要发送变更,可以重用包装器:
const name = 'Learn Amplify'
const description = 'Start first Amplify project'
const response = await callGraphQL<CreateTodoMutation>(createTodo, {
input: { name, description },
} as CreateTodoMutationVariables);
需要导入以下类型:: CreateTodoMutationmutation 返回值的类型createTodo:GraphQL Mutation CreateTodoMutationVariables:: 传入的参数类型。这是一个包含一个input属性的对象,该属性本身也是一个对象,其中包含我们新 ToDo 的属性。
订阅
订阅功能支持实时更新。每当创建新的待办事项时,订阅都会发出该待办事项通知。我们可以用这个新待办事项更新待办事项列表。
为此,我们创建了一个通用接口SubscriptionValue:
interface SubscriptionValue<T> {
value: { data: T };
}
我们还需要一个用于待办事项模型的映射函数:
function mapOnCreateTodoSubscription(createTodoSubscription: OnCreateTodoSubscription): Todo {
const { id, name, description } = createTodoSubscription.onCreateTodo || {};
return {
id, name, description
} as Todo
}
我们再App.tsx添加一个useEffect用于处理订阅的功能:
import Todo, { mapOnCreateTodoSubscription } from './models/todo';
import { SubscriptionValue } from './models/graphql-api';
import { onCreateTodo } from './graphql/subscriptions';
useEffect(() => {
// @ts-ignore
const subscription = API.graphql(graphqlOperation(onCreateTodo)).subscribe({
next: (response: SubscriptionValue<OnCreateTodoSubscription>) => {
const todo = mapOnCreateTodoSubscription(response.value.data);
console.log(todo);
setTodos([...todos, todo]);
},
});
return () => subscription.unsubscribe();
});
这可能是使用 TypeScript 调用 GraphQL API 时最困难的部分。
函数Api.graphql(...)返回类型来自Promise<GraphQLResult> | Observable<object>
只有 `Amplify`类Observable才有这个subscribe函数。如果没有 `Amplify` 类,@ts-ignoreTypeScript 编译器会报错,提示该函数subscribe在类型 `Amplify` 上不存在Promise<GraphQLResult> | Observable<object>。
遗憾的是,我们不能简单地通过 `Amplify` 类进行强制转换,as Observable因为 Amplify SDK 没有导出 ` ObservableAmplify` 类。GitHub 上已经有一个相关的issue了。
订阅函数本身接受一个对象作为参数next,该对象带有一个属性,需要一个函数来接收新待办事项(可以将其视为回调函数)。
该函数的参数类型为 ` ToDo` SubscriptionValue<OnCreateTodoSubscription。将其传递response.value.data给该mapOnCreateTodoSubscription函数,该函数将返回待办事项。之后,状态将更新为新的待办事项。最后,在返回语句中,当组件卸载时,订阅将被取消,以避免内存泄漏。
这段代码看起来可能有点冗长。它可以重构为一个包装函数,就像以下callGraphQL函数一样:
function subscribeGraphQL<T>(subscription: any, callback: (value: T) => void) {
//@ts-ignore
return API.graphql(graphqlOperation(subscription)).subscribe({
next: (response: SubscriptionValue<T>) => {
callback(response.value.data);
},
});
}
这又是一个通用函数,它会返回订阅信息。它接受一个参数subscription和一个回调函数。该回调callback函数会在下一个处理程序中被调用,并response.value.data作为参数传递。
订阅功能useEffect可以重构为:
const onCreateTodoHandler = (
createTodoSubscription: OnCreateTodoSubscription
) => {
const todo = mapOnCreateTodoSubscription(createTodoSubscription);
setTodos([...todos, todo]);
};
useEffect(() => {
const subscription =
subscribeGraphQL <
OnCreateTodoSubscription >
(onCreateTodo, onCreateTodoHandler);
return () => subscription.unsubscribe();
}, [todos]);
它onCreateTodoHandler负责调用映射函数并使用新的待办事项更新状态。
在useEffect新包装函数中,我们只调用它subscribeGraphQL,并传递onCreateTodo订阅和我们的值onCreateTodoHandler。与之前一样,当组件卸载时,订阅会被取消。
概括
Amplify 可以快速搭建 GraphQL API,甚至可以自动生成前端的 TypeScript 代码。通过一些封装函数,可以减少样板代码并实现类型安全。
完整的源代码已上传至GitHub。
欢迎留言!:)
文章来源:https://dev.to/appfield/how-to-use-an-aws-amplify-graphql-api-with-a-react-typescript-frontend-2g79