Hasuraで作るREST API

Hasuraを使用してPostgresデータベースに接続したREST APIを構築し、それを利用したWebアプリを作成します。

これからこのハンズオンで作成するのは次のようなWebアプリです。

構成としては下記の通りです。

  • Hasura Cloud - すぐに利用可能なHasuraの環境
  • StackBlitz - フロントエンドのオンライン開発環境
  • Vue 3 - プログレッシブWebフレームワーク
  • Quill - リッチテキストエディター

それではさっそく作っていきましょう!

Hasura Cloudのアカウント登録

Signup - Hasura Cloud

または

Deploy to Hasura

このリンクから、Hasura Cloudにアクセスします。

GitHubアカウントまたはGoogleアカウント、メールアドレスを使って登録が可能です。

GitHubアカウントを使用する場合:

事前にJoin GitHubからGitHubアカウントを作成し、サインインします。

Googleアカウントを使用する場合:

事前にGoogle アカウントの作成し、ログインします。

メールアドレスを使用する場合:

Signup - Hasura Cloud > [Sign up with Email] から、必要事項を入力し、アカウントを登録します。

アカウントを作成後、Hasura Cloudのプロジェクト一覧にアクセスできれば完了です。

Hasura Cloudプロジェクトの作成

Hasuraを利用するためにHasura Cloudのプロジェクトを作成します。

プロジェクトが存在しない場合、まずプロジェクトの作成を行います。 すでに存在するプロジェクトを利用する場合は、下記のプロジェクトの作成の作業は不要です。

プロジェクトの作成

Hasura Cloudのトップページにアクセスすると、プロジェクトの一覧画面が表示され、[New Project]ボタンから、プロジェクト作成フォームを開きます。

プロジェクト作成フォームに必要事項を入力します。ここでは例として次の項目を選択します。

項目説明
Choose a pricing plan料金プランの選択Free - 無料
Select Cloud Providerクラウド事業者AWS
Select a regionリージョンAsia Pacific (Tokyo) - 東京

必要事項を入力後、[Create Free Project]ボタンを押し、プロジェクトを作成します。 プロジェクトの作成が完了すると、画面上にプロジェクトの詳細が表示されます。

画面上にプロジェクトが表示されれば完了です。

プロジェクトの[Launch Console]ボタンからHasuraのコンソール画面にアクセスして、データベースへの接続を行いましょう。

新しいデータベースへの接続

Hasuraでデータの保存と検索を実現するためにデータベースを接続します。

このハンズオンでは、データベースとしてNeonを利用します。

Hasura Cloudのプロジェクトの[Launch Console]ボタンからHasuraのコンソール画面にアクセスして、データベースへの接続を行うことが可能です。

コンソール > [Data Manager] にアクセスします。

[Connect Neon Database]ボタンを選択し、データベースを新たに作成します。

一連の手順でデータベースを作成すると、Hasuraは自動的にデータベースへの接続を開始します。 しばらく待つと、データベースへの接続が完了します。

これであなたはHasuraを利用可能になりました 🎉

それでは実際にHasuraを使ってみましょう!

テーブルの作成

[Data Manager] > [View Database] > [Create Table]を選択し、新しいテーブルを作成します。

このハンズオンでは、次のデータモデルを作成します。

テーブル名: pages - メモ帳のページ

項目説明
id *Integer (auto-increment)ページの識別子
contentJSONBページの本文 (Quill Deltaオブジェクトを使う想定)

*: Primary Key

データモデルのための必要事項を入力し、テーブルを作成します。Primary Keyとして id (ページの識別子)を選択します。

テーブルの作成が完了するとその作成したテーブルの[Modify]パネルが表示されます。作成したテーブルへの変更はHasuraによって自動的に追従されます。

これでHasuraによるデータの操作が可能になります。

GraphQLによるデータの挿入と取得

GraphQLによるデータの操作を行ってみましょう。

コンソールのトップ画面に戻ると、GraphiQL (GraphQLのプレイグラウンド、開発環境) を使ってGraphQL APIを実際に試すことが可能です。

データの挿入

まずGraphQLでデータを挿入してみます。ここでは例として pages テーブルに次のデータを書き込みます。

項目説明
contentページの本文{} - 空のオブジェクト

GraphQL Queryとしては次のコードを書いて、▶ ボタンからリクエストを行います。

mutation MyMutation { insert_pages_one(object: {content: {}}) { id content } }

サーバーにリクエストを発行しレスポンスボディとして得られたデータは、右側のパネルに表示されます。

問題なくデータが得られたでしょうか。 別のデータも試してみましょう。

例として pages テーブルに次のデータを書き込みます。

項目説明
contentページの本文{hey: "hello!"}
mutation MyMutation { insert_pages_one(object: {content: {hey: "hello!"}}) { id content } }

レスポンスを見ると問題なく id (ページの識別子) が得られており、正しく割り当てられているようですね。 問題なくテーブル内に書き込まれているようです。

実際にデータベースのテーブルに書き込まれていることを確認してみます。[Data Manager] > [View Database] > pages テーブルを選択すると[Browse Rows]パネルでテーブル内のデータを確認することが可能です。

