使用Hasura GraphQL Engine和Vue.js,只需15分钟便能快速创建一个Todo应用,这将使人们无法抗拒

有这么缺乏统一感的多样化圣诞节日历真的好吗…

Hasura是什么?

Hasura是一个与Postgres集成的基于GraphQL的API服务器。
只需按照文档的快速入门操作,即可在2秒内完成Postgres服务器的构建。
它还配备了ARM模板,只需点击一下即可将其部署到Azure上,具有类似于能让IT工程师松懈的豆袋椅的力量。

这次我们会使用Hasura作为API,用Vue构建一个超简单的Todo应用程序,只需要15分钟就可以完成。
由于Hasura会处理所有API方面的工作,所以不需要编写任何代码。

为了防患万一,我随便写了一段代码并放在以下位置,如果发生万一的情况,请点击以下链接查看:
https://github.com/yoshi-sato/hasura_todo_adventcalendar2019

为什么要15分钟?

你和你的伴侣最近开始在东京市海边某处安家,作为一名很厉害的IT工程师。
在一个休息日的傍晚,你们约定去南船橋的宜家购买家具、日用品、零食和其他各种东西。
当你们在宜家讨论要买什么东西时,你注意到需要记下伴侣口中一个接一个说出来的想要的物品。

只能用Hasura和Vue来创建购物清单了……

但是离约定的时间只有短短的15分钟。
由于你是一个遵守承诺的绅士,所以不能说现在才让我等到完成购物清单才出发。
真的能在15分钟内完成Todo应用,并能顺利前往宜家,不失去伴侣的信任吗?

搭建API服务器和数据库服务器。

你匆忙地打开MacBook,开始准备在Docker环境中运行Hasura。
https://docs.hasura.io/1.0/graphql/manual/getting-started/docker-simple.html#step-1-get-the-docker-compose-file

mkdir todo_hasura
cd todo_hasura
wget https://raw.githubusercontent.com/hasura/graphql-engine/master/install-manifests/docker-compose/docker-compose.yaml
docker-compose up -d
docker-compose ps

使用QuickStart中提供的步骤,将其完全复制粘贴到您的终端,并通过一个方便的docker-compose文件启动Hasura和Postgres服务器。
在此过程中,您要注意GraphQL引擎正在8080端口上运行。

            Name                          Command              State           Ports
---------------------------------------------------------------------------------------------
todo_hasura_graphql-engine   graphql-engine serve            Up      0.0.0.0:8080->8080/tcp
todo_hasura_postgres         docker-entrypoint.sh postgres   Up      5432/tcp

然后我悄悄打开了Web浏览器,访问了localhost:8080,并确认了一些东西在运行,感到满足。同时,因为你对端点很感兴趣,所以你会把显示的端点在这里做个记录。

image.png

当你点击打开画面的DATA标签时,你会注意到画面左侧会出现一个名为Add Table的按钮。在此处输入必要的信息并保存,即可在Postgres服务器上创建表。你刚刚创建了一个带有自动增量的整数类型的id列和一个文本类型的title列。id列是主键。你也可以在这个画面上进行记录的添加和删除。

image.png

制作画面模板

虽然 Hasura 看起来在工作,但如果没有屏幕,为了查看购物清单,每次都需要手动输入 GraphQL 查询。
对于聪明的你来说,在拥挤的假日 IKEA 店内,即使是一个宽敞的环境,也显然没有时间去做这种事情。
而且,在公众场合打开 MacBook,快速输入 GraphQL 查询,可能会被认为是一个邪恶的黑客,试图操纵 IKEA 的设施并伤害人们。

你利用vue-cli创建了一个模板,并通过GraphQL API的客户端库Apollo以及其他必要的库使用yarn add逐个安装。
顺便提一下,vue create只能在vue-cli3.0系列中使用。

vue create .
yarn add vue-apollo graphql apollo-client apollo-link apollo-link-http apollo-cache-inmemory graphql-tag

如果只有你一个人使用它的话,你可以继续保持这个朴实无华的界面,但是当你到达IKEA后,你的伴侣也可能会查看这个购物清单。
富有热情和骑士精神的你决定先使用轻量级CSS框架Milligram,并将CDN链接添加到index.html的头部标签中。

