camunda-forms について
camunda-forms は、Camunda 8.8 以降のユーザータスクや開始イベント向けフォーム JSON スキーマを作成するスキルです。コンポーネント配置、プロセス変数との連携、検証、条件付き表示、スキーマ確認を扱います。
主な機能
- 01.form スキーマの作成
- 02入力・表示・レイアウトコンポーネントの設定
- 03フィールドとプロセス変数の連携
- 04検証と条件付き表示の設定
- 05公式スキーマによるフォーム検証
活用シーン
- 01ユーザータスク用フォームの作成
- 02プロセス開始イベント用フォームの作成
- 03プロセス変数による入力値の設定と保存
- 04デプロイ前のフォームスキーマ確認
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ camunda/ skills --skill camunda-formsインストール後、Claude Code で/camunda-formsを入力して読み込まれたか確認します。
同梱ファイルportability.jsonreferences/component-reference.md
詳細
Camunda Forms
Camunda 8.8+ のユーザータスクと開始イベント用の Camunda Form JSON スキーマを作成します。
前提条件
- Camunda 8.8+ クラスター
関連項目
- camunda-bpmn:
<zeebe:formDefinition formId="..."/>を使用してフォームをユーザータスクにリンクする場合に使用します - camunda-feel:フォームの検証式と条件付き表示ロジックに使用します
手順
フォームの基本
Camunda Forms は .json ではなく、拡張子が .form の JSON ファイルです。フォームの id フィールドを使って BPMN ユーザータスクにリンクします。このフィールドは、ユーザータスク内の <zeebe:formDefinition formId="..."/> の formId 属性と一致する必要があります。ユーザータスク自体にも <zeebe:userTask/>(Camunda のユーザータスク実装)が含まれている必要があります。古い formKey 属性を使ってリンクしないでください。これは非推奨のジョブワーカー型ユーザータスクで使用されていたもので、Camunda 8.10 で削除されています。BPMN 側については、camunda-bpmn の「Form Definition」を参照してください。
フォームの構造
例 — 最小限のフォームの骨組み:
{
"components": [],
"executionPlatform": "Camunda Cloud",
"executionPlatformVersion": "8.8.0",
"exporter": { "name": "Camunda Modeler", "version": "5.34.0" },
"schemaVersion": 18,
"id": "my-form-id",
"type": "default"
}
すべてのメタデータフィールドが必須です。schemaVersion: 18 と executionPlatformVersion: "8.8.0" を使用してください。
コンポーネント
各コンポーネントには type、id、layout が必要です。入力コンポーネントには key(プロセス変数名に対応)と label も必要です。
{
"type": "textfield",
"id": "Field_Name",
"key": "customerName",
"label": "Customer Name",
"layout": { "row": "row_0", "columns": null }
}
入力コンポーネント:textfield、textarea、number、checkbox、checklist、radio、select、taglist、datetime
表示コンポーネント:text(Markdown)、html、image、separator、button
レイアウトコンポーネント:group、spacer
スキーマのプロパティ名は正確に使用してください。
- 入力の初期値は
defaultValueで設定します。コンポーネントにvalueを使用しないでください。valueは{ "label": "High", "value": "high" }のようなオプションオブジェクト内でのみ使用します。 - 送信コントロールには、
action: "submit"を指定したbuttonコンポーネントを使用します。type: "submit"は使用せず、ボタンにkeyを追加しないでください。
{
"type": "textfield",
"id": "Field_Name",
"key": "customerName",
"label": "Customer Name",
"defaultValue": "Ada",
"layout": { "row": "row_0", "columns": null }
}
{
"type": "button",
"id": "Button_submit",
"label": "Submit",
"action": "submit",
"layout": { "row": "row_1", "columns": null }
}
各コンポーネントタイプのプロパティについて詳しくは、references/component-reference.md を参照してください。
レイアウト
コンポーネントは行ごとに配置されます。同じ行にコンポーネントを配置すると、横並びで表示されます。
{ "type": "textfield", "id": "F1", "key": "firstName", "label": "First Name", "layout": { "row": "row_0", "columns": null } },
{ "type": "textfield", "id": "F2", "key": "lastName", "label": "Last Name", "layout": { "row": "row_0", "columns": null } }
変数のバインド
key プロパティは、フォームフィールドをプロセス変数に対応付けます。
- 読み込み時:変数が存在する場合、その値がフィールドに事前入力されます
- 送信時:フィールドの値が変数に書き戻されます
検証
{
"validate": {
"required": true,
"minLength": 2,
"maxLength": 100,
"min": 0,
"max": 10000,
"pattern": "^[A-Z]{2}[0-9]+$",
"validationExpression": "=amount <= budget"
}
}
カスタムエラーメッセージには、requiredErrorMessage と patternErrorMessage を使用します。
条件付き表示
FEEL を使用して、他のフィールドの値に基づいてフィールドを非表示または表示します。
{
"conditional": {
"hide": "=approved = false"
}
}
式は、すべてのフォームフィールドの値とプロセス変数を対象に評価されます。
Select/Radio/Checklist のオプション
静的な値:
{
"type": "select",
"values": [
{ "label": "Low", "value": "low" },
{ "label": "Medium", "value": "medium" },
{ "label": "High", "value": "high" }
]
}
プロセス変数からの動的な値:
{
"type": "select",
"valuesExpression": "=departments"
}
変数は {label, value} オブジェクトのリストにしてください。
読み取り専用フィールド
編集を許可せずにデータを表示します。
{
"type": "textfield",
"key": "orderId",
"label": "Order ID",
"readonly": true
}
テキスト/Markdown
Markdown を使って静的なテキストや手順を表示します。
{
"type": "text",
"text": "### Review Details\n\nPlease review the information below.",
"id": "Text_Header",
"layout": { "row": "row_0", "columns": null }
}
グループ
関連するフィールドを整理します。
{
"type": "group",
"label": "Contact Information",
"components": [
{ "type": "textfield", "id": "F1", "key": "email", "label": "Email", "layout": { "row": "row_g0", "columns": null } },
{ "type": "textfield", "id": "F2", "key": "phone", "label": "Phone", "layout": { "row": "row_g1", "columns": null } }
],
"id": "Group_Contact",
"layout": { "row": "row_1", "columns": null }
}
出力形式
完全な .form JSON ファイルを生成します。次の点を確認してください。
- すべての
id値がフォーム内で一意である key値が想定されるプロセス変数名と一致している- 入力のデフォルト値には
defaultValueを使用し、送信コントロールにはkeyを付けず、type: "button"とaction: "submit"を指定している layout.rowの値が連番(row_0、row_1、...)になっている- メタデータフィールドが存在し、正しく設定されている
スキーマ検証ループ(デプロイ前のリント)
.form ファイルを作成または編集した後は、提示またはデプロイする前に、公式の Camunda フォームスキーマに照らして検証してください。ajv と ajv-errors プラグインを直接使用します。公式スキーマではコンポーネントの拡張プロパティが許可されているため、以下のヘルパーでは、既知の無効なコンポーネント形状(value、type: "submit"、ボタンの key)も拒否します。このスキーマでは errorMessage キーワードが使われているため、単純な ajv-cli の実行は失敗します。ajv-cli を使用する場合は、--require で ajv-errors を読み込んでください。すべてのコマンドはプロジェクトルートから実行します。
npm install --save-dev ajv ajv-errors @bpmn-io/[email protected]
# Create the validation helper and commit it as a project tool
cat > validate-form.cjs << 'EOF'
const Ajv = require('ajv');
const addErrors = require('ajv-errors');
const fs = require('fs');
const schema = require('@bpmn-io/form-json-schema/resources/schema.json');
const file = process.argv[2];
if (!file) { console.error('Usage: node validate-form.cjs <path-to-form.form>'); process.exit(1); }
let form;
try { form = JSON.parse(fs.readFileSync(file, 'utf8')); }
catch (e) { console.error(`Cannot read/parse ${file}: ${e.message}`); process.exit(1); }
const ajv = new Ajv({ allErrors: true, strict: false });
addErrors(ajv);
const validate = ajv.compile(schema);
if (!validate(form)) { console.error(JSON.stringify(validate.errors, null, 2)); process.exit(1); }
function invalidComponentShapes(components, path = 'components') {
if (!Array.isArray(components)) return [];
const errors = [];
for (const [index, component] of components.entries()) {
if (!component || typeof component !== 'object' || Array.isArray(component)) continue;
const location = `${path}[${index}]`;
if (Object.prototype.hasOwnProperty.call(component, 'value')) {
errors.push(`${location}: use defaultValue instead of value on a component`);
}
if (component.type === 'submit') {
errors.push(`${location}: use type "button" with action "submit"`);
}
if (component.type === 'button' && Object.prototype.hasOwnProperty.call(component, 'key')) {
errors.push(`${location}: buttons must not define key`);
}
errors.push(...invalidComponentShapes(component.components, `${location}.components`));
}
return errors;
}
const shapeErrors = invalidComponentShapes(form.components);
if (shapeErrors.length) { console.error(JSON.stringify(shapeErrors, null, 2)); process.exit(1); }
console.log('Valid ✓');
EOF
node validate-form.cjs path/to/form.form
複数のフォーム(ネストされたディレクトリ内のものを含む)を検証する場合は、すべてのファイルをループ処理し、終了する前にすべての失敗を収集します。
failed=0
# Use find for recursive discovery; replace with *.form for flat directories
while IFS= read -r f; do
node validate-form.cjs "$f" || { echo "FAILED: $f"; failed=1; }
done < <(find . -name '*.form' -not -path '*/node_modules/*')
exit $failed
よくあるスキーマキーワードと修正方法:
| キーワード | 一般的な意味 | 一般的な修正方法 |
|---|---|---|
required | 必須フィールドがありません | エラーメッセージに記載された不足プロパティを追加します |
additionalProperties | このオブジェクトに不明なプロパティがあります | サポートされていないプロパティを削除します |
type | JSON 値の型が誤っています | 値を期待される型(string/number/boolean/object/array)に変更します |
enum / const | 値が許可されたセットに含まれていません | 許可された値のいずれかに置き換えます |
pattern | 文字列の形式が無効です(多くの場合、id/key) | 必須の正規表現(通常は英数字とアンダースコアの形式)に一致するよう名前を変更します |
トラブルシューティング
- フォームが Tasklist に表示されない — BPMN のユーザータスクに
<zeebe:userTask/>と、フォームのidフィールドに一致する<zeebe:formDefinition formId="..."/>が含まれていることを確認してください。従来のformKey属性は Camunda 8.10 で削除されました。 schemaVersionの不一致エラー —schemaVersion: 18とexecutionPlatformVersion: "8.8.0"を設定してください。古い値は 8.8 以降のクラスターで拒否されます。- 条件付きフィールドが常に非表示になる —
conditional.hideは、非表示にする場合に true を返す FEEL 式です。=の接頭辞を忘れると、リテラル文字列として扱われます。FEEL の詳細については、camunda-feel § よくあるパターンを参照してください。
参考資料
詳細なリファレンス資料は references/ から参照してください:
- component-reference.md — 各コンポーネントタイプ(textfield、number、select、datetime など)のプロパティと例を網羅しています