Vytvoření zabezpečené aplikace GraphQL během několika minut s Hasura

Hasura je úžasná služba, kterou můžete nasměrovat na jakoukoli databázi PostgreSQL, abyste pro svá data automaticky vytvořili rozhraní GraphQL API v reálném čase. V této příručce použijeme Hasura k vytvoření zabezpečené aplikace seznamu úkolů. Zde je rychlý náhled aplikace, kterou vytvoříme ✅

1. Spusťte backend

Přejděte na Hasura a vytvořte si účet. Když Hasura požádá o připojení vaší databáze, zvolte "Vyzkoušet bezplatnou databázi s Heroku" .

Po nastavení jedním kliknutím pomocí Heroku budete mít novou instanci a databázi spuštěnou na adrese URL podobné https://<YOUR_HEROKU_PROJECT>.herokuapp.com . Přejděte na Projekty a klikněte na Spustit konzolu otevřete řídicí panel Hasura vaší aplikace.

2. Tabulky dat

Přejděte do části Data a klikněte na Vytvořit tabulku . Pojmenujme tabulku todos přidejte několik sloupců, například:

  • id :: Integer (auto-incremented)
  • title :: Text
  • is_completed :: Boolean
  • user_id :: Text
  • created_at :: Timestamp

Nezapomeňte označit id sloupec jako jedinečný a nastavte jej jako primární klíč!

3. Ověření

Jedna věc, kterou potřebujete vědět o Hasura, je to, že deleguje ověřování. To znamená, že můžete použít libovolného poskytovatele autorizace třetí strany. V této příručce použijeme Feather, což je lehké rozhraní API pro snadné přidávání ověřování a uživatelských účtů do jakékoli aplikace.

Abychom Feather přiměli pracovat s Hasurou, musíme nastavit speciální proměnnou prostředí nazvanou HASURA_GRAPHQL_JWT_SECRET . To Hasura řekne, jak ověřit tokeny ID uživatele vydané Featherem (jsou to opravdu JWT pod kapotou). Takže zamiřte do over Feather a zaregistrujte nový projekt. Po vytvoření projektu přejděte do Nastavení na panelu Feather a zkopírujte ID projektu.

Zapojte své ID projektu Feather do "audience" pole šablony JSON níže.

{
  "type":"RS256",
  "jwk_url": "https://api.feather.id/v1/.well-known/jwks",
  "issuer": "api.feather.id",
  "audience": "<YOUR_PROJECT_ID>"
}

Chcete-li nyní vytvořit proměnnou prostředí, přejděte na řídicí panel Heroku vašeho projektu na adrese https://dashboard.heroku.com/apps/<YOUR_HEROKU_APP> , přejděte do Nastavení a najděte sekci s názvem Config Vars . Vytvořte novou hodnotu s názvem HASURA_GRAPHQL_JWT_SECRET a vložte hodnotu JSON.

Dobře, nastavili jsme ověřování! Pokračujme připojením Feathera k autorizačnímu systému Hasura.

4. Autorizace

Další z funkcí Hasura je, že poskytuje jemně zrnité autorizační kontroly na úrovni řádků přímo po vybalení! 🤯 Chcete-li jej nastavit, přejděte na ID Tokeny na panelu Feather vašeho projektu. Zkopírujte níže uvedenou strukturu JSON a vložte ji do textového pole s názvem Vlastní nároky . Klikněte na tlačítko Uložit tlačítko pro potvrzení změn.

{
  "https://hasura.io/jwt/claims": {
    "x-hasura-user-id": "{{.USER.ID}}",
    "x-hasura-allowed-roles": ["user"],
    "x-hasura-default-role": "user"
  }
}

Od této chvíle bude každému uživateli, který se přihlásí do aplikace, vydáno "user" roli při zahajování požadavků na Hasura. To nám umožní nastavit pravidla přístupu k datům na "user" aby uživatelé mohli vytvářet, přistupovat a upravovat pouze své vlastní úkoly.

Vraťte se tedy k Údajům na hlavním panelu Hasura a přejděte na Oprávnění podzáložka v tabulce úkolů. Přidejte novou roli s názvem "user" a klikněte na vložit operaci k jeho úpravě. Začněme přidáním přednastaveného sloupce na "user_id" sloupec a nastavte jej na "X-Hasura-User-Id" . To znamená, že kdykoli někdo vytvoří nový úkol, Hasura automaticky nastaví ID uživatele na nový řádek. Super, že!? 😎

