使用 Hasura 在几分钟内构建一个安全的 GraphQL 应用程序 1. 启动后端 2. 数据表 3. 身份验证 4. 授权 5. 前端总结

2025-06-10

使用 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>"
}
Enter fullscreen mode Exit fullscreen mode

现在要创建环境变量,请前往项目的 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"
  }
}
Enter fullscreen mode Exit fullscreen mode

从现在开始,每个登录该应用程序的用户"user"在向 Hasura 发起请求时都会获得一个角色。这将允许我们在角色上设置数据访问规则,"user"以确保用户只能创建、访问和修改自己的待办事项。

因此,请返回Hasura 仪表板上的“数据”选项卡,并导航到待办事项表上的“权限"user"”子选项卡。添加一个名为 的新角色,然后单击插入操作进行编辑。首先,在该"user_id"列上添加一个预设列,并将其设置为"X-Hasura-User-Id"。这意味着每当有人创建新的待办事项时,Hasura 都会自动在新行上设置用户 ID。很酷吧!?😎

替代文本

让我们通过在selectupdatedelete操作上添加自定义检查来完成授权。只有当调用者的字段与被操作行的列"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
Enter fullscreen mode Exit fullscreen mode

现在,用你喜欢的文本编辑器打开项目,并创建一个名为 的新文件src/feather.js。从项目的 Feather 仪表盘复制可发布的 API 密钥,并使用它来初始化 Feather 客户端。

替代文本

import {FeatherClient} from "feather-client-react"

export const feather = FeatherClient("pk_live_...")
Enter fullscreen mode Exit fullscreen mode

现在我们已经完成了与 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)
});
Enter fullscreen mode Exit fullscreen mode

现在让我们将这些客户端连接到 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();
Enter fullscreen mode Exit fullscreen mode

现在所有通信部分都已准备就绪,让我们刷新可视化组件。打开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;
Enter fullscreen mode Exit fullscreen mode

请注意,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;
Enter fullscreen mode Exit fullscreen mode

注意,我们如何使用 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>
  );
}
Enter fullscreen mode Exit fullscreen mode

最后,如果用户无法创建待办事项,这个待办事项应用就没什么用了!创建一个名为 的新文件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;
Enter fullscreen mode Exit fullscreen mode

最后(但同样重要!),让我们给应用添加一些样式,让它看起来更美观。打开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;
}
Enter fullscreen mode Exit fullscreen mode

总结

呼!代码真多!不过如果你已经跟着做了,可以回到终端,在yarn start本地运行该应用。

让我们回顾一下我们所做的一切:

  1. 将 PostgreSQL 数据库实例部署到 Heroku。
  2. 使用 Hasura 在该数据库上生成 GraphQL API。
  3. 设置 Feather 以提供身份验证并发布用户角色。
  4. 添加授权规则以确保用户只能访问自己的数据。
  5. 使用 Feather 和 Apollo 创建了一个前端 React 应用程序。

您可以在 Github 上查看此应用的完整代码库。有关此应用中使用的每种技术的深入指南,您可以查看各自的文档:

鏂囩珷鏉ユ簮锛�https://dev.to/nickgarfield/building-a-secure-graphql-app-in-minutes-with-hasura-58fc
PREV
开发人员的生产力可以衡量吗?引言:专家们一致认为,那么我们该怎么做呢?一个温和的建议:结论
NEXT
什么是 useEffect hook?如何使用它?内容 useEffect hook 简介。哪些参数会被传递给 useEffect hook?将函数作为依赖项传递 参考