operations skill
RESTfulエンドポイントのAPI操作ライフサイクル、セキュリティ式、バリデーショングループ、およびパラメータレベルの制約を設定します。
- GitHubスター
- 29
- ファイル
- 1
- 最終コミット
- 2026年10月22日
- ライセンス
- —
- 許可されたツール
- —
operations skillは何をしますか
operationsはAgent Skillsです。 RESTfulエンドポイントのAPI操作ライフサイクル、セキュリティ式、バリデーショングループ、およびパラメータレベルの制約を設定します。.
skillのSKILL.mdから生成された要約。
operations skill について
このSKILLは、API Platformを使用する開発者が、RESTfulエンドポイントのAPI操作ライフサイクル、セキュリティ、およびバリデーションを設定できるようにします。リクエストの読み取りおよび書き込みフェーズを細かく制御でき、プロバイダーやプロセッサを切り替えて、ファイルのダウンロードやカスタムのupsertロジックなどのタスクを処理することが可能です。また、異なるHTTPメソッドに対して読み取りおよび書き込みフラグを明示的に設定することで、CQRSの分離を管理できます。
ライフサイクル管理に加えて、このSKILLはSymfony ExpressionLanguageを使用して、ロールベースまたはオブジェクトベースのアクセス制御によるエンドポイントの保護機能を提供します。クエリパラメータやヘッダーパラメータに対するパラメータレベルのセキュリティも扱えます。データの整合性については、作成時と更新時の制約を区別するためのバリデーショングループや、デノーマライズ失敗時のエラー収集をサポートしています。さらに、特定のヘッダーを介したエンドポイントの非推奨化(deprecation)の管理や、JSON merge-patchシナリオにおけるネストされたPATCHリクエストの処理に関するガイダンスも提供します。
エージェントができること
- 1ロールによるエンドポイントアクセスの制限AIエージェントがPOST操作を設定し、管理者権限を持つユーザーのみが実行できるようにします。
- 2異なるバリデーションルールの適用開発者がエージェントに対し、ユーザーリソースの作成時と更新時で特定のバリデーショングループを使用するよう指示できます。
- 3特定のクエリパラメータの保護プロバイダーが実行される前に、特定のクエリパラメータをセキュリティ式に基づいて検証するようリクエストできます。
- 4APIの非推奨化の通知エージェントが既存のリソースに非推奨の理由とsunset dateを追加し、クライアントに今後の変更を通知できます。
- 5詳細なエラーレポートの有効化最初のエラーで停止するのではなく、すべての不正なフィールドのエラーを一度に返すようにAPIを設定できます。
skill の SKILL.md から作成されています。すべてのユースケースは、その引用と照合されています。
operations skillのインストール方法
各コマンドはskillフォルダをクライアントのskillsディレクトリにコピーします。読み込むには、クライアントを再起動するか新しいセッションを開始してください。プラグインにoperations skillをインストールする
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Claude Codeにoperations skillをインストールする
~/.claude/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.claude/skills/operationsClaude Code (project)にoperations skillをインストールする
.claude/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations .claude/skills/operationsCodexにoperations skillをインストールする
~/.agents/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.agents/skills/operationsGemini CLIにoperations skillをインストールする
~/.gemini/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.gemini/skills/operationsClaude.aiにoperations skillをインストールする
npx degit api-platform/skillset/skills/operations operations && zip -r operations.zip operations # claude.ai → Customize → Skills → + → Create skill → Upload a skill → operations.zip
SKILL.md
GitHubで表示Operations: Security, Validation & Lifecycle
Operation-level configuration that sits between the resource shape (see api-resource) and the read/write logic (see state-provider / state-processor).
Read & write phases (the provider/processor lifecycle)
Every operation runs two phases, each toggled by a boolean flag:
| Phase | Flag | Runs | Maps to (CQRS) |
|---|---|---|---|
| Read | read |
a provider fetches the data | Query |
| Write | write |
a processor persists / acts on the data | Command |
A full request is read → deserialize → validate → write → serialize. The provider
supplies the object the rest of the chain operates on; the processor performs the
side effect (persist, delete, send mail, build a file). This is API Platform's CQRS
split: GET-style reads go through a provider, state changes go through a
processor.
The flags default from the request, not the verb
You leave read/write unset and API Platform resolves them at runtime from the
HTTP request (see MainController / WriteListener in core):
writedefaults to "the method is not safe" —falseforGET/HEAD,trueforPOST,PUT,PATCH,DELETE.readdefaults to "the operation has URI variables, or the method is safe" — so item operations (which carry{id}) and allGETs read, but a collectionPOST(no URI variables, unsafe) does not.
| Operation | read |
write |
|---|---|---|
Get (item) |
true (has {id}) |
false |
GetCollection |
true (safe) |
false |
Post (collection) |
false (no URI vars, unsafe) |
true |
Put, Patch (item) |
true (has {id}) |
true |
Delete (item) |
true (has {id}) |
true |
So a Get skips the processor by default, and a collection Post runs a processor
without a provider. Override either flag to decouple the phase from this default:
// Run a processor on a GET (file download, report generation, counter bump):
new Get(
uriTemplate: '/orders/{id}/download',
write: true, // turn the write phase ON — processor now fires
processor: OrderDownloadProcessor::class,
)
// Skip the built-in fetch on an item write (upsert: processor handles a missing row):
new Put(
read: false, // no provider runs; $data comes from deserialization only
processor: UpsertProcessor::class,
)
write: true is the common case: a Get whose write defaults to false would
never invoke its processor — flipping the flag is what enables it. Conversely
read: false stops the built-in provider on an item operation from a needless fetch.
Securing operations
The security attribute takes a Symfony ExpressionLanguage string evaluated
before the operation runs. Available variables: user, object (item operations),
request parameters when explicitly exposed.
Role-based
#[ApiResource(
operations: [
new GetCollection(),
new Post(security: "is_granted('ROLE_ADMIN')"),
]
)]
class Invoice {}
Object-based (item operations)
object is the fetched resource — use it for ownership checks on Get, Put,
Patch, Delete:
new Get(security: "object.getOwner() == user")
new Patch(security: "object.getOwner() == user or is_granted('ROLE_ADMIN')")
securityPostDenormalize runs after the request body is applied — use it when the
decision depends on incoming values (e.g. preventing privilege escalation on PATCH).
Parameter-based
Each parameter carries its own security expression, where the parameter name
becomes a variable bound to the submitted value. Declare them in parameters as
QueryParameter or HeaderParameter:
use ApiPlatform\Metadata\GetCollection;
use ApiPlatform\Metadata\HeaderParameter;
use ApiPlatform\Metadata\QueryParameter;
use Symfony\Component\TypeInfo\Type\BuiltinType;
use Symfony\Component\TypeInfo\TypeIdentifier;
#[GetCollection(
parameters: [
'name' => new QueryParameter(security: 'is_granted("ROLE_ADMIN")'),
'auth' => new HeaderParameter(security: '"secured" == auth', nativeType: new BuiltinType(TypeIdentifier::STRING)),
'secret' => new QueryParameter(security: '"secured" == secret', nativeType: new BuiltinType(TypeIdentifier::STRING)),
],
)]
?name=foo evaluates is_granted("ROLE_ADMIN") (403 otherwise); the auth header
or ?secret=… must equal secured or the request is rejected. The parameter is
only checked when present — an absent or value-less parameter passes.
For query-level data isolation (multi-tenant, soft-delete) that must apply to every query regardless of operation, use a Doctrine extension or link handler instead — see securing-collections.
securityonly guards an already-fetched object; it does not scope collections.
Validation
API Platform runs Symfony's Validator on the deserialized object before the processor. Constraints live on the resource/input DTO (see custom-validator).
Validation groups per operation
#[ApiResource(
operations: [
new Post(validationContext: ['groups' => ['Default', 'user:create']]),
new Patch(validationContext: ['groups' => ['Default', 'user:update']]),
]
)]
#[Assert\NotBlank(groups: ['user:create'])] // required on create only
#[Assert\Email(groups: ['user:create', 'user:update'])] // checked on both
public ?string $email = null;
Collect denormalization errors
By default a type mismatch in the body throws on the first bad field. Enable
collect_denormalization_errors to report every malformed field at once in the 422
violations array:
#[Post(validationContext: ['collect_denormalization_errors' => true])]
Each malformed field becomes a violations entry with a propertyPath, a
This value should be of type … message, and a hint explaining the failure.
Validating query parameters
Attach constraints to a QueryParameter (or HeaderParameter); invalid values
yield 422 before the provider runs:
use ApiPlatform\Metadata\QueryParameter;
use Symfony\Component\Validator\Constraints as Assert;
#[GetCollection(
parameters: [
'page' => new QueryParameter(constraints: [new Assert\Positive()]),
],
)]
Deprecating endpoints
deprecationReason adds a Deprecation header; sunset adds a Sunset header
with the removal date. Apply at resource or operation level.
#[ApiResource(
deprecationReason: 'Use /v2/invoices instead.',
sunset: '2026-01-01T00:00:00+00:00',
)]
class Invoice {}
deprecationReason emits a Deprecation header and sunset a Sunset header. To
also advertise a migration doc, add an explicit operation links: entry —
new Link('deprecation', 'https://…') renders Link: <…>; rel="deprecation".
Nested PATCH gotcha
With application/merge-patch+json, to update an existing nested resource you
must include its identifier. Omitting it makes the serializer treat the nested
object as new (and attempt to create it):
{ "shippingAddress": { "id": 12, "city": "Lyon" } }
Without "id", API Platform tries to create a new Address rather than patch #12.
Laravel
Per-operation metadata (security, validationContext groups, collect_denormalization_errors,
deprecationReason/sunset, parameter validation/security) is mostly shared, but
auth and validation wiring differ:
- Authorization integrates with Laravel policies, not Symfony voters. Once a
policy exists, API Platform auto-maps operations to methods: GET collection →
viewAny, GET →view, POST →create, PATCH/PUT →update(PUT →createif absent), DELETE →delete. Override the mapping with apolicy:property on the operation:new Patch(policy: 'myCustomPolicy'). ThesecurityExpressionLanguage string also works. - Authentication / middleware is attached with the Laravel
middleware:property per operation (new Patch(middleware: 'auth:sanctum')) or globally underdefaults.middlewareinconfig/api-platform.php. - Validation uses Laravel
rules(array / closure /FormRequest) per resource or operation rather than SymfonyvalidationContextgroups — see the custom-validator Laravel section.AuthenticationException→ 401 andAuthorizationException→ 403 are mapped by default in the config'sexception_to_status. - Query/header
Parameterconstraintsare Laravel validation rule strings (e.g.'min:2'), not Symfony constraints.
Checklist
- Write operations restricted with
security/securityPostDenormalize - Collection isolation handled by an extension, not
security(see securing-collections) -
validationContextgroups split create vs update constraints -
collect_denormalization_errorsenabled where clients need full error lists - Query parameters carry validation constraints
- Deprecated endpoints set
deprecationReason+sunset
このスキルのファイル
1operations skillに関する質問
Claude Codeにoperations skillをインストールするには
npx degit api-platform/skillset/skills/operations ~/.claude/skills/operationsを実行してください。Claude Codeは~/.claude/skills(すべてのプロジェクト)または.claude/skills(単一プロジェクト)からskillsを読み込み、説明がタスクに一致する場合にskillを使用します。
operations skillはどのクライアントで使用できますか
Agent Skills形式を読み込むすべてのクライアントで使用できます: Claude Code、Codex、Gemini CLI、claude.ai(ZIPとしてアップロード)。skill内のスクリプトは、クライアントがコード実行を許可した場合にのみ実行されます。
エンドポイントを管理者のみに制限するにはどうすればよいですか?
is_granted('ROLE_ADMIN') のようなSymfony ExpressionLanguageの文字列を使用して、security属性を指定できます。
リソースの作成時と更新時で異なるバリデーションルールを適用するにはどうすればよいですか?
operationのvalidationContext内でバリデーショングループを使用することで、異なる制約セットを指定できます。
最初のエラーだけでなく、すべての不正なフィールドを一度に報告するにはどうすればよいですか?
validationContextで collect_denormalization_errors フラグを有効にすると、422レスポンスで違反リストの全内容を返します。