はじめに
今年の FOSS4G 2025 Japan にて「クラウド環境におけるマネージドなPostgreSQL/PostGIS の選択肢」を聞いて、最近のマネージドなデータベースサービスが気になっていました。FOSS4G Advent Calendar 2025 では、マネージドなデータベースサービスとして Neon について紹介しました。
今回は、Neon と同じように PostgreSQL 基盤 の環境を提供している Supabase(スパベース)を触ってみましたので紹介したいと思います。また、Supabase を利用して、クライアントアプリケーションも作成しました。クライアントアプリケーションの作成には、ArcGIS の ArcGIS Maps SDK for JavaScript を使用しました。
BaaS(Backend as a Service)の Supabase を使用して、ArcGIS Maps SDK for JavaScript で岐阜県の 500 m メッシュ別将来推計人口データ(R6 国政局推計)と地価公示データを表示した例です。



サンプルアプリは、GitHub にもアップしていますのでご確認いただけます。 valuecreation.github.io
ArcGIS Maps SDK for JavaScript は、マッピング アプリケーション開発用 Web API で、Web ブラウザー向けのアプリケーションや Web サイトに GIS(地図)機能を組み込むための API を提供します。ArcGIS Location Platform に開発者アカウントを作成することで、ArcGIS Location Platform (PaaS) のロケーションサービスを毎月一定枠まで無償でご利用することができ、すぐに開発をはじめることができます。
Supabase(スパベース)とは
Supabase は Postgres 開発プラットフォームです。Firebase のオープンソース代替を目指す BaaS(Backend as a Service)で、PostgreSQL データベースを基盤に、認証、リアルタイムデータベース、ストレージ、サーバーレス関数などを提供し、開発者がバックエンドの構築を迅速に行えるようにするプラットフォームです。データベースを設計するだけで、REST/GraphQL API が自動生成され、認証やファイル管理も簡単に実装できるため、Web・モバイルアプリ開発の工数を大幅に削減できます。
Supabase もNeon 同様に無償から始めることができ、PostGIS も使用できるため、開発者がデータベースを利用した地図アプリケーションを手軽に開発できるかと思います。
Supabase については、色々な方が試されており、Google 検索などから参考となる情報が幾つかありました。以下のサイトが参考になりました。
Supabase は、BaaS(バース)「Backend As A Service」だけあって、クライアントの API/SDK からも簡単に接続できる環境を提供しています。JavaScript からも簡単に接続が可能で ArcGIS Maps SDK for JavaScript からも簡単にデータベースにアクセスできました。
JavaScript ライブライには supabase-js が提供されています。その他、Python や Flutter、Swift など各言語にも対応しています。 supabase.com
Supabase の準備
Supabase の環境を準備するため、アカウントを作成します。無料プランを使用します。Supabase の各プランについては、以下をご確認ください。
現在の無料プランは、以下となっているようです。
カード登録は不要です。
| プラン名 | 価格 | 機能 |
|---|---|---|
| Free | 無料 | ・無制限の API リクエスト ・月間アクティブ ユーザー数 5 万人 ・500 MB のデータベースサイズ ・帯域 5GB ・ファイル ストレージ 1GB |
無料プロジェクトは 1 週間操作がないと一時停止されます。
アクティブなプロジェクトは 2 つまでです。
アカウント作成後、Supabase の環境を準備するため、プロジェクトを作成します。プロジェクト作成後、PostgreSQL の環境が使用できるようになります。



以下は、Database の Tables で、extensions の PostGIS を表示している例です。