<head>
  <link rel="stylesheet" href="//cdn.rawgit.com/milligram/milligram/master/dist/milligram.min.css">
</head>

连接Vue和Hasura

使用Apollo连接Vue和Hasura。
您将在main.js中输入代码来使用Apollo实例。
在创建链接时,指定uri使用先前记下的端点。

import Vue from 'vue'
import App from './App.vue'

// GraphQLを使うのに必要なライブラリをインポートする
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from "apollo-cache-inmemory";

import VueApollo from "vue-apollo";

Vue.config.productionTip = false;

// GraphQL APIへの接続先を設定する
const httpLink = new HttpLink({
  uri: 'http://localhost:8080/v1/graphql'
});

const apolloClient = new ApolloClient({
  link: httpLink,
  // 実行したクエリをメモリにキャッシュします。
  cache: new InMemoryCache(),
  connectToDevTool: true
});

Vue.use(VueApollo);

const apolloProvider = new VueApollo({
  defaultClient: apolloClient
});

new Vue({
  el: '#app',
  apolloProvider,
  render: h => h(App),
});

使用GraphQL进行INSERT操作

为了记录并将来自合作伙伴只能发出而您仍然希望拥有的物品清单插入到数据库中,您首先决定从插入进程开始创建它。
在components目录中创建AddTodo组件。

<template>
    <form @submit="submit">
        <fieldset>
            <input type="text" placeholder="トゥドゥ" v-model="title">
        </fieldset>
        <input class="button-primary" type="submit" value="Send">
    </form>
</template>

<script>
import gql from 'graphql-tag';

// GraphQLクエリを作成します。INSERTなのでmutationです。
// 返り値としてINSERTしたレコードのidを指定しています。
const ADD_TODO = gql`
    mutation addTodo(
        $title: String!
    ) {
        insert_todos(
            objects: [
                {
                    title: $title
                }
            ]
        ) {
            returning {
                id
            }
        }
    }
`;

export default {
    name: "AddTodo",
    data() {
        return {title: ""};
    },
    methods: {
        // submit時にGraphQLでINSERTします。
        submit(e) {
            e.preventDefault();
            const { title } = this.$data;
            // apolloインスタンスを指定してmutateクエリを実行します。
            this.$apollo.mutate({
                mutation: ADD_TODO,
                variables: {
                    title
                },
                // INSERTした後画面をリロードしなくても良いように、TodoListで実行した取得用のクエリを自動で実行します。
                // 一度実行したクエリはメモリ上にキャッシュされていますので、クエリ名を指定します。
                refetchQueries: ["getTodos"]
            });
            this.$data.title = "";
        }
    }
}
</script>

用GraphQL进行GET请求

若无法将已添加的项目作为列表显示出来,那就没办法了。
您需要在components目录中新建一个名为TodoList的组件,并创建一个名为TodoItem的组件来显示每个项目作为一个列表显示。

<template>
    <ul>
        <TodoItem v-for="todo in todos" :key="todo.id" :todo="todo"/>
    </ul>
</template>

<script>
import TodoItem from './TodoItem.vue';
import gql from 'graphql-tag';

// GETするだけのGrahpQLクエリです。
// idも返すように要求しますが、今回は使っていません。
const GET_TODOS = gql`
    query getTodos {
        todos {
            id
            title
        }
    }
`

export default {
    name: 'TodoList',
    components: {
        TodoItem
    },
    data() {
        return {
            todos: []
        } 
    },
    // apolloで実行するクエリを指定します。
    apollo: {
        todos: {
            query: GET_TODOS
        }
    }
}
</script>

<template>
    <li :key="todo.id">{{ todo.title }}</li>
</template>

<script>
export default {
    name: "TodoItem",
    props: ["todo"]
}
</script>

我已经完成了INSERT和GET的实现。
在根组件App.vue中,我删除了Hello World模板组件,并显示新创建的组件。
当您创建模板时,您会犹豫要如何处理Vue的标志。但是,作为具备营销才能的您认为,如果看到一个没有任何图片的简陋界面,购买欲望可能会消失。因此,我决定将其保留。

