发布于 2025-12-10 4 阅读
0

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

使用 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