Supabase で地理空間データを扱うため PostGIS ) を使用します。PostGIS 拡張機能を有効にするため、Supabase のダッシュボードで PostGIS 拡張機能を有効にします。Supabase の SQL エディタから、以下の CREATE EXTENSION ステートメントを実行して拡張機能を有効にすることも可能です。
-- Example: enable the "postgis" extension create extension postgis with schema "extensions";
PostGIS については、Supabase のガイドにも紹介されています。
PostGIS の環境が整ったら GIS データを登録しています。GIS データとして、国土数値情報の 500 mメッシュ別将来推計人口データ(R6 国政局推計)と地価公示データを使用しました。
上記のデータは、シェープファイル(Shapefile)のため、 PostGIS で使用するため、shp2pgsql コマンドからシェープファイルを SQL に変換して使用します。
shp2pgsql ユーティリティは、シェープファイルのデータを、バイナリから、データベースにデータをロードする一連の SQL コマンドに変換して PostGIS で使用できるようにします。
shp2pgsql コマンドからシェープファイルを SQL に変換します。ここでは、クライアント端末から行いました。
% shp2pgsql -W cp932 -D -I -s 4326 500m_mesh_2024_21.shp 500m_mesh_2024_21 > 500m_mesh_2024_21.sql % shp2pgsql -W cp932 -D -I -s 4326 L01-25_21.shp L01_25_21 > L01_25_21.sql
作成した 500m_mesh_2024_21.sql と L01_25_21.sql の SQL ファイルは、Supabase に接続された psql などのクライアントから実行しました。
% psql -h aws-1-ap-northeast-1.pooler.supabase.com -p 6543 -d postgres -U postgres.xxxxxxxxxxx -f 500m_mesh_2024_21.sql ユーザー postgres.xxxxxxxxxxx のパスワード: SET SET BEGIN CREATE TABLE ALTER TABLE addgeometrycolumn ------------------------------------------------------------------- public.500m_mesh_2024_21.geom SRID:4326 TYPE:MULTIPOLYGON DIMS:2 (1 行) COPY 10577 CREATE INDEX COMMIT ANALYZE % psql -h aws-1-ap-northeast-1.pooler.supabase.com -p 6543 -d postgres -U postgres.xxxxxxxxxxx -f L01_25_21.sql ユーザー postgres.xxxxxxxxxxx のパスワード: SET SET BEGIN CREATE TABLE ALTER TABLE addgeometrycolumn ---------------------------------------------------- public.l01_25_21.geom SRID:4326 TYPE:POINT DIMS:2 (1 行) COPY 376 CREATE INDEX COMMIT ANALYZE
作成したテーブルは、Table Editor などから参照して確認できます。SQL Editor からも確認できます。

ArcGIS Maps SDK for JavaScript から Supabase を利用
ArcGIS Maps SDK for JavaScript から Supabase への接続には supabase-js を使用しました。Supabase のドキュメントの JavaScript Client Library を参考にしました。
ArcGIS Maps SDK for JavaScript はサンプルソースを使用しました。サンプルは、GitHub に公開されており、今回は Components (Map and Charts) Vite sample をベースにして実装しました。
Visual Studio Code コード エディターなどから Git で Clone した Components (Map and Charts) Vite sample を開き、npm コマンドで、supabase をインストールします。
npm install @supabase/supabase-js
Supabase への接続と各テーブルへのアクセスは、以下のように実装しました。
// Supabase import import { createClient } from '@supabase/supabase-js' const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseKey = import.meta.env.VITE_SUPABASE_KEY; // Create a single supabase client for interacting with your database const supabase = createClient(supabaseUrl, supabaseKey); const gifu_Land_price = await supabase.from('l01_25_21').select('l01_007, l01_008, l01_009, l01_025, l01_029, l01_047, l01_048, geom'); const gifu_500m_mesh = await supabase.from('500m_mesh_2024_21').select('ptn_2020, ptn_2025, geom');
Supabase への接続 は、Connect to your project からも確認することができます。

