Skill operations
Configura el ciclo de vida de las operaciones de la API, expresiones de seguridad, grupos de validación y restricciones a nivel de parámetro para endpoints RESTful.
- Estrellas en GitHub
- 29
- Archivos
- 1
- Último commit
- 22 oct 2026
- Licencia
- —
- Herramientas permitidas
- —
¿Qué hace la skill operations?
operations es una agent skill. Configura el ciclo de vida de las operaciones de la API, expresiones de seguridad, grupos de validación y restricciones a nivel de parámetro para endpoints RESTful.
Resumen generado a partir del SKILL.md de la skill.
Sobre la skill operations
Configurar los ciclos de vida, la seguridad y la validación de las operaciones de la API para endpoints RESTful es lo que este SKILL permite a los desarrolladores que utilizan API Platform. Permite un control detallado sobre las fases de lectura y escritura de una solicitud, permitiendo a los usuarios alternar entre providers y processors para manejar tareas como descargas de archivos o lógica de upsert personalizada. Los desarrolladores pueden usarlo para gestionar la división CQRS estableciendo explícitamente flags de lectura y escritura para diferentes métodos HTTP.
Más allá de la gestión del ciclo de vida, el SKILL proporciona herramientas para asegurar endpoints utilizando Symfony ExpressionLanguage para el control de acceso basado en roles u objetos. También gestiona la seguridad a nivel de parámetro para parámetros de consulta y de cabecera. Para la integridad de los datos, admite grupos de validación para diferenciar entre las restricciones de creación y actualización, así como la recopilación de errores para fallos de desnormalización. También gestiona la depreciación de endpoints mediante cabeceras específicas y proporciona orientación para manejar solicitudes PATCH anidadas en escenarios de JSON merge-patch.
Lo que un agente puede hacer con ella
- 1Restringir el acceso al endpoint por rolUn agente de IA puede configurar una operación POST para que solo los usuarios con privilegios administrativos puedan ejecutarla.
- 2Aplicar diferentes reglas de validaciónUn desarrollador puede instruir al agente para que utilice grupos de validación específicos para la creación frente a la actualización de un recurso de usuario.
- 3Asegurar parámetros de consulta específicosPuedes solicitar que un parámetro de consulta específico se valide contra una expresión de seguridad antes de que se ejecute el provider.
- 4Comunicar la depreciación de la APIUn agente puede añadir motivos de depreciación y fechas de retiro a un recurso existente para notificar a los clientes sobre los próximos cambios.
- 5Habilitar reportes de errores detalladosUn desarrollador puede configurar la API para que devuelva todos los errores de campos malformados a la vez en lugar de detenerse en el primer error.
Escrito a partir del SKILL.md de la skill. Cada caso de uso se verifica con una cita del mismo.
Cómo instalar la skill operations
Cada comando copia la carpeta de la skill al directorio de skills del cliente. Reinicia el cliente o inicia una nueva sesión para cargarla.Instalar la skill operations en Plugin
/plugin marketplace add api-platform/skillset /plugin install api-platform@api-platform-skillset
Instalar la skill operations en Claude Code
~/.claude/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.claude/skills/operationsInstalar la skill operations en Claude Code (project)
.claude/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations .claude/skills/operationsInstalar la skill operations en Codex
~/.agents/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.agents/skills/operationsInstalar la skill operations en Gemini CLI
~/.gemini/skills/operations/SKILL.md
$ npx degit api-platform/skillset/skills/operations ~/.gemini/skills/operationsInstalar la skill operations en Claude.ai
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
Ver en GitHubOperations: 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
Archivos de esta skill
1Preguntas sobre la skill operations
¿Cómo instalo la skill operations en Claude Code?
Ejecuta npx degit api-platform/skillset/skills/operations ~/.claude/skills/operations. Claude Code carga las skills desde ~/.claude/skills (todos los proyectos) o .claude/skills (un proyecto) y usa una skill cuando su descripción coincide con la tarea.
¿Qué clientes pueden usar la skill operations?
Cualquier cliente que lea el formato Agent Skills: Claude Code, Codex, Gemini CLI y claude.ai (subir como ZIP). Los scripts de la skill se ejecutan solo donde el cliente permite la ejecución de código.
¿Cómo puedo restringir un endpoint solo a administradores?
Puedes usar el atributo security con una cadena de Symfony ExpressionLanguage, como is_granted('ROLE_ADMIN').
¿Cómo aplico diferentes reglas de validación para crear frente a actualizar un recurso?
Puedes usar grupos de validación dentro del validationContext de una operación para especificar diferentes conjuntos de restricciones.
¿Cómo puedo reportar todos los campos malformados a la vez en lugar de solo el primero?
Activa el flag collect_denormalization_errors en el validationContext para devolver una lista completa de violaciones en una respuesta 422.