<template>
  <div id="app">
    <!-- 格好いいVのロゴは残す。 -->
    <img alt="Vue logo" src="./assets/logo.png">
    <add-todo/>
    <todo-list/>
  </div>
</template>

<script>
import TodoList from './components/TodoList.vue';
import AddTodo from './components/AddTodo.vue';

export default {
  name: 'app',
  components: {
    TodoList,
    AddTodo
  }
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

你完成了一些基本的实现,并决定进行实际的运行测试。

yarn serve

按下回车键啪嗒一声!

 DONE  Compiled successfully in 72ms                                                                                          


  App running at:
  - Local:   http://localhost:8081/

Vue应用程序已经开始在8081端口上运行。

当你意识到在视野的角落里悠闲地移动的伴侣时,你感到焦虑不安。
伴侣已经准备好了,而你还穿着鲜艳的紫色睡衣。
你用期待和紧张微颤的手打开浏览器,连接到localhost:8081。

image.png

你毫不留情地在一個橫跨整個螢幕寬度的文字方塊中慢慢輸入「買魷魚」,然後點擊發送按鈕。
當你看到被顯示在螢幕上的文字列時,你有所想。

不管怎么说,虽然有点不太合适,但也无所谓了。

作为一名优秀的IT工程师,你不会去追求完美而忽视交付期限,这样是愚蠢的行为。
是的,这就是QCD(Quality, Cost, Delivery)原则。始终要记住”完成比完美更重要”。
我们将以Alpha版本的形式发布。

用GraphQL进行删除操作

在这个阶段,我成功地创建了购物清单,但是突然,你的脑海里涌起了一些不安感。

在IT行业中,各种妖魔鬼怪无处不在。作为一个经历了无数次生死线的人,您已经意识到已经确定的决策很容易被权威人士的一句话推翻。您直觉地预见到这个购物清单需要一个删除功能。

你开始动手,同时认为对于TodoItem组件,你需要添加一个处理列表元素点击事件和删除查询的事件处理程序。

<template>
    <li :key="todo.id" v-on:click="removeTodo">{{ todo.title }}</li>
</template>

<script>
import gql from 'graphql-tag'

// GraphQLクエリを作成します。DELETEなのでmutationです。
// idが一致するレコードを削除し、返り値として削除したレコード数を返します。
const REMOVE_TODO = gql`
    mutation removeTodo(
        $id: Int!
    ) {
        delete_todos(where: {id: {_eq: $id}}) {
            affected_rows
        }
    }
`;

export default {
    name: "TodoItem",
    props: ["todo"],
    methods: {
        removeTodo() {
            const { id } = this.todo;
            this.$apollo.mutate({
                mutation: REMOVE_TODO,
                variables: {
                    id
                },
                // DELETEした後画面をリロードしなくても良いように、TodoListで実行した取得用のクエリを自動で実行します。
                // 一度実行したクエリはメモリ上にキャッシュされていますので、クエリ名を指定します。
                refetchQueries: ["getTodos"]
            });            
        }
    }
}
</script>

只要有这个,就算伙伴的心情变化了也可以放心。
您可以删除刚才添加的”乌贼之类的”,并加入要在宜家购买的东西。

image.png

为了以防万一,打开Hasura并在浏览器中,从DATA选项卡检查是否已添加记录到todos表中。

image.png

你查看了存储在PostgreSQL服务器上的购物清单,静静地,但确实带着无敌的笑容。拥有让每个人都羡慕的才能和时间管理能力的你,今天又像往常一样完美地完成了工作。

你褪下了鲜艳的紫色睡衣,换上了略带光泽的深蓝色套装。手拿着刚完成的购物清单和本地运行的MacBook,你朝着夕阳映照下的东京湾眯着眼,帅气地向南船桥IKEA东京湾前进。

结束了

很抱歉。
下次我想部署到Azure并订阅。

※ 所有内容均为虚构。

请提供更多上下文以便我理解应该如何帮助您。

以下是原文的中文翻译:
https://hasura.io/vue-graphql
https://docs.hasura.io/1.0/graphql/manual/index.html
https://learn.hasura.io/graphql/vue/introduction
https://medium.com/@marion.schleifer/how-to-connect-your-graphql-api-to-your-vuejs-frontend-61d8e8e455db

image.png