ArcGIS Maps SDK for JavaScript では、 FeatureLayer と呼ばれるレイヤーを使用しました。ArcGIS Maps SDK for JavaScript では、FeatureLayer クラスを使用しました。Supabase への接続から地図に表示するまでの全体的なコードは、以下となります。細かい説明は割愛します。
<!doctype html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Components Vite sample</title> <link rel="icon" href="data:;base64,=" /> </head> <!-- API キー認証を使用する場合、下記を利用してください --> <script> var esriConfig = { apiKey: "<API キー>" }; </script> <body> <arcgis-map basemap="arcgis/topographic" center="136.7222, 35.3911" zoom="12"> <arcgis-zoom slot="top-left"></arcgis-zoom> <arcgis-search slot="top-right"></arcgis-search> <arcgis-legend slot="bottom-left"></arcgis-legend> </arcgis-map> <script type="module" src="./main.js"></script> </body> </html>
import "./styles.css"; // Individual imports for each component import "@arcgis/map-components/components/arcgis-map"; import "@arcgis/map-components/components/arcgis-zoom"; import "@arcgis/map-components/components/arcgis-legend"; import "@arcgis/map-components/components/arcgis-search"; // Core API import import Graphic from "@arcgis/core/Graphic.js"; import FeatureLayer from "@arcgis/core/layers/FeatureLayer.js"; // Supabase import import { createClient } from '@supabase/supabase-js' const viewElement = document.querySelector("arcgis-map"); //await viewElement.viewOnReady(); const supabaseUrl = import.meta.env.VITE_SUPABASE_URL; const supabaseKey = import.meta.env.VITE_SUPABASE_KEY; //console.log(supabaseUrl); //console.log(supabaseKey); // Create a single supabase client for interacting with your database const supabase = createClient(supabaseUrl, supabaseKey); const gifu_Land_price= await supabase.from('l01_25_21').select('l01_007, l01_008, l01_009, l01_025, l01_029, l01_047, l01_048, geom'); const gifu_500m_mesh = await supabase.from('500m_mesh_2024_21').select('ptn_2020, ptn_2025, geom'); const polygonGraphics = [] for (let value of gifu_500m_mesh.data) { //console.log(value); // Configure a polygon's geometry. This is the Bermuda Triangle. const polygon = { type: "polygon", // autocasts as new Polygon() rings: [ value.geom.coordinates[0][0] ] }; // Create a polygon graphic with the polygon geometry and symbol. const polygonGraphic = new Graphic({ geometry: polygon, attributes: { ptn_2020: value.ptn_2020, ptn_2025: value.ptn_2025 }, }); polygonGraphics.push(polygonGraphic); } // Create a symbol to define the polygon's style. const gifu_mesh_renderer = { type: "simple", // autocasts as new SimpleFillSymbol() symbol: { type: "simple-fill", color: [79, 167, 227], outline: { // autocasts as new SimpleLineSymbol() color: [255, 255, 255], width: 0.5 } } }; // 可視化変数(visualVariables)レンダラー gifu_mesh_renderer.visualVariables = [ { type: "opacity", // indicates this is a color visual variable field: "ptn_2025", // total population in poverty legendOptions: { title: "2025年:岐阜県 500 m メッシュ別将来推計人口データ(R6 国政局推計)" }, stops: [ { value: 0, // features where < 10% of the pop in poverty opacity: "0.2", // will be assigned this color (beige) label: "0", // label to display in the legend }, { value: 2500, // features where > 30% of the pop in poverty opacity: "1.0", // will be assigned this color (purple) label: "2,500", // label to display in the legend } ] } ]; // popup template const gifu_mesh_template = { title: "500 m メッシュ別将来推計人口データ(R6国政局推計)", content: [ { type: "fields", fieldInfos: [ { fieldName: "ptn_2020", label: "2020 年の総数人口" }, { fieldName: "ptn_2025", label: "2025 年男女計総数人口" } ] } ] }; // Typical usage // Create featurelayer from client-side graphics const gifu_mesh_featureLayer = new FeatureLayer({ title: "500 m メッシュ別将来推計人口データ", source: polygonGraphics, fields: [{ name: "ptn_2020", alias: "ptn_2020", type: "double" }, { name: "ptn_2025", alias: "ptn_2025", type: "double" }], objectIdField: "ObjectID", popupTemplate: gifu_mesh_template, geometryType: "polygon", renderer: gifu_mesh_renderer }); const pointGraphics = []; for (let value of gifu_Land_price.data) { //console.log(value); // Define a point geometry const point = { type: "point", longitude: value.geom.coordinates[0], latitude: value.geom.coordinates[1], }; // Create a graphic and add the geometry and symbol to it const pointGraphic = new Graphic({ geometry: point, attributes: { l01_007: value.l01_007, l01_008: value.l01_008, l01_009: value.l01_009, l01_025: value.l01_025, l01_029: value.l01_029, l01_047: value.l01_047, l01_048: value.l01_048 } }); pointGraphics.push(pointGraphic); } // Create a symbol to define the polygon's style. const gifu_Land_price_renderer = { type: "simple", symbol: { type: "simple-marker", style: "circle", size: 10, color: "red", outline: { color: "white", width: 1, }, } }; // 可視化変数(visualVariables)レンダラー gifu_Land_price_renderer.visualVariables = [ { type: "opacity", // indicates this is a color visual variable field: "l01_008", // total population in poverty legendOptions: { title: "2025年:岐阜県 標準地の当年の地価公示価格(単位は[円/㎡])" }, stops: [ { value: 10000, // features where < 10% of the pop in poverty opacity: "0.2", // will be assigned this color (beige) label: "10,000", // label to display in the legend }, { value: 700000, // features where > 30% of the pop in poverty opacity: "1.0", // will be assigned this color (purple) label: "700,000", // label to display in the legend } ] } ]; // popup template const gifu_Land_price_template = { title: "岐阜県 地価公示データ", content: [ { type: "fields", fieldInfos: [ { fieldName: "l01_007", label: "年度" }, { fieldName: "l01_008", label: "地価公示価格 [円/㎡]" }, { fieldName: "l01_009", label: "対前年変動率" }, { fieldName: "l01_025", label: "所在並びに地番" }, { fieldName: "l01_029", label: "利用の現況" }, { fieldName: "l01_047", label: "周辺の土地利用の状況" }, { fieldName: "l01_048", label: "最寄り駅名" } ] } ] }; // Typical usage // Create featurelayer from client-side graphics const gifu_Land_price_featureLayer = new FeatureLayer({ title: "地価公示データ", source: pointGraphics, fields: [ { name: "l01_007", alias: "年度", type: "integer" }, { name: "l01_008", alias: "地価公示価格", type: "double" }, { name: "l01_009", alias: "対前年変動率", type: "double" }, { name: "l01_025", alias: "所在並びに地番", type: "string" }, { name: "l01_029", alias: "利用の現況", type: "string" }, { name: "l01_047", alias: "周辺の土地利用の状況", type: "string" }, { name: "l01_048", alias: "最寄り駅名", type: "string" } ], objectIdField: "ObjectID", popupTemplate: gifu_Land_price_template, geometryType: "point", renderer: gifu_Land_price_renderer }); // The view is now ready to be used. viewElement.map.addMany([gifu_mesh_featureLayer, gifu_Land_price_featureLayer]);
開発段階は、npm run dev コマンドでローカル環境で確認し、デプロイの準備ができたら npm run build コマンドでアプリをビルドします。デフォルトでは、ビルドの出力場所が dist となっています。
npm run build
アプリをビルドしたら、npm run preview コマンドを実行し、ローカルでテストします。テストで問題なければ、dist の中身を GitHub Pages で見れるように指定の場所にデプロイします。
npm run preview
ArcGIS Maps SDK for JavaScript を使用して、地図アプリケーションを開発にするあたりに Web GIS を利用できるアカウント (ArcGIS Online/ArcGIS Enterprise/ArcGIS Location Platform) が必要となります。アカウントをお持ちであれば、無償でご利用いただけます。 ここでは、ArcGIS Location Platform を利用しました。ArcGIS Location Platform は、高品質なマップとロケーションサービスを提供しています。ArcGIS Location Platform は、開発者アカウントを作成することで、無償から利用できます。開発者アカウントの作成や ArcGIS Location Platform の使い方などについては、ESRIジャパンが運用している ArcGIS 開発リソース集の開発者アカウントの作成が参考になります。
ArcGIS のサービスを利用するためには API キーなどのアクセス トークンが必要となります。API キーの作成は、ArcGIS 開発リソース集の API キーの取得をご確認ください。
ArcGIS Maps SDK for JavaScript の詳細な情報は、以下のガイドをご確認ください。開発をはじめるための具体的なステップや様々なサンプルがあるなど、初心者で分かりやすいように丁寧に解説しています。
また、はじめて Web マップ アプリ開発を行う方に向けて、ArcGIS Maps SDK for JavaScript を使用した Web マップ アプリ開発の流れを紹介しており、こちらも大変分かりやすく解説しています。

まとめ
今回は、BaaS(Backend as a Service)の Supabase と ArcGIS との連携を試してみました。Neon と同じように開発者は、アプリケーションの開発に集中できるよう簡単にデータベースと連携できるのは、とても便利だと思いました。また、PostGIS も利用できるもうれしいです。