先ほどのGraphQL Queryリクエストが問題なく発行され、正しくデータが書き込まれていますね。

データの取得

続いて、pages テーブルのデータを取得してみます。

次のGraphQL Queryを書き、リクエストを行います。

query MyQuery { pages_by_pk(id: 2) { id content } }

このとき、引数の id として、データに割り当てられた id を使います。

項目説明
idページの識別子2
contentページの本文{hey: "hello!"}

レスポンスを見ると、問題なく先ほど書き込まれたのテーブル内データが得られていますね。

このようにして、HasuraではGraphQLを使って簡単にデータベースのデータを操作できます。 リクエストヘッダーに秘密鍵を与えれば、実際にGraphQL APIエンドポイントとして利用することもできます。

また、実際にはここで紹介した以外にもさまざまな操作を行うことが可能です。GraphiQLの[Explorer]パネルから実行可能な操作や型を参照してみると、実際の操作の参考になるかと思います。 あるいは、右側の[Docs]パネルからGraphQLの型の詳細を参照してみてください。

REST APIエンドポイントの作成

ここからは、Hasuraを使ってREST APIエンドポイントを作成し、実際にWebアプリケーションから利用する方法を説明します。

設計

HasuraはGraphQL APIサーバーとして利用するだけでなく、REST APIサーバーとして利用することもできます。このハンズオンでは、GraphQL QueryをREST化し、次の仕様のREST APIエンドポイントを作成します。

名称HTTPメソッドとパス説明
ページの取得GET /api/rest/page/:id割り当てられた識別子 id をもつページを取得します。ページが存在する場合、レスポンスボディには data.page.content プロパティを含みます。
ページの更新PUT /api/rest/page/:id割り当てられた識別子 id をもつページを更新します。リクエストボディには content プロパティを与えます。

「ページの取得 (GET page/:id)」エンドポイントの作成

ページを取得するためのREST APIエンドポイントを作成します。

コンソールのトップ画面のGraphiQLのパネルにアクセスし、次のコードを書きます。

query getPage($id: Int!) { page: pages_by_pk(id: $id) { id content } }

[REST]ボタンを選択し、REST APIエンドポイント作成フォームを表示します。次の必要事項を入力し、作成ボタンを選択しエンドポイントを作成します。

項目説明内容
Nameエンドポイントの名称get page
URL Path/api/rest/ 以降のパスpage/:id
MethodHTTPメソッドGET

「ページの更新 (PUT page/:id)」エンドポイントの作成

ページを更新するためのREST APIエンドポイントを作成します。

コンソールのトップ画面のGraphiQLのパネルにアクセスし、次のコードを書きます。

mutation putPage($id: Int!, $content: jsonb!) { page: insert_pages_one( object: { id: $id, content: $content } on_conflict: { constraint: pages_pkey, update_columns: content } ) { id content } }

[REST]ボタンを選択し、REST APIエンドポイント作成フォームを表示します。次の必要事項を入力し、作成ボタンを選択しエンドポイントを作成します。

項目説明内容
Nameエンドポイントの名称put page
URL Path/api/rest/ 以降のパスpage/:id
MethodHTTPメソッドPUT

作成が完了すると作成したREST APIエンドポイントの一覧が表示されます。

アクセス権の設定

アクセス権の設定を行います。Hasuraはテーブルの行単位でアクセス制御を行うことが可能です。このハンズオンでは秘密鍵を持たずとも誰でもREST APIを利用できるようにします。

注: セキュアではないので本番環境ではJWT等で認証・認可しましょう

[Data Manager] > [View Database] > pages テーブル > [Permissions]パネルから、テーブル内のデータのアクセス件を設定可能です。

認証・認可していない利用者のために、ここでは例として anonymous ロールを割り当てます。

insert (データの挿入) 権限の設定

anonymous ロールの行 > insert の列を選択し、データの挿入の権限の設定を行います。

下記の項目を選択し、[Save Permissions]ボタンで権限を保存します。

  • Row insert permissions > Without any checks
  • Column insert permissions > id content ([Toggle All]ボタンを押す)

select (データの取得) 権限の設定

anonymous ロールの行 > select の列を選択し、データの取得の権限の設定を行います。

下記の項目を選択し、[Save Permissions]ボタンで権限を保存します。

  • Row select permissions > Without any checks
  • Column select permissions > id content ([Toggle All]ボタンを押す)

update (データの更新) 権限の設定

anonymous ロールの行 > update の列を選択し、データの取得の権限の設定を行います。

下記の項目を選択し、[Save Permissions]ボタンで権限を保存します。

  • Pre-update check > Without any checks
  • Post-update check > Without any checks
  • Column update permissions > id content ([Toggle All]ボタンを押す)

anonymous ロールのinsert、select、updateに✅マークが入っていれば完了です。

環境変数 HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous の設定

Hasuraの環境変数として HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous を与えることで、認証・認可していない利用者に割り当てられるロールを設定します。

Hasura Cloudのプロジェクトの画面に戻り、[Env vars]にアクセスします。

[+ New Env Var]ボタンを押して、環境変数を追加します。

