Nocode App - 動的データベースアプリケーションプラットフォーム

動的にデータベーステーブルを作成・管理できるWebアプリケーションプラットフォーム
DB 一択化(2026-04-25): 本プロジェクトは PostgreSQL 16 のみ をサポートします。MySQL からの移行手順は docs/plans/2026-04-25-postgres-only-data-migration.md を参照してください。
- プロジェクト概要
- 外部データソース接続
- 認証と認可
- 技術スタック
- システムアーキテクチャ
- データベース設計
- API設計
- フロントエンド設計
- セットアップ手順
- 開発ガイドライン
- テスト
Nocode Appは、プログラミング知識なしでビジネスアプリケーションを構築できるノーコードプラットフォームです。ユーザーはGUIを通じて動的にデータベーステーブルを作成し、データの入力・表示・分析を行えます。
| 機能カテゴリ |
機能詳細 |
| アプリ管理 |
アプリ(テーブル)の作成・編集・削除、フィールド定義のドラッグ&ドロップ設計 |
| データ管理 |
レコードのCRUD操作、一覧表示、検索・フィルタリング、ソート |
| ダッシュボード |
アプリデータのウィジェット表示、DnD並び替え、表示形式設定 |
| 表示モード |
テーブルビュー、リストビュー(カード形式)、グラフビュー |
| グラフ機能 |
棒グラフ(縦/横)、折れ線グラフ、円グラフ/ドーナツ、散布図、面グラフ |
| 認証機能 |
ユーザー登録、ログイン/ログアウト、JWT認証、ロールベースアクセス制御 |
| 外部データソース |
外部DB接続、テーブル取得、カラム別名設定、読み取り専用データ表示 |
| タイプ |
説明 |
PostgreSQLカラム型 |
text |
単一行テキスト |
VARCHAR(255) |
textarea |
複数行テキスト |
TEXT |
number |
数値 |
NUMERIC(18,4) |
date |
日付 |
DATE |
datetime |
日時 |
TIMESTAMP |
select |
単一選択(ドロップダウン) |
VARCHAR(255) |
multiselect |
複数選択 |
JSONB |
checkbox |
チェックボックス |
BOOLEAN |
radio |
ラジオボタン |
VARCHAR(255) |
link |
URL/メールリンク |
VARCHAR(500) |
attachment |
ファイル添付 |
JSONB (メタデータ) |
外部データソース接続機能により、既存のデータベースに接続してテーブルデータを読み取り専用で表示できます。新規にテーブルを作成する機能だけではなく、既存システムのデータを活用したアプリケーションを構築できます。
| データベース |
ドライバー |
備考 |
| PostgreSQL |
lib/pq |
Pure Go実装 |
重要: 2026-04-25 の DB 一択化により、外部データソースとしてサポートする RDB は PostgreSQL のみです。MySQL / Oracle / SQL Server のサポートは廃止されました。
- システム全体で共有: 管理者が登録したデータソースは複数のアプリで再利用可能
- 接続情報の暗号化: パスワードはAES-256-GCMで暗号化して保存
- テスト接続: データソース登録前に接続テストを実行可能
flowchart TB
Start["アプリ作成開始"]
TypeSelect{"データソース\nタイプ選択"}
NewTable["新規テーブル作成"]
ExternalDS["外部データソース接続"]
SelectDS["データソース選択"]
TestConn["テスト接続"]
SelectTable["テーブル選択"]
SelectColumns["カラム選択・別名設定"]
FieldDesign["フィールド設計\n(ドラッグ&ドロップ)"]
Complete["アプリ作成完了"]
Start --> TypeSelect
TypeSelect -->|新規テーブル| NewTable
TypeSelect -->|外部接続| ExternalDS
NewTable --> FieldDesign
ExternalDS --> SelectDS
SelectDS --> TestConn
TestConn --> SelectTable
SelectTable --> SelectColumns
SelectColumns --> Complete
FieldDesign --> Complete
Loading
外部テーブルのカラムに対して表示用の別名を設定できます:
| 設定項目 |
説明 |
| 元カラム名(source_column_name) |
外部テーブルの実際のカラム名 |
| フィールドコード(field_code) |
システム内部で使用する識別子 |
| フィールド名(field_name) |
UIに表示される別名 |
| フィールドタイプ(field_type) |
データ型のマッピング |
外部データソースから作成されたアプリは読み取り専用となります:
| 操作 |
新規テーブル |
外部データソース |
| レコード一覧表示 |
✅ |
✅ |
| レコード詳細表示 |
✅ |
✅ |
| レコード作成 |
✅ (admin) |
❌ |
| レコード編集 |
✅ (admin) |
❌ |
| レコード削除 |
✅ (admin) |
❌ |
| グラフ表示 |
✅ |
✅ |
注意: 外部データソースからのデータは、管理者・一般ユーザーを問わず変更操作が禁止されています。
┌─────────────────────────────────────────────────────────────┐
│ AES-256-GCM 暗号化 │
├─────────────────────────────────────────────────────────────┤
│ 1. 環境変数 ENCRYPTION_KEY から32バイトの暗号化キーを取得 │
│ 2. ランダムな12バイトのNonce(IV)を生成 │
│ 3. パスワードをAES-256-GCMで暗号化 │
│ 4. Nonce + 暗号文をBase64エンコードしてDBに保存 │
└─────────────────────────────────────────────────────────────┘
# 32バイト(256ビット)の暗号化キーを生成
openssl rand -base64 32
生成された文字列を環境変数 ENCRYPTION_KEY に設定してください。
⚠️ 重要: 本番環境では必ず安全な方法で生成したキーを使用し、秘密管理システム(AWS Secrets Manager、HashiCorp Vault等)で管理してください。
- 接続パスワードは平文で保存されない
- APIレスポンスにパスワードは含まれない
- データソースの作成・編集・削除は管理者のみ可能
本システムはJWT(JSON Web Token)ベースの認証を採用しています。
sequenceDiagram
participant User
participant Frontend
participant Backend
participant Database
User->>Frontend: ログイン(email, password)
Frontend->>Backend: POST /api/v1/auth/login
Backend->>Database: ユーザー検証
Database-->>Backend: ユーザー情報
Backend->>Backend: パスワード検証(bcrypt)
Backend->>Backend: JWTトークン生成
Backend-->>Frontend: { token, user }
Frontend->>Frontend: トークンをlocalStorageに保存
Frontend-->>User: ダッシュボードへ遷移
Note over Frontend,Backend: 以降のAPIリクエスト
Frontend->>Backend: Authorization: Bearer {token}
Backend->>Backend: トークン検証
Backend-->>Frontend: レスポンス
Loading
| クレーム |
説明 |
user_id |
ユーザーID |
email |
メールアドレス |
role |
ユーザーロール(admin/user) |
exp |
有効期限(デフォルト24時間) |
iat |
発行日時 |
- パスワードは
bcrypt でハッシュ化して保存
- JWTトークンは
HS256 アルゴリズムで署名
- トークンの有効期限は環境変数
JWT_EXPIRY_HOURS で設定可能
- APIエンドポイントは
Authorization ヘッダーでトークンを検証
本システムはロールベースアクセス制御(RBAC)を採用しています。
| ロール |
説明 |
admin |
管理者:全ての操作が可能 |
user |
一般ユーザー:閲覧のみ可能 |
| 操作カテゴリ |
操作 |
admin |
user |
| 認証 |
ログイン/ログアウト |
✅ |
✅ |
|
プロフィール編集 |
✅ |
✅ |
|
パスワード変更 |
✅ |
✅ |
| アプリ |
アプリ一覧表示 |
✅ |
✅ |
|
アプリ詳細表示 |
✅ |
✅ |
|
アプリ作成 |
✅ |
❌ |
|
アプリ編集 |
✅ |
❌ |
|
アプリ削除 |
✅ |
❌ |
| フィールド |
フィールド一覧表示 |
✅ |
✅ |
|
フィールド追加/編集/削除 |
✅ |
❌ |
|
フィールド順序変更 |
✅ |
❌ |
| レコード |
レコード一覧表示 |
✅ |
✅ |
|
レコード詳細表示 |
✅ |
✅ |
|
レコード作成 |
✅ |
❌ |
|
レコード編集 |
✅ |
❌ |
|
レコード削除 |
✅ |
❌ |
|
一括操作 |
✅ |
❌ |
| ビュー |
ビュー一覧表示 |
✅ |
✅ |
|
ビュー作成/編集/削除 |
✅ |
❌ |
| グラフ |
グラフデータ表示 |
✅ |
✅ |
|
グラフ設定保存/削除 |
✅ |
❌ |
| ユーザー管理 |
ユーザー一覧表示 |
✅ |
❌ |
|
ユーザー作成/編集/削除 |
✅ |
❌ |
// RequireAdmin ミドルウェアで管理者権限をチェック
func RequireAdmin(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
claims, ok := GetUserFromContext(r.Context())
if !ok {
utils.WriteErrorResponse(w, http.StatusUnauthorized, "authentication required")
return
}
if claims.Role != "admin" {
utils.WriteErrorResponse(w, http.StatusForbidden, "管理者権限が必要です")
return
}
next(w, r)
}
}
// useAuth フックで isAdmin を提供
const { isAdmin } = useAuth();
// UI要素を条件付きでレンダリング
{isAdmin && (
<Button onClick={handleCreate}>新規作成</Button>
)}
| HTTPステータス |
状況 |
レスポンス例 |
| 401 Unauthorized |
認証なし/トークン無効 |
{"error": "missing authorization header"} |
| 403 Forbidden |
権限不足 |
{"error": "管理者権限が必要です"} |
| 技術 |
バージョン |
用途 |
| Go |
1.25+ |
メイン言語 |
| net/http |
(標準) |
HTTPサーバー/ルーター |
| BUN |
v1 |
ORM/マイグレーション |
| go-playground/validator |
v10 |
バリデーション |
| golang-jwt/jwt |
v5 |
JWT認証 |
| golang.org/x/crypto |
latest |
パスワードハッシュ |
| 技術 |
バージョン |
用途 |
| React |
18+ |
UIライブラリ |
| TypeScript |
5+ |
型安全性 |
| Vite |
6+ |
ビルドツール |
| Chakra UI |
2+ |
UIコンポーネント |
| React Router |
6+ |
ルーティング |
| TanStack Query |
5+ |
データフェッチング/キャッシュ |
| Recharts |
2+ |
グラフ描画 |
| @dnd-kit |
6+ |
ドラッグ&ドロップ |
| Axios |
1+ |
HTTPクライアント |
| Zustand |
5+ |
状態管理 |
| 技術 |
バージョン |
用途 |
| Docker |
24+ |
コンテナ化 |
| Docker Compose |
2+ |
オーケストレーション |
| PostgreSQL |
16+ |
データベース |
| Nginx |
latest |
リバースプロキシ(本番用) |
flowchart TB
subgraph Client["Client (Browser)"]
FE["React + Chakra UI + Recharts"]
end
subgraph Backend["Backend (Go + net/http)"]
subgraph Handlers
AH["Auth Handler"]
APH["Apps Handler"]
FH["Fields Handler"]
RH["Records Handler"]
end
subgraph Services["Service Layer"]
AS["Auth Service"]
APS["App Service"]
DTS["DynamicTable Service"]
end
subgraph Repositories["Repository Layer"]
UR["User Repo"]
AR["App Repo"]
DQ["DynamicQuery Executor"]
end
end
subgraph Database["PostgreSQL 16"]
UT["users"]
AT["apps"]
AFT["app_fields"]
DT["Dynamic Tables (app_data_xxx)"]
end
Client -->|HTTP/REST| Handlers
Handlers --> Services
Services --> Repositories
Repositories -->|SQL| Database
Loading
nocode-app/
├── README.md # 本ドキュメント
├── docker-compose.yaml # Docker Compose設定
├── .env.example # 環境変数テンプレート
│
├── backend/
│ ├── Dockerfile
│ ├── go.mod
│ ├── go.sum
│ │
│ ├── cmd/
│ │ └── server/
│ │ └── main.go # エントリポイント・サーバー起動
│ │
│ ├── internal/
│ │ ├── config/
│ │ │ └── config.go # 設定管理
│ │ │
│ │ ├── middleware/
│ │ │ ├── auth.go # JWT認証ミドルウェア
│ │ │ ├── cors.go # CORS設定
│ │ │ └── logger.go # ロギング
│ │ │
│ │ ├── models/
│ │ │ ├── user.go # ユーザーモデル
│ │ │ ├── app.go # アプリモデル
│ │ │ ├── field.go # フィールド定義モデル
│ │ │ ├── record.go # レコードモデル
│ │ │ └── view.go # ビュー設定モデル
│ │ │
│ │ ├── handlers/
│ │ │ ├── auth.go # 認証ハンドラー
│ │ │ ├── app.go # アプリCRUD
│ │ │ ├── field.go # フィールド管理
│ │ │ ├── record.go # レコードCRUD
│ │ │ └── chart.go # グラフデータ
│ │ │
│ │ ├── services/
│ │ │ ├── auth_service.go
│ │ │ ├── app_service.go
│ │ │ ├── dynamic_table_service.go # 動的テーブル管理
│ │ │ └── chart_service.go
│ │ │
│ │ ├── repositories/
│ │ │ ├── user_repository.go
│ │ │ ├── app_repository.go
│ │ │ └── dynamic_query.go # 動的SQLクエリビルダー
│ │ │
│ │ └── utils/
│ │ ├── jwt.go
│ │ ├── password.go
│ │ └── validator.go
│ │
│ └── migrations/
│ └── init.sql # 初期マイグレーション
│
├── frontend/
│ ├── Dockerfile
│ ├── package.json
│ ├── tsconfig.json
│ ├── vite.config.ts
│ ├── index.html
│ │
│ ├── public/
│ │ └── favicon.ico
│ │
│ └── src/
│ ├── main.tsx # エントリポイント
│ ├── App.tsx # ルートコンポーネント
│ ├── vite-env.d.ts
│ │
│ ├── api/
│ │ ├── client.ts # Axiosインスタンス
│ │ ├── auth.ts # 認証API
│ │ ├── apps.ts # アプリAPI
│ │ ├── records.ts # レコードAPI
│ │ └── charts.ts # グラフAPI
│ │
│ ├── components/
│ │ ├── common/
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ ├── Loading.tsx
│ │ │ └── ErrorBoundary.tsx
│ │ │
│ │ ├── auth/
│ │ │ ├── LoginForm.tsx
│ │ │ └── RegisterForm.tsx
│ │ │
│ │ ├── apps/
│ │ │ ├── AppList.tsx
│ │ │ ├── AppCard.tsx
│ │ │ └── AppFormBuilder.tsx # フィールド設計UI
│ │ │
│ │ ├── fields/
│ │ │ ├── FieldPalette.tsx # ドラッグ元
│ │ │ ├── FieldDropZone.tsx # ドロップ先
│ │ │ ├── FieldEditor.tsx # フィールド設定
│ │ │ └── field-types/
│ │ │ ├── TextField.tsx
│ │ │ ├── NumberField.tsx
│ │ │ ├── DateField.tsx
│ │ │ ├── SelectField.tsx
│ │ │ └── ...
│ │ │
│ │ ├── records/
│ │ │ ├── RecordTable.tsx # テーブルビュー
│ │ │ ├── RecordList.tsx # リストビュー
│ │ │ ├── RecordForm.tsx # 入力フォーム
│ │ │ └── RecordDetail.tsx
│ │ │
│ │ ├── views/
│ │ │ ├── ViewSelector.tsx
│ │ │ └── ViewSettings.tsx
│ │ │
│ │ └── charts/
│ │ ├── ChartBuilder.tsx # グラフ設定UI
│ │ ├── ChartPreview.tsx
│ │ ├── BarChart.tsx
│ │ ├── LineChart.tsx
│ │ ├── PieChart.tsx
│ │ ├── ScatterChart.tsx
│ │ └── AreaChart.tsx
│ │
│ ├── hooks/
│ │ ├── useAuth.ts
│ │ ├── useApps.ts
│ │ ├── useRecords.ts
│ │ └── useCharts.ts
│ │
│ ├── pages/
│ │ ├── LoginPage.tsx
│ │ ├── RegisterPage.tsx
│ │ ├── DashboardPage.tsx
│ │ ├── AppListPage.tsx
│ │ ├── AppBuilderPage.tsx
│ │ ├── RecordsPage.tsx
│ │ └── ChartPage.tsx
│ │
│ ├── stores/
│ │ ├── authStore.ts
│ │ └── appStore.ts
│ │
│ ├── types/
│ │ ├── auth.ts
│ │ ├── app.ts
│ │ ├── field.ts
│ │ ├── record.ts
│ │ └── chart.ts
│ │
│ ├── utils/
│ │ ├── constants.ts
│ │ └── helpers.ts
│ │
│ └── theme/
│ └── index.ts # Chakra UIテーマ
│
└── nginx/
└── nginx.conf # 本番用Nginx設定
erDiagram
users ||--o{ apps : creates
users ||--o{ app_data_xxx : creates
users ||--o{ data_sources : creates
users ||--o{ dashboard_widgets : has
data_sources ||--o{ apps : provides
apps ||--o{ app_fields : has
apps ||--o{ app_views : has
apps ||--o{ app_data_xxx : contains
apps ||--o{ dashboard_widgets : displayed_in
users {
bigint id PK
varchar email UK
varchar password_hash
varchar name
enum role
timestamp created_at
timestamp updated_at
}
data_sources {
bigint id PK
varchar name UK
enum db_type
varchar host
int port
varchar database_name
varchar username
text encrypted_password
bigint created_by FK
timestamp created_at
timestamp updated_at
}
apps {
bigint id PK
varchar name
text description
varchar table_name UK
varchar icon
boolean is_external
bigint data_source_id FK
varchar source_table_name
bigint created_by FK
timestamp created_at
timestamp updated_at
}
app_fields {
bigint id PK
bigint app_id FK
varchar field_code
varchar field_name
varchar field_type
varchar source_column_name
json options
boolean required
int display_order
timestamp created_at
timestamp updated_at
}
app_views {
bigint id PK
bigint app_id FK
varchar name
enum view_type
json config
boolean is_default
timestamp created_at
timestamp updated_at
}
app_data_xxx {
bigint id PK
any dynamic_columns
bigint created_by FK
timestamp created_at
timestamp updated_at
}
dashboard_widgets {
bigint id PK
bigint user_id FK
bigint app_id FK
int display_order
enum view_type
boolean is_visible
enum widget_size
json config
timestamp created_at
timestamp updated_at
}
Loading
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| email |
VARCHAR(255) |
UNIQUE, NOT NULL |
メールアドレス |
| password_hash |
VARCHAR(255) |
NOT NULL |
bcryptハッシュ |
| name |
VARCHAR(100) |
NOT NULL |
表示名 |
| role |
VARCHAR(20) CHECK (role IN ('admin','user')) |
DEFAULT 'user' |
ロール |
| created_at |
TIMESTAMP |
DEFAULT CURRENT_TIMESTAMP |
作成日時 |
| updated_at |
TIMESTAMP |
DEFAULT CURRENT_TIMESTAMP(set_updated_at() BEFORE UPDATE トリガで自動更新) |
更新日時 |
外部データベースへの接続情報を管理するテーブル。
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| name |
VARCHAR(100) |
UNIQUE, NOT NULL |
データソース名 |
| db_type |
VARCHAR(20) CHECK (db_type IN ('postgresql')) |
NOT NULL |
データベースタイプ(PostgreSQL のみ) |
| host |
VARCHAR(255) |
NOT NULL |
ホスト名/IPアドレス |
| port |
INT |
NOT NULL |
ポート番号 |
| database_name |
VARCHAR(100) |
NOT NULL |
データベース名 |
| username |
VARCHAR(100) |
NOT NULL |
接続ユーザー名 |
| encrypted_password |
TEXT |
NOT NULL |
AES-256-GCM暗号化パスワード |
| created_by |
BIGINT |
FK → users.id |
作成者 |
| created_at |
TIMESTAMP |
DEFAULT CURRENT_TIMESTAMP |
作成日時 |
| updated_at |
TIMESTAMP |
DEFAULT CURRENT_TIMESTAMP(set_updated_at() BEFORE UPDATE トリガで自動更新) |
更新日時 |
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| name |
VARCHAR(100) |
NOT NULL |
アプリ名 |
| description |
TEXT |
|
説明 |
| table_name |
VARCHAR(64) |
UNIQUE, NOT NULL |
動的テーブル名(app_data_xxx形式) |
| icon |
VARCHAR(50) |
DEFAULT 'default' |
アイコン識別子 |
| is_external |
BOOLEAN |
DEFAULT FALSE |
外部データソースフラグ |
| data_source_id |
BIGINT |
FK → data_sources.id, NULL |
外部データソースID |
| source_table_name |
VARCHAR(100) |
NULL |
外部DBのテーブル名 |
| created_by |
BIGINT |
FK → users.id |
作成者 |
| created_at |
TIMESTAMP |
|
作成日時 |
| updated_at |
TIMESTAMP |
|
更新日時 |
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| app_id |
BIGINT |
FK → apps.id, NOT NULL |
所属アプリ |
| field_code |
VARCHAR(64) |
NOT NULL |
フィールドコード(カラム名として使用) |
| field_name |
VARCHAR(100) |
NOT NULL |
表示名 |
| field_type |
VARCHAR(20) |
NOT NULL |
フィールドタイプ |
| source_column_name |
VARCHAR(100) |
NULL |
外部テーブルの元カラム名 |
| options |
JSONB |
|
選択肢・設定等 |
| required |
BOOLEAN |
DEFAULT FALSE |
必須フラグ |
| display_order |
INT |
DEFAULT 0 |
表示順序 |
| created_at |
TIMESTAMP |
|
作成日時 |
| updated_at |
TIMESTAMP |
|
更新日時 |
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| app_id |
BIGINT |
FK → apps.id |
所属アプリ |
| name |
VARCHAR(100) |
NOT NULL |
ビュー名 |
| view_type |
VARCHAR(20) CHECK (view_type IN ('table','list','calendar','chart')) |
NOT NULL |
ビュータイプ |
| config |
JSONB |
|
ビュー設定(カラム表示、ソート、フィルタ等) |
| is_default |
BOOLEAN |
DEFAULT FALSE |
デフォルトビューフラグ |
| created_at |
TIMESTAMP |
|
作成日時 |
| updated_at |
TIMESTAMP |
|
更新日時 |
ユーザーごとのダッシュボードウィジェット設定を管理するテーブル。
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
主キー |
| user_id |
BIGINT |
FK → users.id, NOT NULL |
所有ユーザー |
| app_id |
BIGINT |
FK → apps.id, NOT NULL |
対象アプリ |
| display_order |
INT |
DEFAULT 0 |
表示順序 |
| view_type |
VARCHAR(20) CHECK (view_type IN ('table','list','chart')) |
DEFAULT 'table' |
表示形式 |
| is_visible |
BOOLEAN |
DEFAULT TRUE |
表示フラグ |
| widget_size |
VARCHAR(20) CHECK (widget_size IN ('small','medium','large')) |
DEFAULT 'medium' |
ウィジェットサイズ |
| config |
JSONB |
NULL |
追加設定 |
| created_at |
TIMESTAMP |
|
作成日時 |
| updated_at |
TIMESTAMP |
|
更新日時 |
ユニーク制約: (user_id, app_id) - 同一ユーザー・アプリの組み合わせは1つのみ
アプリ作成時に動的に生成されるテーブル。命名規則: app_data_{app_id}
| カラム名 |
型 |
制約 |
説明 |
| id |
BIGSERIAL |
PK |
レコードID |
| {field_code} |
各フィールドタイプに対応 |
|
動的カラム |
| created_by |
BIGINT |
FK → users.id |
作成者 |
| created_at |
TIMESTAMP |
|
作成日時 |
| updated_at |
TIMESTAMP |
|
更新日時 |
| メソッド |
エンドポイント |
説明 |
| POST |
/api/v1/auth/register |
ユーザー登録 |
| POST |
/api/v1/auth/login |
ログイン(JWT発行) |
| POST |
/api/v1/auth/refresh |
トークンリフレッシュ |
| GET |
/api/v1/auth/me |
現在のユーザー情報取得 |
| PUT |
/api/v1/auth/profile |
自分のプロフィール更新(名前) |
| PUT |
/api/v1/auth/password |
パスワード変更 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/users |
ユーザー一覧取得 |
| POST |
/api/v1/users |
ユーザー作成(招待) |
| GET |
/api/v1/users/:id |
ユーザー詳細取得 |
| PUT |
/api/v1/users/:id |
ユーザー更新(名前、ロール) |
| DELETE |
/api/v1/users/:id |
ユーザー削除 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/datasources |
データソース一覧取得 |
| POST |
/api/v1/datasources |
データソース作成 |
| GET |
/api/v1/datasources/:id |
データソース詳細取得 |
| PUT |
/api/v1/datasources/:id |
データソース更新 |
| DELETE |
/api/v1/datasources/:id |
データソース削除 |
| POST |
/api/v1/datasources/test |
テスト接続(保存前の接続確認) |
| GET |
/api/v1/datasources/:id/tables |
テーブル一覧取得 |
| GET |
/api/v1/datasources/:id/tables/:table/columns |
カラム一覧取得 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/apps |
アプリ一覧取得 |
| POST |
/api/v1/apps |
アプリ作成(テーブル生成含む) |
| GET |
/api/v1/apps/:id |
アプリ詳細取得 |
| PUT |
/api/v1/apps/:id |
アプリ更新 |
| DELETE |
/api/v1/apps/:id |
アプリ削除(テーブル削除含む) |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/apps/:appId/fields |
フィールド一覧取得 |
| POST |
/api/v1/apps/:appId/fields |
フィールド追加(ALTER TABLE) |
| PUT |
/api/v1/apps/:appId/fields/:id |
フィールド更新 |
| DELETE |
/api/v1/apps/:appId/fields/:id |
フィールド削除(ALTER TABLE) |
| PUT |
/api/v1/apps/:appId/fields/order |
フィールド順序更新 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/apps/:appId/records |
レコード一覧取得(ページネーション、フィルタ、ソート対応) |
| POST |
/api/v1/apps/:appId/records |
レコード作成 |
| GET |
/api/v1/apps/:appId/records/:id |
レコード詳細取得 |
| PUT |
/api/v1/apps/:appId/records/:id |
レコード更新 |
| DELETE |
/api/v1/apps/:appId/records/:id |
レコード削除 |
| POST |
/api/v1/apps/:appId/records/bulk |
一括登録 |
| DELETE |
/api/v1/apps/:appId/records/bulk |
一括削除 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/apps/:appId/views |
ビュー一覧取得 |
| POST |
/api/v1/apps/:appId/views |
ビュー作成 |
| PUT |
/api/v1/apps/:appId/views/:id |
ビュー更新 |
| DELETE |
/api/v1/apps/:appId/views/:id |
ビュー削除 |
| メソッド |
エンドポイント |
説明 |
| POST |
/api/v1/apps/:appId/charts/data |
グラフ用データ取得(集計処理) |
| GET |
/api/v1/apps/:appId/charts/config |
保存済みグラフ設定一覧 |
| POST |
/api/v1/apps/:appId/charts/config |
グラフ設定保存 |
| メソッド |
エンドポイント |
説明 |
| GET |
/api/v1/dashboard/widgets |
ウィジェット一覧取得 |
| POST |
/api/v1/dashboard/widgets |
ウィジェット作成 |
| PUT |
/api/v1/dashboard/widgets/:id |
ウィジェット更新 |
| DELETE |
/api/v1/dashboard/widgets/:id |
ウィジェット削除 |
| PUT |
/api/v1/dashboard/widgets/reorder |
並び替え |
| PUT |
/api/v1/dashboard/widgets/:id/toggle |
表示/非表示切替 |
// PUT /api/v1/auth/profile
// Request
{
"name": "新しい名前"
}
// Response
{
"id": 1,
"email": "user@example.com",
"name": "新しい名前",
"role": "user",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
// PUT /api/v1/auth/password
// Request
{
"current_password": "oldpassword",
"new_password": "newpassword123"
}
// Response
{
"message": "パスワードを変更しました"
}
// GET /api/v1/users?page=1&limit=20
// Response
{
"users": [
{
"id": 1,
"email": "admin@example.com",
"name": "管理者",
"role": "admin",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
},
{
"id": 2,
"email": "user@example.com",
"name": "一般ユーザー",
"role": "user",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 2,
"total_pages": 1
}
}
// POST /api/v1/users
// Request
{
"email": "newuser@example.com",
"password": "password123",
"name": "新規ユーザー",
"role": "user"
}
// Response
{
"id": 3,
"email": "newuser@example.com",
"name": "新規ユーザー",
"role": "user",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
// PUT /api/v1/users/2
// Request
{
"name": "更新された名前",
"role": "admin"
}
// Response
{
"id": 2,
"email": "user@example.com",
"name": "更新された名前",
"role": "admin",
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
// POST /api/v1/apps
// Request
{
"name": "顧客管理",
"description": "顧客情報を管理するアプリ",
"icon": "users",
"fields": [
{
"field_code": "customer_name",
"field_name": "顧客名",
"field_type": "text",
"required": true,
"display_order": 1
},
{
"field_code": "email",
"field_name": "メールアドレス",
"field_type": "link",
"options": { "link_type": "email" },
"required": true,
"display_order": 2
},
{
"field_code": "status",
"field_name": "ステータス",
"field_type": "select",
"options": {
"choices": ["見込み", "商談中", "成約", "失注"]
},
"display_order": 3
}
]
}
// Response
{
"id": 1,
"name": "顧客管理",
"description": "顧客情報を管理するアプリ",
"table_name": "app_data_1",
"icon": "users",
"fields": [...],
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
// GET /api/v1/apps/1/records?page=1&limit=20&sort=created_at:desc&filter=status:eq:商談中
// Response
{
"records": [
{
"id": 1,
"customer_name": "株式会社ABC",
"email": "contact@abc.co.jp",
"status": "商談中",
"created_by": 1,
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-01-01T00:00:00Z"
}
],
"pagination": {
"page": 1,
"limit": 20,
"total": 150,
"total_pages": 8
}
}
// POST /api/v1/apps/1/charts/data
// Request
{
"chart_type": "bar",
"x_axis": {
"field": "status",
"label": "ステータス"
},
"y_axis": {
"aggregation": "count",
"label": "件数"
},
"filters": []
}
// Response
{
"labels": ["見込み", "商談中", "成約", "失注"],
"datasets": [
{
"label": "件数",
"data": [45, 30, 60, 15]
}
]
}
flowchart TB
Login["Login Page"]
Register["Register Page"]
Dashboard["Dashboard Page<br/>(Widget Grid)"]
Logout["Logout"]
AppList["App List Page"]
AppBuilder["App Builder"]
Settings["Settings Page"]
DataManagement["Data Management Page"]
ProfileSettings["Profile Settings"]
UserManagement["User Management (admin)"]
AppSettings["App Settings<br/>(Dashboard Display)"]
Records["Records Page"]
TableView["Table View"]
ListView["List View"]
ChartView["Chart View"]
Login --> Register
Login --> Dashboard
Login --> Logout
Dashboard --> AppList
Dashboard --> AppBuilder
Dashboard --> Settings
Dashboard --> DataManagement
Settings --> ProfileSettings
Settings --> UserManagement
Settings --> AppSettings
DataManagement --> Records
AppList --> Records
Records --> TableView
Records --> ListView
Records --> ChartView
Loading
AppFormBuilder(フィールド設計UI)
┌──────────────────────────────────────────────────────────────┐
│ アプリ設定: 顧客管理 [保存] [×] │
├──────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────────────────────┐ │
│ │ フィールド │ │ フォームプレビュー │ │
│ │ パレット │ │ ┌─────────────────────────────────┐ │ │
│ │ │ │ │ ╔═══════════════════════════╗ │ │ │
│ │ [テキスト] │ │ │ ║ 顧客名 * ║ │ │ │
│ │ [数値] │ │ │ ╚═══════════════════════════╝ │ │ │
│ │ [日付] │ │ │ ┌───────────────────────────┐ │ │ │
│ │ [選択] │ │ │ │ メールアドレス * │ │ │ │
│ │ [複数選択] │ │ │ └───────────────────────────┘ │ │ │
│ │ [チェック] │ │ │ ┌───────────────────────────┐ │ │ │
│ │ [添付] │ │ │ │ ステータス ▼ │ │ │ │
│ │ ... │ │ │ └───────────────────────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ ←ドラッグ │ │ │ ここにドロップ │ │ │
│ └─────────────┘ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ フィールド設定 │ │
│ │ 名前: [顧客名 ] │ │
│ │ コード: [customer_name] │ │
│ │ 必須: [✓] │ │
│ └─────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 顧客管理 [+ 追加] │
├──────────────────────────────────────────────────────────────┤
│ ビュー: [テーブル ▼] [リスト] [グラフ] 検索: [________] │
├──────────────────────────────────────────────────────────────┤
│ □ │ ID │ 顧客名 ▲ │ メール │ ステータス │ ... │
├───┼────┼───────────────┼─────────────────┼────────────┼─────┤
│ □ │ 1 │ 株式会社ABC │ abc@example.com │ 商談中 │ ... │
│ □ │ 2 │ 株式会社XYZ │ xyz@example.com │ 成約 │ ... │
│ □ │ 3 │ DEF商事 │ def@example.com │ 見込み │ ... │
├───┴────┴───────────────┴─────────────────┴────────────┴─────┤
│ [選択削除] < 1 2 3 ... 8 > 20件/ページ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ グラフ作成 [保存] │
├──────────────────────────────────────────────────────────────┤
│ ┌─────────────────────┐ ┌─────────────────────────────────┐ │
│ │ グラフ種類 │ │ │ │
│ │ (●) 棒グラフ │ │ ████ │ │
│ │ ( ) 横棒グラフ │ │ ████ ████ │ │
│ │ ( ) 折れ線グラフ │ │ ████ ████ ████ │ │
│ │ ( ) 円グラフ │ │ ████ ████ ████ ████ │ │
│ │ ( ) ドーナツ │ │ ────────────────── │ │
│ │ ( ) 散布図 │ │ 見込 商談 成約 失注 │ │
│ │ ( ) 面グラフ │ │ │ │
│ ├─────────────────────┤ │ プレビュー │ │
│ │ X軸 │ │ │ │
│ │ フィールド: [ステータス ▼] │ │
│ ├─────────────────────┤ │ │ │
│ │ Y軸 │ │ │ │
│ │ 集計: [件数 ▼] │ │ │ │
│ │ フィールド: [-- ▼] │ └─────────────────────────────────┘ │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ ダッシュボード [+ ウィジェット追加] │
├──────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────┐ ┌─────────────────────────────┐ │
│ │ アプリ数 ユーザー数 │ │ 総レコード数 今日の更新 │ │
│ │ 12 5 │ │ 1,234 45 │ │
│ └─────────────────────────┘ └─────────────────────────────┘ │
├──────────────────────────────────────────────────────────────┤
│ アプリウィジェット │
│ ┌────────────────────────────┐ ┌────────────────────────────┐│
│ │ ☰ 顧客管理 テーブル │ │ ☰ 案件管理 グラフ ││
│ │ ┌──────────────────────┐ │ │ ┌──────────────────────┐ ││
│ │ │ ID │ 名前 │ 状態 │ │ │ │ ████ │ ││
│ │ │ 1 │ ABC社 │ 商談中 │ │ │ │ ████████ │ ││
│ │ │ 2 │ XYZ社 │ 成約 │ │ │ │ ████████████ │ ││
│ │ │ 3 │ DEF社 │ 見込み │ │ │ └──────────────────────┘ ││
│ │ └──────────────────────┘ │ │ 進行中 完了 保留 ││
│ └────────────────────────────┘ └────────────────────────────┘│
│ ↑ドラッグ&ドロップで並び替え↑ │
└──────────────────────────────────────────────────────────────┘
ダッシュボード機能:
- ウィジェット表示: 各アプリのデータをテーブル/リスト/グラフ形式で表示
- DnD並び替え: @dnd-kitによるドラッグ&ドロップでウィジェット順序を変更
- 表示設定: アプリ設定ページでウィジェットの表示形式・サイズ・表示ON/OFFを設定
- Docker Desktop 4.0以上
- Docker Compose 2.0以上
- Node.js 20以上(ローカル開発時)
- Go 1.25以上(ローカル開発時)
# リポジトリのクローン
cd nocode-app
# 環境変数ファイルの作成
cp .env.example .env
# .envを編集して必要な値を設定
# コンテナのビルドと起動
docker compose up -d --build
# ログの確認
docker compose logs -f
# 停止
docker compose down
# ボリュームも含めて削除
docker compose down -v
# .env.example
# PostgreSQL
POSTGRES_DB=nocode-app
POSTGRES_USER=nocode
POSTGRES_PASSWORD=nocodepassword
# Backend
DB_HOST=postgres
DB_PORT=5432
DB_USER=nocode
DB_PASSWORD=nocodepassword
DB_NAME=nocode-app
JWT_SECRET=your-super-secret-jwt-key-change-in-production
JWT_EXPIRY_HOURS=24
ENCRYPTION_KEY=your-32-byte-base64-encoded-encryption-key
# Frontend
VITE_API_URL=http://localhost:8080/api/v1
- ESLint + Prettier による自動フォーマット
- コンポーネントは関数コンポーネント + Hooks
- 型定義は types/ ディレクトリに集約
- カスタムフックは use プレフィックス
- main: 本番リリース用
- develop: 開発統合ブランチ
- feature/xxx: 機能開発
- fix/xxx: バグ修正
コミットメッセージは Conventional Commits 形式:
feat: アプリ作成機能を追加
fix: レコード削除時のエラーを修正
docs: README更新
refactor: ハンドラーの共通処理を抽出
クローン直後に一度だけ以下を実行すると、git commit の前にバックエンド/フロントエンドの品質検証が自動で走り、失敗するとコミットを中断する:
これは git config core.hooksPath .githooks を設定し、.githooks/pre-commit を有効化するだけのスクリプト。フックは変更ファイルの場所を見て関連スイートだけを走らせる:
| 変更場所 |
走る検証 |
backend/** |
gofmt -l .(差分なし), go vet ./..., go test -short ./... |
frontend/** |
pnpm run typecheck, pnpm run lint, pnpm run format:check, pnpm test -- --run |
CI(GitHub Actions, .github/workflows/ci.yml)も同じスイートを PR 時に実行する。フックを通すこと = CI が通ることを意味する。
cd backend
# 全テスト実行(lint含む)
./lint.sh
# ユニットテストのみ
go test ./...
# カバレッジ付き
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
cd frontend
# ユニットテスト
pnpm test
# カバレッジ付き
pnpm test -- --coverage
# lint + typecheck
pnpm check
cd e2e
# 依存関係インストール
pnpm install
pnpm exec playwright install
# テスト実行(ヘッドレス)
pnpm test
# UIモードで実行
pnpm test:ui
# 特定のテストファイルのみ
pnpm test tests/auth.spec.ts
# レポート表示
pnpm exec playwright show-report
- Docker Composeでアプリケーションが起動していること
http://localhost:3000 でフロントエンドにアクセス可能
http://localhost:8080 でバックエンドAPIにアクセス可能
| テストファイル |
テスト内容 |
auth.spec.ts |
ユーザー登録、ログイン、ログアウト |
apps.spec.ts |
アプリ作成、編集、削除 |
records.spec.ts |
レコードのCRUD、ビュー切替 |
settings.spec.ts |
プロフィール、パスワード変更、ユーザー管理 |
authorization.spec.ts |
ロールベースアクセス制御の検証 |
E2Eテストは冪等性を保証するため、以下の戦略を採用しています:
- テストごとの独立したデータ: 各テストはタイムスタンプ付きのユニークなデータを作成
- テスト後のクリーンアップ: 作成したリソースはテスト終了時に削除
- 順序非依存: テストは任意の順序で実行可能
MIT License