Hasura를 사용하여 몇 분 내에 안전한 GraphQL 애플리케이션 구축
35989 단어 reactgraphqlsecurityjavascript
1. 백엔드 시작
Hasura으로 가서 계정을 만듭니다.Hasura가 데이터베이스 연결을 요청하면 "Heroku를 사용하여 무료 데이터베이스 시도"를 선택하십시오.
Heroku를 사용하여 원키 설정을 완료하면 https://<YOUR_HEROKU_PROJECT>.herokuapp.com
과 유사한 URL에서 새 실례와 데이터베이스가 실행됩니다.프로젝트 탭으로 이동하여 시작 콘솔을 클릭하여 응용 프로그램의 Hasura 대시보드를 엽니다.
2. 데이터 테이블
데이터 탭으로 이동한 다음 테이블 만들기 를 클릭합니다.테이블을 todos
으로 명명하고 두 열을 추가합니다. 예를 들어 다음과 같습니다.
데이터 탭으로 이동한 다음 테이블 만들기 를 클릭합니다.테이블을
todos
으로 명명하고 두 열을 추가합니다. 예를 들어 다음과 같습니다.id :: Integer (auto-incremented)
title :: Text
is_completed :: Boolean
user_id :: Text
created_at :: Timestamp
id
열을 유일한 키로 표시하고 키로 설정하는 것을 기억하십시오!3. 인증
Hasura에 대해 당신이 알아야 할 것은 신분 검증을 대표한다는 것입니다.이것은 귀하가 원하는 제3자 인증 공급자를 사용할 수 있음을 의미합니다.이 설명서에서 Feather는 모든 애플리케이션에 인증과 사용자 계정을 쉽게 추가할 수 있는 경량 API입니다.
Feather와 Hasura가 함께 작업하기 위해서는 HASURA_GRAPHQL_JWT_SECRET
이라는 특수 환경 변수를 설정해야 합니다.이것은 Hasura가 Feather가 발행한 사용자 ID 영패(실제로는 JWTs)를 검증하는 방법을 알려줄 것이다.따라서 Feather에 방문하여 새 프로젝트를 등록하십시오.항목을 작성한 후 Feather 대시보드의 설정 탭으로 이동하여 항목 ID를 복사합니다.
Feather 프로젝트 ID를 아래 JSON 템플릿의 "audience"
필드에 삽입합니다.
{
"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 영패 탭으로 이동합니다.다음 JSON 구조를 복사하여 Custom Claims라는 제목이 붙은 텍스트 상자에 붙여넣습니다.저장 버튼을 클릭하여 변경 사항을 커밋합니다.
{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "{{.USER.ID}}",
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user"
}
}
지금부터 이 프로그램에 로그인한 모든 사용자는 Hasura에 요청할 때 "user"
개의 역할을 얻게 됩니다.이렇게 하면 "user"
역할에 데이터 액세스 규칙을 설정하여 사용자가 자신의 TODO를 만들고, 액세스하고, 수정할 수 있도록 합니다.
따라서 Hasura 대시보드의 데이터 옵션으로 돌아가 todos 테이블의 권한 하위 옵션으로 이동합니다."user"
이라는 새 역할을 추가한 다음 삽입 작업을 클릭하여 편집합니다.우선 "user_id"
열에 열 사전 설정을 추가하여 "X-Hasura-User-Id"
으로 설정합니다.이것은 누군가가 새로운 대기항목을 만들 때마다 Hasura가 자동으로 새 줄에 사용자 ID를 설정한다는 것을 의미한다.멋있지!?😎
선택, 업데이트, 삭제 작업에 사용자 정의 검사를 추가함으로써 인증을 끝냅니다.만약 호출자의 "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
을 엽니다.우리가 해야 할 첫 번째 일은 현재 사용자가 로그인했는지 확인하는 것이다.없으면 인증 양식을 표시합니다.그렇지 않으면, 사용자의 대기 사항을 열거합시다.
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에는 사용자 정의 스타일을 제공할 수 있는 사전 구성된 인증 양식이 포함되어 있습니다.이것은 새로운 프로젝트를 설정할 때 많은 시간을 절약할 수 있다. 왜냐하면 그것은 심지어 어떤 추가 작업도 필요하지 않은 상황에서 비밀번호 리셋 같은 일을 처리할 수 있기 때문이다.⚡️
이제 사용자에게 TODO를 보는 방법을 추가합니다.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 요청을 직접 보내는 방법에 유의하십시오!다음은 사용자가 TODO를 편집할 수 있는 방법이 필요합니다.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를 만들 수 없다면, 이 todo 응용 프로그램은 매우 유용하지 않을 것입니다!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의 또 다른 특성은 실시간 세립도 등급 권한 수여 제어를 제공한다는 것이다!🤯 설정하려면 항목의 Feather 대시보드에서 ID 영패 탭으로 이동합니다.다음 JSON 구조를 복사하여 Custom Claims라는 제목이 붙은 텍스트 상자에 붙여넣습니다.저장 버튼을 클릭하여 변경 사항을 커밋합니다.
{
"https://hasura.io/jwt/claims": {
"x-hasura-user-id": "{{.USER.ID}}",
"x-hasura-allowed-roles": ["user"],
"x-hasura-default-role": "user"
}
}
지금부터 이 프로그램에 로그인한 모든 사용자는 Hasura에 요청할 때 "user"
개의 역할을 얻게 됩니다.이렇게 하면 "user"
역할에 데이터 액세스 규칙을 설정하여 사용자가 자신의 TODO를 만들고, 액세스하고, 수정할 수 있도록 합니다.따라서 Hasura 대시보드의 데이터 옵션으로 돌아가 todos 테이블의 권한 하위 옵션으로 이동합니다.
"user"
이라는 새 역할을 추가한 다음 삽입 작업을 클릭하여 편집합니다.우선 "user_id"
열에 열 사전 설정을 추가하여 "X-Hasura-User-Id"
으로 설정합니다.이것은 누군가가 새로운 대기항목을 만들 때마다 Hasura가 자동으로 새 줄에 사용자 ID를 설정한다는 것을 의미한다.멋있지!?😎선택, 업데이트, 삭제 작업에 사용자 정의 검사를 추가함으로써 인증을 끝냅니다.만약 호출자의
"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
을 엽니다.우리가 해야 할 첫 번째 일은 현재 사용자가 로그인했는지 확인하는 것이다.없으면 인증 양식을 표시합니다.그렇지 않으면, 사용자의 대기 사항을 열거합시다.
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에는 사용자 정의 스타일을 제공할 수 있는 사전 구성된 인증 양식이 포함되어 있습니다.이것은 새로운 프로젝트를 설정할 때 많은 시간을 절약할 수 있다. 왜냐하면 그것은 심지어 어떤 추가 작업도 필요하지 않은 상황에서 비밀번호 리셋 같은 일을 처리할 수 있기 때문이다.⚡️
이제 사용자에게 TODO를 보는 방법을 추가합니다.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 요청을 직접 보내는 방법에 유의하십시오!다음은 사용자가 TODO를 편집할 수 있는 방법이 필요합니다.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를 만들 수 없다면, 이 todo 응용 프로그램은 매우 유용하지 않을 것입니다!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
이 문제에 관하여(Hasura를 사용하여 몇 분 내에 안전한 GraphQL 애플리케이션 구축), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/nickgarfield/building-a-secure-graphql-app-in-minutes-with-hasura-58fc텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)