環境変数説明
HASURA_GRAPHQL_UNAUTHORIZED_ROLE認証・認可されていない利用者に割り当てるロールanonymous

入力後、[Add]ボタン押して追加します。

REST APIエンドポイントの確認

実際にHasuraのREST APIエンドポイントにアクセスできるか確認してみましょう。

  • プロトコル: HTTPS
  • ドメイン名: {Hasura Cloudのプロジェクト名}.hasura.app
名称HTTPメソッドとパス説明
ページの取得GET /api/rest/page/:id割り当てられた識別子 id をもつページを取得します。ページが存在する場合、レスポンスボディには data.page.content プロパティを含みます。
ページの更新PUT /api/rest/page/:id割り当てられた識別子 id をもつページを更新します。リクエストボディには content プロパティを与えます。

例: id1 のページにアクセスする https://memo-demo.hasura.app/api/rest/page/1

Hasura Cloudで作成したREST APIエンドポイントにアクセスするには、Hasura Cloudのプロジェクト名の直後に .hasura.app を加えたドメイン名でアクセスできます。 たとえば、Hasura Cloudのプロジェクト名が memo-demo の場合、ドメイン名は memo-demo.hasura.app となります。プロジェクトによって異なるので、自分の作成したプロジェクトに合わせて書き換えましょう。

これで、REST APIエンドポイントをWebアプリから利用する準備が整いました。

Vueアプリケーションの作成

次のリンクにアクセスすると、StackBlitzでVueアプリケーションを作成できます

Edit on StackBlitz

<!-- src/App.vue --> <script setup> import { onMounted, ref } from "vue"; import { useDebounceFn } from "@vueuse/core"; import { Delta, QuillEditor } from "@vueup/vue-quill"; import "@vueup/vue-quill/dist/vue-quill.snow.css"; const id = window.localStorage.getItem("id") ?? String(window.crypto.getRandomValues(new Uint16Array(1))[0]); window.localStorage.setItem("id", id); // ここに作成したREST APIエンドポイントを指定 // `memo-demo` の部分をHasura Cloudプロジェクト名に書き換えてください const endpoint = `https://memo-demo.hasura.app/api/rest/page/${id}`; const content = ref(new Delta([{ insert: "読み込み中…" }])); const defaultContent = new Delta([ { insert: "メモ帳\n", attributes: { header: 1 } }, { insert: "こんにちは!\n" }, { insert: "これは「Hasuraで作るREST API」のデモ用Webアプリです。\n" }, { insert: "ここに入力した内容は自動的にHasuraに送信されデータベースに保存されます。\n", attributes: { link: endpoint }, }, ]); onMounted(async () => { console.log("endpoint", endpoint); const res = await fetch(endpoint); const data = await res.json(); content.value = new Delta(data.page?.content ?? defaultContent); console.log("mounted", data); }); const updateContent = useDebounceFn(async (content) => { const res = await fetch(endpoint, { method: "PUT", body: JSON.stringify({ content }), }); const data = await res.json(); console.log("updated", data); }, 250); </script> <template> <QuillEditor toolbar="full" :content="content" @update:content="updateContent" /> </template>

ローカル環境で作成する場合は、StackBlitzにアクセスし、左の[Project]パネルから[↓ (Download Project)]ボタンを押すと、ソースコード一式をダウンロードできます。ダウンロードしたZIPファイルを展開後、npm install コマンドなどで必要なパッケージを導入することで同様にVueアプリケーションを作成できます。

ソースコードの中には、HasuraのREST APIのエンドポイントURLが含まれます。そのURLに含まれるドメイン名の部分の memo-demo は、Hasura Cloudプロジェクト名です。プロジェクトによって異なるので、自分の作成したプロジェクトに合わせて書き換えましょう。

// src/App.vue // ここに作成したREST APIエンドポイントを指定 // `memo-demo` の部分をHasura Cloudプロジェクト名に書き換えてください const endpoint = `https://memo-demo.hasura.app/api/rest/page/${id}`;

このエンドポイントURLを自分の作成したプロジェクトのものに書き換えると完成です。

Hasuraを使用してPostgresデータベースに接続したREST APIを構築し、それを利用したWebアプリを作成できました 🎉

Hasuraのコンソールにアクセスすると、実際にデータが更新されていることを確認できます。

実践的な機能

認証

認証には、Webhookを介する方法とJWTを介する方法があります。

より詳しい情報は、公式ドキュメント や、公式チュートリアルを参照してください。

CORS (Cross-Origin Resource Sharing) の設定

応答ヘッダーには、デフォルトで Access-Control-Allow-Origin: * ヘッダーが含まれており、外部からのアクセスをすべて許可しています。 適宜セキュリティ要件に合わせて設定することを推奨します。

より詳しい情報は、公式ドキュメント を参照してください。

参考文献

Hasura公式ドキュメント

Hasura GraphQL Docs

Hasuraチュートリアル

Hasura GraphQLチュートリアル

GraphQLチュートリアル

GraphQLチュートリアル

質問・提案・問題の報告

もし何か気になることがあれば、GitHub Issues からお気軽にお寄せください。