はじめに
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つだけです。
ER図メニューから「DDLを作成する」を選択します。
RDBMS種類でPostgreSQLを選択し、DDL生成ボタンを押します。
以下の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
データベースを作成します。名前を「sample_db」にしましたが、何でも良いです。
CREATE DATABASE sample_db;
カレントデータベースを、作成したsample_dbに切り替えます。
\c sample_db
先ほどERモデリングツールが生成したDDLをpsqlにコピペして実行します。
以下の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;
以下のSELECT文を流して、データが作成されたか確認します。
SELECT
*
FROM
weather_forecasts;
以下のようにidの降順で表示されましたが、順番に意味はありません。
psqlから抜けます。
PostgRESTの作業
本記事ではPostgRESTをPostgreSQLと同じローカルのLinuxホストにインストールします。
このページを参考にして、PostgRESTのバイナリをインストール&起動します。
Dockerを使う方法もあります。
PostgREST起動時に指定するコンフィグファイル例
db-uri = "postgres://postgres:secret@localhost:5432/sample_db"
db-schema = "public"
db-anon-role = "postgres"
起動コマンド例
postgrest sample.conf
起動画面
postgrestコマンドを起動するだけで、PostgreSQLのスキーマを読み取ってAPIエンドポイントを自動生成してくれます。
ブラウザで http://localhost:3000/weather_forecasts にアクセスし、weather_forecastsテーブルのデータを見てみると、以下のように表示されました。
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を呼ぶことが出来ました。
Querierでアプリを開発する
Querierにログイン後、以下の作業を行います。
サイドメニュー「連携データ」での作業
サイドメニューから「連携データ」を選択し、データソースを新規作成します。下図のように、
- 表示名:sample_restapi
- データタイプ:restapi
- baseurl:http://yourdomain.com:13001
としました。
サイドメニュー「アプリ一覧」での作業
サイドメニューから「アプリ一覧」を選択し、アプリを新規作成します。以下のように、アプリ名をweather_forecasts_restapiとしました。
アプリ名の右側の三点ボタンから「編集」を選択し、アプリの編集画面に進みます。
データフローを4つ作成
下図のように、SQLのSELECT文、INSERT文、UPDATE文、DELETE文に該当する4つのデータフローを作成することにします。
4つ共通の設定として、「データソース=連携データで作成したsample_restapi」にします。
以下、データフローから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列の昇順でソートするよう指定しています。
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 }}"
}
としました。
下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのINSERT文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。
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 }}
としました。
下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのUPDATE文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。
DELETE文のデータフロー
名前を「df_delete_weather_forecasts」にしました。
「オプションの入力」は、PostgRESTの仕様に従い、
- メソッド:DELETE
- URL:/weather_forecasts
- データ:NONE
- URLパラメーター:id eq.{{ input_id.value }}
としました。
下にスクロールして、「成功時のトリガーを設定する」に「df_select_weather_forecasts」を設定します。
この設定を行うと、SQLのDELETE文が成功したら引き続きSELECT文が自動実行され、画面のテーブル表示が自動更新されます。
以上でデータフローの作成は完了です。
コンポーネントの配置と設定
カラムコンポーネント
画面右側からコンポーネントタブに行き、カラムコンポーネントを下図のようにドラッグ&ドロップします。
カラムコンポーネントの左側を広めにします。
テーブルコンポーネント「table1」
コンポーネントタブに行き、テーブルコンポーネントを下図のようにカラムコンポーネントの左側にドラッグ&ドロップします。
エディタータブに行き、データに以下を入力してSELECT文のデータフローを設定します。
{{ df_select_weather_forecasts.data }}
テーブルコンポーネントのカラム(列)が古いままですが、アプリ編集を一旦終了し、再び開き直してデータフロー「df_select_weather_forecasts」を実行したら更新されました。
インプットコンポーネント「input_dt」
コンポーネントタブに行き、インプットコンポーネントを下図のようにカラムコンポーネントの右側にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:input_dt
- ラベル:date
- DefaultValue:
{{ table1.selectedRow.data ? table1.selectedRow.data.dt : '' }}
- 必須:オン
DefaultValueにtable1のデータを指定したので、table1の別の行を選択すると値がinput_dtに反映されます。
インプットコンポーネント「input_id」
コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_dtの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:input_id
- ラベル:id
- DefaultValue:
{{ table1.selectedRow.data ? table1.selectedRow.data.id : '' }}
- 必須:オン
インプットコンポーネント「input_summary」
コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_idの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:input_summary
- ラベル:summary
- DefaultValue:
{{ table1.selectedRow.data ? table1.selectedRow.data.summary : '' }}
- 必須:オン
インプットコンポーネント「input_temperature_c」
コンポーネントタブに行き、インプットコンポーネントを下図のようにinput_summaryの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:input_temperature_c
- ラベル:temperature
- DefaultValue:
{{ table1.selectedRow.data ? table1.selectedRow.data.temperature_c : '' }}
- 必須:オン
ボタンコンポーネント「button_select」
コンポーネントタブに行き、ボタンコンポーネントを下図のようにtable1の下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:button_select
- ラベル:読み直し
- データフロー:df_select_weather_forecasts
ボタンコンポーネント「button_insert」
コンポーネントタブに行き、ボタンコンポーネントを下図のようにinput_temperature_cの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:button_insert
- ラベル:新規登録
- データフロー:df_insert_weather_forecasts
それでは存在しないデータを新規登録してみましょう。
インプットコンポーネントに適当な値を入力して、以下の画面で新規登録ボタンをクリックします。
以下のように、右上にdf_insert_weather_forecastsが成功したメッセージが表示さました。
また、今回のケースではテーブルコンポーネントに16ページ目が追加されて、df_insert_weather_forecastsに続いてdf_select_weather_forecastsが自動実行された様子です。
その16ページ目を表示させると、新規登録したデータが見えました。
ボタンコンポーネント「button_update」
コンポーネントタブに行き、ボタンコンポーネントを下図のようにbutton_insertの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:button_update
- ラベル:行更新
- データフロー:df_update_weather_forecasts
それでは先程新規登録したデータを更新してみましょう。
table1で先程のデータを選択後、インプットコンポーネントのid以外の値を適当に変更して、以下の画面で行更新ボタンをクリックします。
以下のように、右上にdf_update_weather_forecastsが成功したメッセージが表示さました。
また続いてdf_select_weather_forecastsも実行され、テーブル表示が自動更新されました。
ボタンコンポーネント「button_delete」
コンポーネントタブに行き、ボタンコンポーネントを下図のようにbutton_updateの下にドラッグ&ドロップします。
エディタータブに行き、以下のように設定します。
- ウィジェット名:button_delete
- ラベル:行削除
- データフロー:df_delete_weather_forecasts
それでは先程更新したデータを削除してみましょう。
table1で先程のデータを選択後、以下の画面で行削除ボタンをクリックします。
以下のように、右上にdf_delete_weather_forecastsが成功したメッセージが表示さました。
また続いてdf_select_weather_forecastsも実行され、今回のケースではテーブルコンポーネントから16ページ目がなくなりました。
以上、ひと通りCRUD機能を実装できました。
BIツールではできなかった、データの挿入・編集も可能でした。
使ってみての感想
これはエンジニア向けのツールですね。
ローコードなので、コードを書ける分ノーコードよりできることが多く、YesCoderにも受け入れられそうです。
UIをドラッグ&ドロップで構築できて、コードも追加できるので、かつてのDelphiを思い出しました。
共有データベースとQuerierを連携させれば、共有データベースを通じてYesCodeアプリとも機能連携できそうです。
まず共有データベースをしっかり作り、Querierで作れる機能はサクッと作って工数削減し、複雑な機能だけYesCodeで開発してQuerierと機能連携させるイメージです。
良いと思いますねー。機会があれば、ぜひ業務で使いたいです。
国産ツールなので日本語でサポートが受けられるのも、ありがたいですね。