数分で安全なGraphSQLアプリの構築
35749 ワード
HASURAは、PostgreSQLデータベースにおいて、データ用のリアルタイムのGraphSQL APIを自動的にスピンさせるための、驚くべきサービスです.このガイドでは、我々は安全なToDoリストアプリケーションを構築するためにハハラを使用します.ここで我々はビルドするアプリの簡単なプレビューです✅
1 .バックエンドを起動する
覚えておいてください
認証
HerokuにPostgreSQLデータベースインスタンスを展開します. hasuraを使用して、そのデータベース上のGraphSQL APIを生成します. 認証を提供し、ユーザロールを発行するために羽を設定します. ユーザーが自分のデータにのみアクセスできるようにするための認証規則の追加. 羽とアポロを使用してフロントエンド反応アプリを作成しました. あなたは、このアプリのための完全なコードベースをチェックアウトすることができますon Github . このアプリで使用される各技術の詳細ガイドについては、以下の個々のドキュメントをチェックアウトできます. Hasura docs Feather docs Apollo docs
1 .バックエンドを起動する
ヘッドオーバーHasura アカウントを作成します.Hasuraがあなたのデータベースを接続するように頼むとき、「Herokuで無料のデータベースをためしてください」を選んでください.
Herokuを使用してワンクリックのセットアップを次の後、新しいインスタンスとデータベースに似ているURLで実行する必要がありますhttps://<YOUR_HEROKU_PROJECT>.herokuapp.com
. 「プロジェクト」タブに移動し、「コンソールを起動」をクリックしてアプリケーションのHasuraダッシュボードを開きます.
2 .データテーブル
データタブに移動し、「表を作成」をクリックします.テーブル名を挙げましょうtodos
以下のような2つのカラムを追加します.
データタブに移動し、「表を作成」をクリックします.テーブル名を挙げましょう
todos
以下のような2つのカラムを追加します.id :: Integer (auto-incremented)
title :: Text
is_completed :: Boolean
user_id :: Text
created_at :: Timestamp
id
ユニークな列として、主キーとして設定!認証
あなたがHasuraについて知っている必要がある1つのものは、それが認証を代表するということです.これは、任意のサードパーティ製のAuthプロバイダを使用することができます.このガイドのために、我々は簡単に任意のアプリケーションに認証とユーザーアカウントを追加するための軽量APIである羽を使用します.
HASURAで作業するためには、特別な環境変数を設定する必要がありますHASURA_GRAPHQL_JWT_SECRET
. これはHasuraにフェザーによって発行されたユーザIDトークンを検証する方法を教えますJWTs フードの下で.だから頭にFeather 新しいプロジェクトを登録します.プロジェクトの作成後、フェザーダッシュボードの[設定]タブに移動し、プロジェクトIDをコピーします.
あなたの羽プロジェクト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>
, タブに移動し、config - varsというタイトルのセクションを見つけます.新しい値を作成するHASURA_GRAPHQL_JWT_SECRET
とJSON値にペーストします.
大丈夫、セットアップ認証!羽村の認定制度で羽毛を引っ張りましょう.
認可
Hasuraの特徴のもう一つは、それが箱からまっすぐに制御される細粒度の行レベル承認を提供するということです!🤯 設定するには、プロジェクトの羽ダッシュボードのIDトークンタブに移動します.以下のJSON構造体をコピーし、カスタム・クレームというテキストボックスに貼り付けます.ボタンをクリックして変更をコミットします.
{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "{{.USER.ID}}",
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user"
}
}
これからは、アプリにログインするすべてのユーザーが発行される"user"
hasuraへのリクエストの開始時のロールこれにより、データアクセス規則の設定が可能になります"user"
役割は、ユーザーが唯一の作成、アクセス、および独自のtodosを変更できるようにする.
したがって、Hasuraダッシュボードのデータタブに戻り、ToDOSテーブルのパーミッションサブタブに移動します.新しい役割を加える"user"
挿入操作をクリックして編集します.列にプリセットを追加することから始めましょう"user_id"
設定するカラム"X-Hasura-User-Id"
. これは誰かが新しいtodoを作成するたびに、hasuraは自動的に新しい行のユーザーIDを設定します.クール、右!?😎
カスタムチェックを選択、更新、および削除操作に追加することで、認証をラップしましょう.呼び出し元の場合、これらの操作のうちの1つだけを承認します"X-Hasura-User-Id"
マッチ"user_id"
操作する行の列.
PostgreSQLデータベース、GraphSQL API、ユーザー認証、および行レベルの認証を完全にバックエンド全体に設定するだけで、コードの1行を書くことなく!すべてのアプリの楽しい部分で仕上げましょう:フロントエンド!🥳
フロントエンド
端末を開いて、きれいなディレクトリを見つけて、以下のコマンドを実行し、必要なすべての依存関係を持つ新しい反応アプリケーションをスキャフォールドする.
$ 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
. プロジェクトの羽のダッシュボードから発行可能なAPIキーをコピーし、フェザークライアントを初期化するために使用します.
import {FeatherClient} from "feather-client-react"
export const feather = FeatherClient("pk_live_...")
今、私たちは、すべての設定は、フェザーAPIと話をしている私たちのhasura APIに要求を送信するためにGraphSQLクライアントを作成しましょう.このために、我々は使用します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)
});
今すぐアプリを使用することができますので、反応コンポーネントツリーにこれらのクライアントをフックしましょう.オープン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
. まず最初にするのは、現在のユーザがサインされているかどうかを調べるための羽のチェックです.そうでない場合は、認証フォームを表示します.さもなければ、ユーザのtodosをリストしましょう.
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;
通知羽は、カスタムスタイルを与えることができる事前に構築された認証フォームが付属しています.これは、新しいプロジェクトを設定するときにも、追加の仕事なしでパスワードのリセットのようなものを処理するための時間の束を節約!⚡️
ここで、ユーザが自分のトツを見る方法を追加しましょう.新しいファイルを作成する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;
どのように我々は単に反応から直接GraphSQLの要求を送信するアポロを使用することができます注意してください!次に、ユーザが自分のtodosを編集する方法が必要です.新しいファイルを作成する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>
);
}
最後に、このTODOアプリは非常に便利なユーザーがtodosを作成することはできません!新しいファイルを作成する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
ローカルアプリケーションを実行します.
我々がしたすべてを見ましょう:
{
"type":"RS256",
"jwk_url": "https://api.feather.id/v1/.well-known/jwks",
"issuer": "api.feather.id",
"audience": "<YOUR_PROJECT_ID>"
}
Hasuraの特徴のもう一つは、それが箱からまっすぐに制御される細粒度の行レベル承認を提供するということです!🤯 設定するには、プロジェクトの羽ダッシュボードのIDトークンタブに移動します.以下のJSON構造体をコピーし、カスタム・クレームというテキストボックスに貼り付けます.ボタンをクリックして変更をコミットします.
{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "{{.USER.ID}}",
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user"
}
}
これからは、アプリにログインするすべてのユーザーが発行される"user"
hasuraへのリクエストの開始時のロールこれにより、データアクセス規則の設定が可能になります"user"
役割は、ユーザーが唯一の作成、アクセス、および独自のtodosを変更できるようにする.したがって、Hasuraダッシュボードのデータタブに戻り、ToDOSテーブルのパーミッションサブタブに移動します.新しい役割を加える
"user"
挿入操作をクリックして編集します.列にプリセットを追加することから始めましょう"user_id"
設定するカラム"X-Hasura-User-Id"
. これは誰かが新しいtodoを作成するたびに、hasuraは自動的に新しい行のユーザーIDを設定します.クール、右!?😎カスタムチェックを選択、更新、および削除操作に追加することで、認証をラップしましょう.呼び出し元の場合、これらの操作のうちの1つだけを承認します
"X-Hasura-User-Id"
マッチ"user_id"
操作する行の列.PostgreSQLデータベース、GraphSQL API、ユーザー認証、および行レベルの認証を完全にバックエンド全体に設定するだけで、コードの1行を書くことなく!すべてのアプリの楽しい部分で仕上げましょう:フロントエンド!🥳
フロントエンド
端末を開いて、きれいなディレクトリを見つけて、以下のコマンドを実行し、必要なすべての依存関係を持つ新しい反応アプリケーションをスキャフォールドする.
$ 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
. プロジェクトの羽のダッシュボードから発行可能なAPIキーをコピーし、フェザークライアントを初期化するために使用します.
import {FeatherClient} from "feather-client-react"
export const feather = FeatherClient("pk_live_...")
今、私たちは、すべての設定は、フェザーAPIと話をしている私たちのhasura APIに要求を送信するためにGraphSQLクライアントを作成しましょう.このために、我々は使用します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)
});
今すぐアプリを使用することができますので、反応コンポーネントツリーにこれらのクライアントをフックしましょう.オープン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
. まず最初にするのは、現在のユーザがサインされているかどうかを調べるための羽のチェックです.そうでない場合は、認証フォームを表示します.さもなければ、ユーザのtodosをリストしましょう.
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;
通知羽は、カスタムスタイルを与えることができる事前に構築された認証フォームが付属しています.これは、新しいプロジェクトを設定するときにも、追加の仕事なしでパスワードのリセットのようなものを処理するための時間の束を節約!⚡️
ここで、ユーザが自分のトツを見る方法を追加しましょう.新しいファイルを作成する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;
どのように我々は単に反応から直接GraphSQLの要求を送信するアポロを使用することができます注意してください!次に、ユーザが自分のtodosを編集する方法が必要です.新しいファイルを作成する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>
);
}
最後に、このTODOアプリは非常に便利なユーザーがtodosを作成することはできません!新しいファイルを作成する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
ローカルアプリケーションを実行します.
我々がしたすべてを見ましょう:
$ 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
import {FeatherClient} from "feather-client-react"
export const feather = FeatherClient("pk_live_...")
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)
});
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();
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;
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;
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>
);
}
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;
.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
ローカルアプリケーションを実行します.我々がしたすべてを見ましょう:
Reference
この問題について(数分で安全なGraphSQLアプリの構築), 我々は、より多くの情報をここで見つけました https://dev.to/nickgarfield/building-a-secure-graphql-app-in-minutes-with-hasura-58fcテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol