10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

国産ローコードツールQuerierで、PostgreSQLへのCRUDアプリを作る(REST API編)

Posted at

はじめに

YesCoderの私ですが、先日リリースされた国産ローコード開発ツール「Querier」を試したら良かったので、記事にしますよ。
今回はREST API編、次回はGraphQL編の予定です。
Web上でドラッグ&ドロップで簡単にUIを構築できる開発ツールです。

Querierの気に入った点はいろいろありますが、まず一つ挙げるなら、SQLデータベースやFiresbase等の外部データと簡単に連携できるところですね。
一般にNoCode/LowCodeでの開発は、今はNoCodeデータベースを使うことが多いと思います。
でも本格的なシステムを作るなら、大規模データで実績のあるデータベースを採用したいです。
NoCodeデータベースとNoCodeアプリの組み合わせは、大規模の実績は私の耳に入ってきませんが、Querierのように外部データベースと連携できる開発ツールなら、LowCodeで本格的なシステム開発もいけると私は見ています。

そして本格的なシステム開発もいけるなら、いよいよ「YesCodeのかなりの仕事がNoCode/LowCodeで置き換わる」話が現実味を帯びてきます。
Querierからは、そんな未来を感じました。

Querierは管理画面の開発に特化した、バックエンドエンジニア向けのローコード開発ツールですが、今後はプロダクト向けでも、外部データベースと簡単に連携できる開発ツールが出てくるかもしれませんね。
私もYesCodeの仕事が減りそうで、うかうかしていられません。

それではQuerierのフリープランで、CRUDアプリを試作してみます。

記事の内容

  • ローコード開発ツールのQuerierを使用して、PostgreSQLにデータを読み書きするWebアプリを試作します。
  • 自宅のローカル環境のPostgreSQLを使用しました。
  • 同じくローカルでPostgRESTを起動し、この記事の方法で外部公開し、Querierアプリからアクセスしました。PostgRESTは、PostgreSQLを操作するRESTful APIを提供する、オープンソースのWeb APIサーバーです。
  • 本記事ではバリデーションやPostgRESTのユーザー認証は省きました。

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

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

ERモデリングツールはA5:SQL Mk-2を使用しました。
以下のER図を描きました。エンティティ1つだけです。

a10.png

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

a11.png

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

a12.png

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


-- 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

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


CREATE DATABASE sample_db;

a13.png

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

\c sample_db

a14.png

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

a15.png

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


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

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


SELECT
    *
FROM
    weather_forecasts;

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

a17.png

psqlから抜けます。

a18.png

PostgRESTの作業

本記事ではPostgRESTをPostgreSQLと同じローカルのLinuxホストにインストールします。
このページを参考にして、PostgRESTのバイナリをインストール&起動します。
Dockerを使う方法もあります。

PostgREST起動時に指定するコンフィグファイル例

sample.conf

db-uri = "postgres://postgres:secret@localhost:5432/sample_db"
db-schema = "public"
db-anon-role = "postgres"

起動コマンド例


postgrest sample.conf

起動画面

710.png

postgrestコマンドを起動するだけで、PostgreSQLのスキーマを読み取ってAPIエンドポイントを自動生成してくれます。

ブラウザで http://localhost:3000/weather_forecasts にアクセスし、weather_forecastsテーブルのデータを見てみると、以下のように表示されました。

720.png

PostgRESTサーバーを外部公開する

この記事のhttpの手順を行って、先程ローカルで起動したPostgRESTサーバー(ポート3000)を外部公開(ポート13001)しました。記事内の動作確認用Webサーバー(python3 -m http.server 3001)の起動は不要です。
なお、本記事ではhttpsは使用しなかったので、Caddyの手順は行いませんでした。

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

以下のようにリバースSSHポートフォワーディングを利用して、ブラウザからローカルのPostgRESTのAPIを呼ぶことが出来ました。

730.png

Querierでアプリを開発する

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

サイドメニュー「連携データ」での作業

サイドメニューから「連携データ」を選択し、データソースを新規作成します。下図のように、

