Hasuraで作るREST API
Hasuraを使用してPostgresデータベースに接続したREST APIを構築し、それを利用したWebアプリを作成します。
これからこのハンズオンで作成するのは次のようなWebアプリです。
構成としては下記の通りです。
- Hasura Cloud - すぐに利用可能なHasuraの環境
- StackBlitz - フロントエンドのオンライン開発環境
- Vue 3 - プログレッシブWebフレームワーク
- Quill - リッチテキストエディター
それではさっそく作っていきましょう!
Hasura Cloudのアカウント登録
または
このリンクから、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) | ページの識別子 |
content | JSONB | ページの本文 (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 |
Method | HTTPメソッド | 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 |
Method | HTTPメソッド | 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 プロパティを与えます。 |
例: id
が 1
のページにアクセスする 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アプリケーションを作成できます
<!-- 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チュートリアル
質問・提案・問題の報告
もし何か気になることがあれば、GitHub Issues からお気軽にお寄せください。