使用 Hasura 在几分钟内构建一个安全的 GraphQL 应用程序
1.启动后端
2. 数据表
3. 身份验证
4.授权
5. 前端
总结
Hasura 是一项非常棒的服务,您可以将其指向任何 PostgreSQL 数据库,自动为您的数据启动实时 GraphQL API。在本指南中,我们将使用 Hasura 构建一个安全的待办事项列表应用。以下是我们将要构建的应用的快速预览✅
1.启动后端
前往Hasura并创建一个帐户。当 Hasura 要求连接数据库时,选择“使用 Heroku 试用免费数据库”。
按照 Heroku 的一键设置操作后,您将拥有一个运行在类似于 的 URL 上的新实例和数据库https://<YOUR_HEROKU_PROJECT>.herokuapp.com
。导航到“项目”选项卡,然后单击“启动控制台”以打开应用的 Hasura 仪表板。
2. 数据表
导航到“数据”选项卡,然后单击“创建表”。让我们为该表命名todos
并添加几列,例如:
id :: Integer (auto-incremented)
title :: Text
is_completed :: Boolean
user_id :: Text
created_at :: Timestamp
记住将该列标记id
为唯一并将其设置为主键!
3. 身份验证
关于 Hasura,你需要知道的一件事是它支持委托身份验证。这意味着你可以使用任何第三方身份验证提供商。在本指南中,我们将使用 Feather,它是一个轻量级 API,可以轻松地为任何应用添加身份验证和用户帐户。
为了让 Feather 与 Hasura 协同工作,我们需要设置一个名为 的特殊环境变量HASURA_GRAPHQL_JWT_SECRET
。这将告诉 Hasura 如何验证 Feather 颁发的用户 ID 令牌(它们实际上是JWT)。因此,请前往Feather并注册一个新项目。创建项目后,导航到Feather 仪表板上的“设置”选项卡并复制项目 ID。
将您的 Feather 项目 ID 插入"audience"
下面的 JSON 模板字段。
{
"type":"RS256",
"jwk_url": "https://api.feather.id/v1/.well-known/jwks",
"issuer": "api.feather.id",
"audience": "<YOUR_PROJECT_ID>"
}
现在要创建环境变量,请前往项目的 Heroku 仪表板https://dashboard.heroku.com/apps/<YOUR_HEROKU_APP>
,导航到“设置”选项卡,然后找到标题为“配置变量”的部分。创建一个名为 的新值HASURA_GRAPHQL_JWT_SECRET
并粘贴 JSON 值。
好了,身份验证设置好了!接下来,让我们将 Feather 与 Hasura 的授权系统连接起来。
4.授权
Hasura 的另一个功能是它提供了开箱即用的细粒度行级授权控制!🤯 要进行设置,请导航到项目 Feather 仪表板上的“ID Tokens”选项卡。复制下面的 JSON 结构并将其粘贴到标题为“Custom Claims”的文本框中。单击“Save”按钮提交更改。
{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "{{.USER.ID}}",
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user"
}
}
从现在开始,每个登录该应用程序的用户"user"
在向 Hasura 发起请求时都会获得一个角色。这将允许我们在角色上设置数据访问规则,"user"
以确保用户只能创建、访问和修改自己的待办事项。
因此,请返回Hasura 仪表板上的“数据”选项卡,并导航到待办事项表上的“权限"user"
”子选项卡。添加一个名为 的新角色,然后单击插入操作进行编辑。首先,在该"user_id"
列上添加一个预设列,并将其设置为"X-Hasura-User-Id"
。这意味着每当有人创建新的待办事项时,Hasura 都会自动在新行上设置用户 ID。很酷吧!?😎
让我们通过在select、update和delete操作上添加自定义检查来完成授权。只有当调用者的字段与被操作行的列"X-Hasura-User-Id"
匹配时,我们才会授权其中一个操作。"user_id"
你只需设置一个完整的后端,包括 PostgreSQL 数据库、GraphQL API、用户身份验证和行级授权,无需编写任何代码!让我们来结束每个应用程序最有趣的部分:前端!🥳
5. 前端
打开终端,找到一个干净的目录,然后运行以下命令来构建一个包含我们需要的所有依赖项的新 React 应用程序:
$ npx create-react-app hasurademo && cd hasurademo && yarn add @apollo/client apollo-link-context apollo-link-http apollo-cache-inmemory feather-client-react graphql graphql-tag
现在,用你喜欢的文本编辑器打开项目,并创建一个名为 的新文件src/feather.js
。从项目的 Feather 仪表盘复制可发布的 API 密钥,并使用它来初始化 Feather 客户端。
import {FeatherClient} from "feather-client-react"
export const feather = FeatherClient("pk_live_...")
现在我们已经完成了与 Feather API 通信的设置,接下来创建一个 GraphQL 客户端来向 Hasura API 发送请求。为此,我们将使用Apollo。创建一个名为 的新文件src/apollo.js
,并添加以下代码:
import { ApolloClient } from "@apollo/client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { HttpLink } from "apollo-link-http";
import { setContext } from "apollo-link-context";
import { feather } from "./feather";
const httpLink = new HttpLink({
uri: "https://hasura-test-pliao.herokuapp.com/v1/graphql",
fetchPolicy: "network-only"
});
const authLink = setContext((_, { headers }) =>
feather
.currentUser()
.then(u => ({
headers: {
...headers,
authorization: `Bearer ${u.tokens.idToken}`
}
}))
.catch(_ => ({ headers }))
);
export const apollo = new ApolloClient({
cache: new InMemoryCache(),
link: authLink.concat(httpLink)
});
现在让我们将这些客户端连接到 React 组件树,以便应用程序可以使用它们。打开src/index.js
并添加以下代码:
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import * as serviceWorker from "./serviceWorker";
import { FeatherProvider } from "feather-client-react";
import { feather } from "./feather";
import { ApolloProvider } from "@apollo/client";
import { apollo } from "./apollo";
ReactDOM.render(
<React.StrictMode>
<FeatherProvider client={feather}>
<ApolloProvider client={apollo}>
<App />
</ApolloProvider>
</FeatherProvider>
</React.StrictMode>,
document.getElementById("root")
);
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: https://bit.ly/CRA-PWA
serviceWorker.unregister();
现在所有通信部分都已准备就绪,让我们刷新可视化组件。打开src/App.js
。我们要做的第一件事是检查 Feather,看看当前用户是否已登录。如果没有,我们将显示身份验证表单。否则,让我们列出用户的待办事项。
import React from "react";
import { AuthenticationForm, useCurrentUser } from "feather-client-react";
import Todos from "./Todos";
const styles = {
title: provided => ({
...provided,
fontSize: "40px",
fontWeight: 700
})
};
function App(props) {
const { loading, currentUser } = useCurrentUser();
if (loading) return <div />;
if (!currentUser)
return (
<div className="app">
<AuthenticationForm styles={styles} />
</div>
);
return (
<div className="app">
<div className="app-header">
<h1>My to-do list</h1>
<p>{currentUser.email}</p>
</div>
<Todos />
</div>
);
}
export default App;
请注意,Feather 自带预建的身份验证表单,您可以自定义样式。这在设置新项目时可以节省大量时间,因为它甚至可以处理诸如密码重置之类的操作,无需任何额外操作!⚡️
现在让我们为用户添加一种查看待办事项的方式。创建一个名为 的新文件src/Todos.js
并添加以下代码:
import React from "react";
import Todo from "./Todo";
import NewTodo from "./NewTodo";
import { useQuery, gql } from "@apollo/client";
export const GET_TODOS = gql`
query GetTodos {
todos {
id
title
is_completed
}
}
`;
function Todos(props) {
const { loading, error, data } = useQuery(GET_TODOS);
if (error) return <p>{error.message}</p>;
if (loading) return <p>Loading ...</p>;
return (
<div>
{data.todos.map(todo => (
<Todo key={todo.id} todo={todo} />
))}
<NewTodo />
</div>
);
}
export default Todos;
注意,我们如何使用 Apollo 直接从 React 发送 GraphQL 请求!接下来,我们需要为用户提供一种编辑待办事项的方式。创建一个名为 的新文件src/Todo.js
,并添加以下代码:
import React from "react";
import { useMutation, gql } from "@apollo/client";
const TOGGLE_TODO = gql`
mutation ToggleTodo($id: Int!, $is_completed: Boolean!) {
update_todos(
where: { id: { _eq: $id } }
_set: { is_completed: $is_completed }
) {
returning {
id
is_completed
}
}
}
`;
export default function Todo(props) {
const [toggleTodo] = useMutation(TOGGLE_TODO);
const onChange = e => {
toggleTodo({
variables: {
id: props.todo.id,
is_completed: !props.todo.is_completed
}
});
};
return (
<div style={{ display: "flex", flexDirection: "row" }}>
<input
type="checkbox"
className="todo-checkbox"
name={props.todo.id}
checked={props.todo.is_completed}
onChange={onChange}
/>
<p>{props.todo.title}</p>
</div>
);
}
最后,如果用户无法创建待办事项,这个待办事项应用就没什么用了!创建一个名为 的新文件src/NewTodo.js
,并添加以下代码:
import React, { useState } from "react";
import { useMutation, gql } from "@apollo/client";
const CREATE_TODO = gql`
mutation CreateTodo($title: String!) {
insert_todos_one(object: { title: $title }) {
id
title
is_completed
}
}
`;
function NewTodo(props) {
const [title, setTitle] = useState("");
const [createTodo] = useMutation(CREATE_TODO);
const onSubmit = e => {
e.preventDefault();
createTodo({ variables: { title } });
};
const onChange = e => {
setTitle(e.target.value);
};
return (
<form onSubmit={onSubmit}>
<input
className="new-todo-input"
value={title}
onChange={onChange}
type="text"
placeholder="Today I will..."
/>
</form>
);
}
export default NewTodo;
最后(但同样重要!),让我们给应用添加一些样式,让它看起来更美观。打开src/index.css
并添加以下 CSS 类 🎨:
.app {
padding: 80px;
max-width: 400px;
margin: 20px auto;
}
.app-header {
margin-bottom: 40px;
}
.todo-checkbox {
margin: auto 10px auto 0px;
}
.new-todo-input {
font-size: 20px;
padding: 20px;
width: 100%;
margin-top: 40px;
}
总结
呼!代码真多!不过如果你已经跟着做了,可以回到终端,在yarn start
本地运行该应用。
让我们回顾一下我们所做的一切:
- 将 PostgreSQL 数据库实例部署到 Heroku。
- 使用 Hasura 在该数据库上生成 GraphQL API。
- 设置 Feather 以提供身份验证并发布用户角色。
- 添加授权规则以确保用户只能访问自己的数据。
- 使用 Feather 和 Apollo 创建了一个前端 React 应用程序。
您可以在 Github 上查看此应用的完整代码库。有关此应用中使用的每种技术的深入指南,您可以查看各自的文档:
鏂囩珷鏉ユ簮锛�https://dev.to/nickgarfield/building-a-secure-graphql-app-in-minutes-with-hasura-58fc