としました。

2010.png

サイドメニュー「アプリ一覧」での作業

サイドメニューから「アプリ一覧」を選択し、アプリを新規作成します。以下のように、アプリ名をweather_forecasts_restapiとしました。

2020.png

アプリ名の右側の三点ボタンから「編集」を選択し、アプリの編集画面に進みます。

データフローを4つ作成

下図のように、SQLのSELECT文、INSERT文、UPDATE文、DELETE文に該当する4つのデータフローを作成することにします。

760.png

4つ共通の設定として、「データソース=連携データで作成したsample_restapi」にします。

2030.png

以下、データフローからinput_id、input_dt、input_temperature_c、input_summaryの4つのインプットコンポーネントを参照しますが、これらは後の作業で作成します。

SELECT文のデータフロー

名前を「df_select_weather_forecasts」にしました。
「オプションの入力」は、PostgRESTの仕様に従い、

  • メソッド:GET
  • URL:/weather_forecasts
  • URLパラメーター:order id.asc

としました。URLパラメーターで、id列の昇順でソートするよう指定しています。

2040.png

INSERT文のデータフロー

名前を「df_insert_weather_forecasts」にしました。
「オプションの入力」は、PostgRESTの仕様に従い、

  • メソッド:POST
  • URL:/weather_forecasts
  • データ:JSON

{
    "id":{{ input_id.value }},
    "dt":"{{ input_dt.value }}",
    "temperature_c":{{ input_temperature_c.value }},
    "summary":"{{ input_summary.value }}"
}

としました。

790.png

下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのINSERT文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。

2050.png

UPDATE文のデータフロー

名前を「df_update_weather_forecasts」にしました。
「オプションの入力」は、PostgRESTの仕様に従い、

  • メソッド:PATCH
  • URL:/weather_forecasts
  • データ:JSON

{
    "dt":"{{ input_dt.value }}",
    "temperature_c":{{ input_temperature_c.value }},
    "summary":"{{ input_summary.value }}"
}
  • URLパラメーター:id eq.{{ input_id.value }}

としました。

800.png

下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのUPDATE文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。

2060.png

DELETE文のデータフロー

名前を「df_delete_weather_forecasts」にしました。
「オプションの入力」は、PostgRESTの仕様に従い、

  • メソッド:DELETE
  • URL:/weather_forecasts
  • データ:NONE
  • URLパラメーター:id eq.{{ input_id.value }}

としました。

810.png

下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのDELETE文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。

2070.png

以上でデータフローの作成は完了です。

コンポーネントの配置と設定

カラムコンポーネント

画面右側からコンポーネントタブに行き、カラムコンポーネントを下図のようにドラッグ&ドロップします。

2080.png

カラムコンポーネントの左側を広めにします。

840.png

テーブルコンポーネント「table1」

コンポーネントタブに行き、テーブルコンポーネントを下図のようにカラムコンポーネントの左側にドラッグ&ドロップします。

2090.png

860.png

エディタータブに行き、データに以下を入力してSELECT文のデータフローを設定します。


{{ df_select_weather_forecasts.data }}

870.png

テーブルコンポーネントのカラム(列)が古いままですが、アプリ編集を一旦終了し、再び開き直してデータフロー「df_select_weather_forecasts」を実行したら更新されました。

875.png

880.png

インプットコンポーネント「input_dt」

コンポーネントタブに行き、インプットコンポーネントを下図のようにカラムコンポーネントの右側にドラッグ&ドロップします。

2100.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:input_dt
  • ラベル:date
  • DefaultValue:

{{ table1.selectedRow.data ? table1.selectedRow.data.dt : '' }}
  • 必須:オン

2110.png

DefaultValueにtable1のデータを指定したので、table1の別の行を選択すると値がinput_dtに反映されます。

2120.png

インプットコンポーネント「input_id」

コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_dtの下にドラッグ&ドロップします。

2130.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:input_id
  • ラベル:id
  • DefaultValue:

{{ table1.selectedRow.data ? table1.selectedRow.data.id : '' }}
  • 必須:オン

