動的にデータベーステーブルを作成・管理できるWebアプリケーションプラットフォーム
Nocode Appは、プログラミング知識なしでビジネスアプリケーションを構築できるノーコードプラットフォームです。ユーザーはGUIを通じて動的にデータベーステーブルを作成し、データの入力・表示・分析を行えます。
| 機能カテゴリ | 機能詳細 |
|---|---|
| アプリ管理 | アプリ(テーブル)の作成・編集・削除、フィールド定義のドラッグ&ドロップ設計 |
| データ管理 | レコードのCRUD操作、一覧表示、検索・フィルタリング、ソート |
| ダッシュボード | アプリデータのウィジェット表示、DnD並び替え、表示形式設定 |
| 表示モード | テーブルビュー、リストビュー(カード形式)、グラフビュー |
| グラフ機能 | 棒グラフ(縦/横)、折れ線グラフ、円グラフ/ドーナツ、散布図、面グラフ |
| 認証機能 | ユーザー登録、ログイン/ログアウト、JWT認証、ロールベースアクセス制御 |
| 外部データソース | 外部DB接続、テーブル取得、カラム別名設定、読み取り専用データ表示 |
| タイプ | 説明 | MySQLカラム型 |
|---|---|---|
text |
単一行テキスト | VARCHAR(255) |
textarea |
複数行テキスト | TEXT |
number |
数値 | DECIMAL(18,4) |
date |
日付 | DATE |
datetime |
日時 | DATETIME |
select |
単一選択(ドロップダウン) | VARCHAR(255) |
multiselect |
複数選択 | JSON |
checkbox |
チェックボックス | BOOLEAN |
radio |
ラジオボタン | VARCHAR(255) |
link |
URL/メールリンク | VARCHAR(500) |
attachment |
ファイル添付 | JSON (メタデータ) |
外部データソース接続機能により、既存のデータベースに接続してテーブルデータを読み取り専用で表示できます。新規にテーブルを作成する機能だけではなく、既存システムのデータを活用したアプリケーションを構築できます。
| データベース | ドライバー | 備考 |
|---|---|---|
| PostgreSQL | lib/pq | Pure Go実装 |
| MySQL | go-sql-driver/mysql | Pure Go実装 |
| Oracle | sijms/go-ora/v2 | Pure Go実装(Thin Mode) |
| SQL Server | denisenkom/go-mssqldb | Pure Go実装 |
重要: すべてのドライバーはThin Mode(Pure Go実装)を採用しており、外部ライブラリ(Oracle Instant Clientなど)のインストールは不要です。
- システム全体で共有: 管理者が登録したデータソースは複数のアプリで再利用可能
- 接続情報の暗号化: パスワードは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
外部テーブルのカラムに対して表示用の別名を設定できます:
| 設定項目 | 説明 |
|---|---|
| 元カラム名(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: レスポンス
| クレーム | 説明 |
|---|---|
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.24+ | メイン言語 |
| 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+ | オーケストレーション |
| MySQL | 8.0+ | データベース |
| 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["MySQL 8.0"]
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
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
}
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| VARCHAR(255) | UNIQUE, NOT NULL | メールアドレス | |
| password_hash | VARCHAR(255) | NOT NULL | bcryptハッシュ |
| name | VARCHAR(100) | NOT NULL | 表示名 |
| role | ENUM('admin','user') | DEFAULT 'user' | ロール |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 作成日時 |
| updated_at | TIMESTAMP | ON UPDATE CURRENT_TIMESTAMP | 更新日時 |
外部データベースへの接続情報を管理するテーブル。
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| name | VARCHAR(100) | UNIQUE, NOT NULL | データソース名 |
| db_type | ENUM('postgresql','mysql','oracle','sqlserver') | NOT NULL | データベースタイプ |
| 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 UNSIGNED | FK → users.id | 作成者 |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | 作成日時 |
| updated_at | TIMESTAMP | ON UPDATE CURRENT_TIMESTAMP | 更新日時 |
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| 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 UNSIGNED | FK → data_sources.id, NULL | 外部データソースID |
| source_table_name | VARCHAR(100) | NULL | 外部DBのテーブル名 |
| created_by | BIGINT UNSIGNED | FK → users.id | 作成者 |
| created_at | TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | 更新日時 |
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| app_id | BIGINT UNSIGNED | 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 | JSON | 選択肢・設定等 | |
| required | BOOLEAN | DEFAULT FALSE | 必須フラグ |
| display_order | INT | DEFAULT 0 | 表示順序 |
| created_at | TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | 更新日時 |
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| app_id | BIGINT UNSIGNED | FK → apps.id | 所属アプリ |
| name | VARCHAR(100) | NOT NULL | ビュー名 |
| view_type | ENUM('table','list','calendar','chart') | NOT NULL | ビュータイプ |
| config | JSON | ビュー設定(カラム表示、ソート、フィルタ等) | |
| is_default | BOOLEAN | DEFAULT FALSE | デフォルトビューフラグ |
| created_at | TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | 更新日時 |
ユーザーごとのダッシュボードウィジェット設定を管理するテーブル。
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | 主キー |
| user_id | BIGINT UNSIGNED | FK → users.id, NOT NULL | 所有ユーザー |
| app_id | BIGINT UNSIGNED | FK → apps.id, NOT NULL | 対象アプリ |
| display_order | INT | DEFAULT 0 | 表示順序 |
| view_type | ENUM('table','list','chart') | DEFAULT 'table' | 表示形式 |
| is_visible | BOOLEAN | DEFAULT TRUE | 表示フラグ |
| widget_size | ENUM('small','medium','large') | DEFAULT 'medium' | ウィジェットサイズ |
| config | JSON | NULL | 追加設定 |
| created_at | TIMESTAMP | 作成日時 | |
| updated_at | TIMESTAMP | 更新日時 |
ユニーク制約: (user_id, app_id) - 同一ユーザー・アプリの組み合わせは1つのみ
アプリ作成時に動的に生成されるテーブル。命名規則: app_data_{app_id}
| カラム名 | 型 | 制約 | 説明 |
|---|---|---|---|
| id | BIGINT UNSIGNED | PK, AUTO_INCREMENT | レコードID |
| {field_code} | 各フィールドタイプに対応 | 動的カラム | |
| created_by | BIGINT UNSIGNED | 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
┌──────────────────────────────────────────────────────────────┐
│ アプリ設定: 顧客管理 [保存] [×] │
├──────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────────────────────────────────┐ │
│ │ フィールド │ │ フォームプレビュー │ │
│ │ パレット │ │ ┌─────────────────────────────────┐ │ │
│ │ │ │ │ ╔═══════════════════════════╗ │ │ │
│ │ [テキスト] │ │ │ ║ 顧客名 * ║ │ │ │
│ │ [数値] │ │ │ ╚═══════════════════════════╝ │ │ │
│ │ [日付] │ │ │ ┌───────────────────────────┐ │ │ │
│ │ [選択] │ │ │ │ メールアドレス * │ │ │ │
│ │ [複数選択] │ │ │ └───────────────────────────┘ │ │ │
│ │ [チェック] │ │ │ ┌───────────────────────────┐ │ │ │
│ │ [添付] │ │ │ │ ステータス ▼ │ │ │ │
│ │ ... │ │ │ └───────────────────────────┘ │ │ │
│ │ │ │ │ │ │ │
│ │ ←ドラッグ │ │ │ ここにドロップ │ │ │
│ └─────────────┘ └─────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────┐ │
│ │ フィールド設定 │ │
│ │ 名前: [顧客名 ] │ │
│ │ コード: [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.22以上(ローカル開発時)
# リポジトリのクローン
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
# MySQL
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=nocode-app
MYSQL_USER=nocode
MYSQL_PASSWORD=nocodepassword
# Backend
DB_HOST=mysql
DB_PORT=3306
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| サービス | URL |
|---|---|
| フロントエンド | http://localhost:3000 |
| バックエンドAPI | http://localhost:8080/api/v1 |
| MySQL | localhost:3306 |
- Effective Go に準拠
- パッケージ名は小文字単数形
- エラーは必ずハンドリング
- コメントはGoDoc形式で記述
- ESLint + Prettier による自動フォーマット
- コンポーネントは関数コンポーネント + Hooks
- 型定義は types/ ディレクトリに集約
- カスタムフックは use プレフィックス
- main: 本番リリース用
- develop: 開発統合ブランチ
- feature/xxx: 機能開発
- fix/xxx: バグ修正
コミットメッセージは Conventional Commits 形式:
feat: アプリ作成機能を追加
fix: レコード削除時のエラーを修正
docs: README更新
refactor: ハンドラーの共通処理を抽出
cd backend
# 全テスト実行(lint含む)
./lint.sh
# ユニットテストのみ
go test ./...
# カバレッジ付き
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.outcd frontend
# ユニットテスト
pnpm test
# カバレッジ付き
pnpm test -- --coverage
# lint + typecheck
pnpm checkcd 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