在 Amplify 中使用 GraphQL 和 Cognito 认证
总结
这是关于首次 Amplify 部署的续篇。
在前一次项目创建时,我们设置了使用 Cognito 进行身份验证,并将必要的配置放入了 Docker 中的模拟环境。
现在我们将实现使用 Cognito 进行用户身份验证,并与 GraphQL 进行协作。
添加用户认证功能。
yarn add aws-amplify aws-amplify-react
为了在创建帐户时不需要电话号码,我们在 withAuthenticator 的 signUpConfig 中添加了 hiddenDefaults。
import React from 'react';
import Amplify from '@aws-amplify/core';
import awsmobile from './aws-exports';
import { withAuthenticator } from 'aws-amplify-react';
import '@aws-amplify/ui/dist/style.css';
Amplify.configure(awsmobile)
const App: React.FC = () => {
return (
<>
<h1>タイトル</h1>
</>
)
}
//@ts-ignore
export default withAuthenticator(App, {
signUpConfig: {
hiddenDefaults: ['phone_number']
}
});

在登录页面上通过点击创建账户按钮来注册账户,并完成邮件验证后即可登录。会显示”标题”。
创建 GraphQL API
首先,让我们尝试一下自动生成的 schema.graphql 中的 TODO 模型。
type Todo @model {
id: ID!
name: String!
description: String
}
使用 Mock 可以在本地进行验证。
$ amplify mock api



React 和 GraphQL 的整合
我将展示生成的数据在屏幕上。
由于这次是在 Docker 中进行开发,所以需要将 aws-exports.js 文件中的 aws_appsync_graphqlEndpoint 改为 localhost(仅限本地开发时)。
"aws_appsync_graphqlEndpoint": "http://localhost:20002/graphql",
将App.tsx进行如下更改:
显示保存在Todo中的一项数据。
import React, {useState} from 'react'
import Amplify from '@aws-amplify/core'
import awsmobile from './aws-exports'
import {withAuthenticator} from 'aws-amplify-react'
import '@aws-amplify/ui/dist/style.css'
import {listTodos} from './graphql/queries'
import API, {graphqlOperation} from '@aws-amplify/api'
import { Auth } from 'aws-amplify';
Amplify.configure(awsmobile)
const App: React.FC = () => {
const [todos, setTodos] = useState<any>({})
const getTodo = async() => {
try {
const res: any = await API.graphql(graphqlOperation(listTodos))
console.log(res)
setTodos(res.data.listTodos.items)
} catch (e) {
console.log(e)
}
}
const ShowTodo = () => {
if (todos.length > 0) {
const todo = todos[0]
return (
<>
<p>{ todo.id }</p>
<p>{ todo.name }</p>
<p>{ todo.description }</p>
<p>{ todo.createdAt }</p>
<p>{ todo.updatedAt }</p>
</>
)
}
return (
<></>
)
}
return (
<>
<h1>Todos</h1>
<button onClick={() => getTodo()}>get todos!</button>
<div>
<ShowTodo/>
</div>
</>
)
}
...

由Cognito团队进行管理

将 schema.graphql 进行如下修改。
type Todo
@model
@auth(rules: [
{ allow: owner },
{ allow: groups, groups: ["admin"], operations: [read, update] }
])
{
id: ID!
name: String!
description: String
}
通过这样做,成员权限的帐户将无法对Todo进行任何操作。
为了登出一次,我们需要将App.tsx修改如下。
import React, {useState} from 'react'
import Amplify from '@aws-amplify/core'
import awsmobile from './aws-exports'
import {withAuthenticator} from 'aws-amplify-react'
import '@aws-amplify/ui/dist/style.css'
import {listTodos} from './graphql/queries'
import API, {graphqlOperation} from '@aws-amplify/api'
import { Auth } from 'aws-amplify';
Amplify.configure(awsmobile)
const App: React.FC = () => {
const [todos, setTodos] = useState<any>({})
const signOut = () => {
Auth.signOut()
}
const getTodo = async() => {
try {
const res: any = await API.graphql(graphqlOperation(listTodos))
console.log(res)
setTodos(res.data.listTodos.items)
} catch (e) {
console.log(e)
}
}
const ShowTodo = () => {
if (todos.length > 0) {
const todo = todos[0]
return (
<>
<p>{ todo.id }</p>
<p>{ todo.name }</p>
<p>{ todo.description }</p>
<p>{ todo.createdAt }</p>
<p>{ todo.updatedAt }</p>
</>
)
}
return (
<></>
)
}
return (
<>
<button onClick={ () => signOut() }>ログアウト</button>
<h1>Todos</h1>
<button onClick={() => getTodo()}>get todos!</button>
<div>
<ShowTodo/>
</div>
</>
)
}
...

此外,我还希望将屏幕显示按组进行分组,因此我在App.tsx中添加了以下内容。
在首次登录时,获取登录用户的组信息,并根据该信息进行显示分组。
在useEffect中,仅在第一次获取用户信息(如果不将[]作为第二个参数,则会导致无限循环)。
const [group, updateGroup] = useState<any>({})
const getUser = async() => {
const user = await Auth.currentAuthenticatedUser();
updateGroup(user.signInUserSession.accessToken.payload["cognito:groups"])
console.log(user.signInUserSession.accessToken.payload["cognito:groups"])
}
useEffect(() => {
getUser()
}, [])
return (
<>
{
group === 'admin' ? (
<h1>管理者画面</h1>
) : (
<h1>メンバー画面</h1>
)
}
<button onClick={ () => signOut() }>ログアウト</button>
<h1>Todos</h1>
<button onClick={() => getTodo()}>get todos!</button>
<div>
<ShowTodo/>
</div>
</>
)