2140.png

インプットコンポーネント「input_summary」

コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_idの下にドラッグ&ドロップします。

2150.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:input_summary
  • ラベル:summary
  • DefaultValue:

{{ table1.selectedRow.data ? table1.selectedRow.data.summary : '' }}
  • 必須:オン

2160.png

インプットコンポーネント「input_temperature_c」

コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_summaryの下にドラッグ&ドロップします。

2180.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:input_temperature_c
  • ラベル:temperature
  • DefaultValue:

{{ table1.selectedRow.data ? table1.selectedRow.data.temperature_c : '' }}
  • 必須:オン

2170.png

ボタンコンポーネント「button_select」

コンポーネントタブに行き、ボタンコンポーネントを下図のようにtable1の下にドラッグ&ドロップします。

1010.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:button_select
  • ラベル:読み直し
  • データフロー:df_select_weather_forecasts

2190.png

ボタンコンポーネント「button_insert」

コンポーネントタブに行き、ボタンコンポーネントを下図のようにinput_temperature_cの下にドラッグ&ドロップします。

980.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:button_insert
  • ラベル:新規登録
  • データフロー:df_insert_weather_forecasts

990.png

それでは存在しないデータを新規登録してみましょう。
インプットコンポーネントに適当な値を入力して、以下の画面で新規登録ボタンをクリックします。

1030.png

以下のように、右上にdf_insert_weather_forecastsが成功したメッセージが表示さました。
また、今回のケースではテーブルコンポーネントに16ページ目が追加されて、df_insert_weather_forecastsに続いてdf_select_weather_forecastsが自動実行された様子です。

2210.png

その16ページ目を表示させると、新規登録したデータが見えました。

2220.png

ボタンコンポーネント「button_update」

コンポーネントタブに行き、ボタンコンポーネントを下図のようにbutton_insertの下にドラッグ&ドロップします。

1050.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:button_update
  • ラベル:行更新
  • データフロー:df_update_weather_forecasts

2230.png

それでは先程新規登録したデータを更新してみましょう。
table1で先程のデータを選択後、インプットコンポーネントのid以外の値を適当に変更して、以下の画面で行更新ボタンをクリックします。

2240.png

以下のように、右上にdf_update_weather_forecastsが成功したメッセージが表示さました。
また続いてdf_select_weather_forecastsも実行され、テーブル表示が自動更新されました。

2250.png

ボタンコンポーネント「button_delete」

コンポーネントタブに行き、ボタンコンポーネントを下図のようにbutton_updateの下にドラッグ&ドロップします。

1090.png

エディタータブに行き、以下のように設定します。

  • ウィジェット名:button_delete
  • ラベル:行削除
  • データフロー:df_delete_weather_forecasts

2260.png

それでは先程更新したデータを削除してみましょう。
table1で先程のデータを選択後、以下の画面で行削除ボタンをクリックします。

2270.png

以下のように、右上にdf_delete_weather_forecastsが成功したメッセージが表示さました。
また続いてdf_select_weather_forecastsも実行され、今回のケースではテーブルコンポーネントから16ページ目がなくなりました。

2280.png

以上、ひと通りCRUD機能を実装できました。
BIツールではできなかった、データの挿入・編集も可能でした。

使ってみての感想

これはエンジニア向けのツールですね。
ローコードなので、コードを書ける分ノーコードよりできることが多く、YesCoderにも受け入れられそうです。
UIをドラッグ&ドロップで構築できて、コードも追加できるので、かつてのDelphiを思い出しました。

共有データベースとQuerierを連携させれば、共有データベースを通じてYesCodeアプリとも機能連携できそうです。
まず共有データベースをしっかり作り、Querierで作れる機能はサクッと作って工数削減し、複雑な機能だけYesCodeで開発してQuerierと機能連携させるイメージです。
良いと思いますねー。機会があれば、ぜひ業務で使いたいです。
国産ツールなので日本語でサポートが受けられるのも、ありがたいですね。

10
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?