ローコード開発ツールのRetoolからPostGraphile（GraphQL）を使ってみた

# 記事の内容

- ローコード開発ツールの[Retool](https://retool.com)を使用して、PostgreSQLにデータを読み書きするアプリを試作します。
- 自宅のローカル環境のPostgreSQLを使用しました。
- 同じくローカルでPostGraphileを起動し、[この記事](https://qiita.com/kanedaq/items/4c3d37d278e674df6e14)の方法で外部公開し、Retoolアプリからアクセスしました。
- 本記事ではPostGraphileのユーザー認証は省きました。

※ [Hasura](https://hasura.io/)もPostGraphileと類似のプロダクトと認識していますが、本記事では私が利用経験のあるPostGraphileを使用しました。

## 参考ページ（感謝します）

[ローコードアプリ開発プラットフォーム Retool で API 連携アプリを作成](https://www.cdatablog.jp/entry/retoolapiserver)

# 【ローカル環境にて】PostgreSQLにデータを用意し、PostGraphileを立ち上げる

ローカル環境での作業手順は[この記事](https://qiita.com/kanedaq/items/718937b55e095e3749e1)とほぼ同じですが、若干の違いがありますので再掲します。

## ERモデリングツールでの作業

ERモデリングツールは[A5:SQL Mk-2](https://a5m2.mmatsubara.com/)を使用しました。
以下のER図を描きました。エンティティ1つだけです。

![a10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/2bc3e939-c1e7-662b-6e37-adf4d3762f6b.png)

ER図メニューから「DDLを作成する」を選択します。

![a11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/580e4a58-3c1e-afa8-7c47-79e72c0f8d5c.png)

RDBMS種類でPostgreSQLを選択し、DDL生成ボタンを押します。

![a12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/2756a184-d9f9-db3c-3fcc-7085699f6f29.png)

以下のDDLが生成されました。

```sql

-- RDBMS Type   : PostgreSQL
-- Application  : A5:SQL Mk-2

/*
  BackupToTempTable, RestoreFromTempTable疑似命令が付加されています。
  これにより、drop table, create table 後もデータが残ります。
  この機能は一時的に $$TableName のような一時テーブルを作成します。
*/

-- WeatherForecast
--* BackupToTempTable
DROP TABLE if exists weather_forecasts CASCADE;

--* RestoreFromTempTable
CREATE TABLE weather_forecasts (
  id integer NOT NULL
  , dt date NOT NULL
  , temperature_c double precision NOT NULL
  , summary character varying NOT NULL
  , CONSTRAINT weather_forecasts_PKC PRIMARY KEY (id)
) ;

COMMENT ON TABLE weather_forecasts IS 'WeatherForecast';
COMMENT ON COLUMN weather_forecasts.id IS 'Id';
COMMENT ON COLUMN weather_forecasts.dt IS 'Date';
COMMENT ON COLUMN weather_forecasts.temperature_c IS 'TemperatureC';
COMMENT ON COLUMN weather_forecasts.summary IS 'Summary';
```

## PostgreSQLの作業

本記事ではLinux上のPostgreSQLを使用します。
psqlを起動します。

```

psql --host=localhost --username=postgres --password
```

![a09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/7f3aa0ff-dbe6-20cd-abdc-6b83838dbca5.png)

データベースを作成します。名前を「sample_db」にしましたが、何でも良いです。

```sql

CREATE DATABASE sample_db;
```

![a13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/5c542d5f-80a4-12d1-3129-503b02fde128.png)

カレントデータベースを、作成したsample_dbに切り替えます。

```
\c sample_db
```

![a14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/199852b4-9249-3385-bc68-2e441bf918cf.png)

先ほどERモデリングツールが生成したDDLをpsqlにコピペして実行します。

![a15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/c5a76dc0-911e-ddd3-f71d-c9f145a96b91.png)

以下のINSERT文を流して、2000年1月1日から150日分のテストデータを作成します。
テーブルにはidの降順でINSERTしてみます。

```sql

INSERT INTO weather_forecasts (
    id,
    dt,
    temperature_c,
    summary
)
SELECT
    id,
    ('1999-12-31'::DATE + (id::TEXT || ' days')::INTERVAL)::DATE AS dt,
    (random() * 75 - 20)::INT AS temperature_c,
    CASE (random() * 1000)::INT % 10
        WHEN 0 THEN 'Freezing'
        WHEN 1 THEN 'Bracing'
        WHEN 2 THEN 'Chilly'
        WHEN 3 THEN 'Cool'
        WHEN 4 THEN 'Mild'
        WHEN 5 THEN 'Warm'
        WHEN 6 THEN 'Balmy'
        WHEN 7 THEN 'Hot'
        WHEN 8 THEN 'Sweltering'
        WHEN 9 THEN 'Scorching'
    END AS summary
FROM
    generate_series(1, 150) AS id
ORDER BY id DESC;
```

![a16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/86580e77-2bc5-cbe5-75ae-59a8c8fb916e.png)

以下のSELECT文を流して、データが作成されたか確認します。

```sql

SELECT
    *
FROM
    weather_forecasts;
```

以下のようにidの降順で表示されましたが、順番に意味はありません。

![a17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/7b0b6a6c-6f69-faec-5d12-a96f5c49b52c.png)

psqlから抜けます。

![a18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/c127c795-8346-9b8b-fabb-9c6f56a80306.png)

## PostGraphileの作業

本記事ではPostGraphileをPostgreSQLと同じローカルのLinuxホストにインストールします。
[このページ](https://www.graphile.org/postgraphile/usage-cli/)を参考にして、PostGraphileをインストール＆起動します。
Dockerを使う方法もあります。

インストール

```

npm install -g postgraphile
```

起動コマンド例

```

postgraphile --connection postgres://postgres:secret@localhost/sample_db --port 3001 --schema public --export-schema-graphql ~/schema.graphql

```

起動画面

![b00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/830d57d5-06bc-685e-4e3c-ed4ae63bb8f0.png)

postgraphileコマンドを起動するだけで、PostgreSQLのスキーマを読み取ってGraphQLエンドポイントを自動生成してくれます。
とても楽で、これもローコードと言えるかもしれません。

起動画面によれば、URLは

- GraphQLエンドポイント：http://localhost:3001/graphql
- GraphiQL：http://localhost:3001/graphiql

となっています。

ここでブラウザからGraphiQLにアクセスして、クエリーを発行したりドキュメントを見たりしてみましょう。

クエリー例

```graphql

query allWeatherForecasts {
  allWeatherForecasts {
    nodes {
      id
      dt
      temperatureC
      summary
    }
  }
}
```

ブラウザ画面

![b03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/fbea2b77-e520-c1aa-826d-b7ae03203d3d.png)

レスポンス

```

{
  "data": {
    "allWeatherForecasts": {
      "nodes": [
        {
          "id": 1,
          "dt": "2000-01-01",
          "temperatureC": 7,
          "summary": "Hot"
        },
        {
          "id": 2,
          "dt": "2000-01-02",
          "temperatureC": -16,
          "summary": "Cool"
        },
        {
          "id": 3,
          "dt": "2000-01-03",
          "temperatureC": 17,
          "summary": "Hot"
        },

        （中略）

        {
          "id": 150,
          "dt": "2000-05-29",
          "temperatureC": 14,
          "summary": "Freezing"
        }
      ]
    }
  }
}
```

psqlからSELECT文を実行したときはid列の降順で表示されましたが、今回のレスポンスを見ると昇順になっていますね。
この順番は気にしないことにして、先に進みます。

# PostGraphileサーバーを外部公開する

[この記事](https://qiita.com/kanedaq/items/4c3d37d278e674df6e14)のhttpの手順を行って、先程ローカルで起動したPostGraphileサーバーを外部公開しました。ただし、動作確認用Webサーバー（python3 -m http.server 3001）の起動は不要です。
なお、本記事ではhttpsは使用しなかったので、Caddyの手順は行いませんでした。

外部からGraphiQLにアクセスできるか、ブラウザで確認してみます。
VPSのドメインが example.com であれば、アクセス先は http://example.com:13001/graphiql となります。

以下のようにリバースSSHポートフォワーディングを利用して、ブラウザからローカルのPostGraphileにクエリーを発行できました。

![b05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/e873f68f-81b9-baa0-1efb-2831c23077a2.png)

# Retoolでアプリを開発する

Retoolにログイン後、以下の作業を行います。

## Resoucesタブでの作業

Resoucesタブに行って、Create newボタンをクリックします。

![b01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/ef1f8bb8-6c25-d54a-e2b6-5b3f5a1d3cfc.png)

以下のように一覧から接続先が選べますので、GraphQLを選択します。

![b02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/fc395dc8-f88c-e16e-36b5-e121f1f34d37.png)

以下のようにNameとBaseURLを設定し、Test ConnectionボタンをクリックしてGraphQLエンドポイントに接続できるか確認します。

```

Name：sample_db（何でも良いです）
BaseURL：http://your_domain:13001/graphql
```

![b06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/0ece2156-adc9-66fe-1551-ed275724cdba.png)

上図のように「Connection success!」と表示されれば、PostGraphileへの接続は成功ですので、Create resourceボタンをクリックします。
「Resource created」と表示されれば、リソースの作成は成功です。

## Appsタブでの作業（その1）データ表示

Appsタブに行って、Create newボタンをクリックします。
出てきたポップアップメニューから、今回は「Create a blank app」を選択しました。

![b07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/8e26da9a-96be-9424-058b-0bfdf8426198.png)

query1について、以下の設定を行います。

- query1の名前を「allWeatherForecasts」に変更
- Resourceを「sample_db」に変更
- Queryに以下のクエリーを入力
- Save & Runをクリック

```graphql

query allWeatherForecasts {
    allWeatherForecasts {
        nodes {
            id
            dt
            temperatureC
            summary
        }
    }
}
```

![b08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/cdf7cbef-b201-da46-b8b3-3dc0afc8bf8d.png)

table1を選択し、以下を行います。

- BASIC > Dataに「{{ allWeatherForecasts.data.allWeatherForecasts.nodes }}」と入力
- Previewボタンをクリック

![b09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/2cb758c2-127c-9374-f5c3-4868e81d2be0.png)

以下のようにアプリが起動し、PostgreSQLのデータが一覧表示されました。

![b10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/87aaaee4-be90-cf7f-8639-c9cdef8eec8f.png)

## Appsタブでの作業（その2）データ新規登録

Editボタンを押してアプリ編集画面に戻ります。
以下のように、Gridの下にTextInputを4つとButtonを1つ、ドラッグ＆ドロップして設置します。

![b11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/c429491e-f985-f4c9-749b-35e2e12054c0.png)

textinput1の属性を以下のように変更します。

```

名前：textinput_id
Label：id
Input type：number
```

![b12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/8b480ecb-1b59-af30-4056-d68a49061e7c.png)

textinput2の属性を以下のように変更します。

```

名前：textinput_dt
Label：dt
Input type：date
```

![b13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/a5361344-fdbb-2a99-0fd3-9d4b2e502fff.png)

textinput3の属性を以下のように変更します。

```

名前：textinput_c
Label：temperatureC
Input type：number
```

![b14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/e8605533-ffff-85b2-7909-3ca70dfea8ad.png)

textinput4の属性を以下のように変更します。

```

名前：textinput_summary
Label：summary
```

![b15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/39e9b3c5-cf6f-8d73-0fc4-f10911ff04f8.png)

データを新規登録するクエリーを追加します。
以下のようにQueryの「+ New」ボタンをクリックし、Resource queryを選択します。

![b16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/8a8b1d7b-55b2-2f1e-3b1d-78a4eba096f7.png)

以下の設定を行います。

- query2の名前を「createWeatherForecast」に変更
- Queryに以下のクエリーを入力

```graphql

mutation createWeatherForecast ($input: CreateWeatherForecastInput!) {
    createWeatherForecast (input: $input) {
        weatherForecast {
            id
            dt
            temperatureC
            summary
        }
    }
}
```

- Variables > inputに以下を入力

```json

{
    "weatherForecast": {
        "id": {{parseInt(textinput_id.value)}},
        "dt": {{textinput_dt.value}},
        "temperatureC": {{_.toNumber(textinput_c.value)}},
        "summary": {{textinput_summary.value}}
    }
}
```

- Saveボタンをクリックします。

![b17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/e40a9cef-68fd-03d9-cbb3-ad63799ff1f7.png)

button1を選択し、以下の設定を行います。

- On clickが「Run a query」になっていなければ「Run a query」にする
- Select a queryでcreateWeatherForecastを選択する

![b18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/f9f9718b-e0a1-3ee2-d091-66b6218667fc.png)

それでは右上のPreviewボタンをクリックして、アプリを動かしてみます。
適当なデータを入力して新規登録してみましょう。

![b19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/8becf81a-a4d4-8692-d537-d14d278c125f.png)

Submitボタンをクリックすると、以下のように「Query ran successfully」と表示されました。

![b20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/33689628-2fee-daea-de6d-0b2181cd3e04.png)

ブラウザでページを更新（再読み込み）すると、データが1件増えたことが確認できました。
データ一覧の最終ページに移動すると、新規登録したデータが表示されました。

![b21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327312/4226d92f-c887-9fba-549a-948304fa4fcc.png)

以上です。