Pojďme autorizaci uzavřít přidáním vlastních kontrol na vybrat , aktualizovat a smazat operace. Jednu z těchto operací povolíme pouze v případě, že má volající "X-Hasura-User-Id" odpovídá "user_id" sloupec řádků, se kterými se pracuje.

Stačí nastavit celý backend s databází PostgreSQL, rozhraním GraphQL API, autentizací uživatele a autorizací na úrovni řádků, aniž byste museli psát jediný řádek kódu! Pojďme skončit zábavnou částí každé aplikace:rozhraním! 🥳

5. Rozhraní

Otevřete svůj terminál, najděte čistý adresář a spusťte následující příkazy pro vytvoření nové aplikace React se všemi závislostmi, které budeme potřebovat:

$ 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

Nyní otevřete projekt ve svém oblíbeném textovém editoru a vytvořte nový soubor s názvem src/feather.js . Zkopírujte publikovatelný klíč API z řídicího panelu Feather vašeho projektu a použijte jej k inicializaci klienta Feather.


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

export const feather = FeatherClient("pk_live_...")

Nyní jsme všichni připraveni mluvit s Feather API, pojďme vytvořit klienta GraphQL pro odesílání požadavků do našeho Hasura API. K tomu použijeme Apollo. Vytvořte nový soubor s názvem src/apollo.js a přidejte následující kód:

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)
});

Nyní připojíme tyto klienty ke stromu komponent React, aby je aplikace mohla používat. Otevřete src/index.js a přidejte následující kód:

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();

Nyní, když jsou všechny komunikační prvky připraveny, pojďme spláchnout vizuální komponenty. Otevřete src/App.js . První věc, kterou uděláme, je zkontrolovat Feather, abychom zjistili, zda je aktuální uživatel přihlášen. Pokud ne, zobrazíme ověřovací formulář. Jinak uveďme seznam úkolů uživatele.

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;

Notice Feather přichází s předem vytvořeným ověřovacím formulářem, kterému lze dát vlastní styl. To ušetří spoustu času při nastavování nových projektů, protože to dokonce zvládne věci jako resetování hesla bez jakékoli další práce! ⚡️

Nyní přidáme způsob, jak si uživatel může prohlížet své úkoly. Vytvořte nový soubor s názvem src/Todos.js a přidejte následující kód:

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;

Všimněte si, jak můžeme použít Apollo k odesílání požadavků GraphQL přímo z React! Dále potřebujeme způsob, jak mohou uživatelé upravovat své úkoly. Vytvořte nový soubor s názvem src/Todo.js a přidejte následující kód:

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>
  );
}

Nakonec by tato aplikace pro úkoly nebyla příliš užitečná, pokud by uživatelé nemohli vytvářet úkoly! Vytvořte nový soubor s názvem src/NewTodo.js a přidejte následující kód:

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;

A nakonec (ale v neposlední řadě!), dopřejme aplikaci trochu stylu, aby vypadala hezky. Otevřete src/index.css a přidejte následující třídy 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;
}

Shrnutí

Fuj! To bylo hodně kódu! Ale pokud jste to sledovali, můžete se vrátit zpět do Terminálu a spustit yarn start pro spuštění aplikace lokálně.

Pojďme si zopakovat vše, co jsme udělali:

  1. Nasadili instanci databáze PostgreSQL do Heroku.
  2. Použil Hasura k vygenerování GraphQL API přes tuto databázi.
  3. Nastavte Feather, aby poskytovala ověřování a vydávala uživatelské role.
  4. Přidána pravidla autorizace, aby uživatelé měli přístup pouze ke svým vlastním datům.
  5. Vytvořili frontendovou aplikaci React pomocí Feather a Apollo.

Kompletní kódovou základnu pro tuto aplikaci si můžete prohlédnout na Github. Podrobné průvodce každou z technologií používaných v této aplikaci najdete v jejich individuální dokumentaci na adrese:

  • Dokumenty Hasura
  • Dokumenty Feather
  • Dokumenty Apollo