# Operations
API Platform relies on the concept of operations. Operations can be applied to a resource exposed by the API. From
an implementation point of view, an operation is a link between a resource, a route and its related controller.

Watch the Operations screencast
API Platform automatically registers typical [CRUD](https://en.wikipedia.org/wiki/Create,_read,_update_and_delete) operations
and describes them in the exposed documentation (Hydra and Swagger). It also creates and registers routes corresponding
to these operations in the Symfony routing system (if it is available).
The behavior of built-in operations is briefly presented in the [Getting started](getting-started.md#mapping-the-entities)
guide.
The list of enabled operations can be configured on a per-resource basis. Creating custom operations on specific routes
is also possible.
There are two types of operations: collection operations and item operations.
Collection operations act on a collection of resources. By default two routes are implemented: `POST` and `GET`. Item
operations act on an individual resource. Four default routes are defined: `GET`, `PUT`, `DELETE` and `PATCH`. `PATCH` is supported
with [JSON Merge Patch (RFC 7396)](https://www.rfc-editor.org/rfc/rfc7386), or [using the JSON:API format](https://jsonapi.org/format/#crud-updating), as required by the specification.
When the `ApiPlatform\Metadata\ApiResource` annotation is applied to an entity class, the following built-in CRUD
operations are automatically enabled:
Collection operations:
Method | Mandatory | Description
-------|-----------|------------------------------------------
`GET` | yes | Retrieve the (paginated) list of elements
`POST` | no | Create a new element
Item operations:
Method | Mandatory | Description
---------|-----------|-------------------------------------------
`GET` | yes | Retrieve an element
`PUT` | no | Replace an element
`PATCH` | no | Apply a partial modification to an element
`DELETE` | no | Delete an element
Note: the `PATCH` method must be enabled explicitly in the configuration, refer to the [Content Negotiation](content-negotiation.md) section for more information.
Note: with JSON Merge Patch, the [null values will be skipped](https://symfony.com/doc/current/components/serializer.html#skipping-null-values) in the response.
Note: Current `PUT` implementation behaves more or less like the `PATCH` method.
Existing properties not included in the payload are **not** removed, their current values are preserved.
To remove an existing property, its value must be explicitly set to `null`.
Implementing [the standard `PUT` behavior](https://httpwg.org/specs/rfc7231.html#PUT) is on the roadmap, follow [issue #4344](https://github.com/api-platform/core/issues/4344) to track the progress.
## Enabling and Disabling Operations
If no operation is specified, all default CRUD operations are automatically registered. It is also possible - and recommended
for large projects - to define operations explicitly.
Keep in mind that once you explicitly set up an operation, the automatically registered CRUD will no longer be.
If you declare even one operation manually, such as `#[GET]`, you must declare the others manually as well if you need them.
Operations can be configured using attributes, XML or YAML. In the following examples, we enable only the built-in operation
for the `GET` method for both `collection` and `item` to create a readonly endpoint.
If the operation's name matches a supported HTTP method (`GET`, `POST`, `PUT`, `PATCH` or `DELETE`), the corresponding `method` property
will be automatically added.
Note: The `#[GetCollection]` attribute is an alias for `#[Get(collection: true)]`
```php
```
The previous example can also be written with an explicit method definition:
```php
```
API Platform is smart enough to automatically register the applicable Symfony route referencing a built-in CRUD action
just by specifying the method name as key, or by checking the explicitly configured HTTP method.
If you do not want to allow access to the resource item (i.e. you don't want a `GET` item operation), instead of omitting it altogether, you should instead declare a `GET` item operation which returns HTTP 404 (Not Found), so that the resource item can still be identified by an IRI. For example:
```php
```
## Configuring Operations
The URL, the method and the default status code (among other options) can be configured per operation.
In the next example, both `GET` and `POST` operations are registered with custom URLs. Those will override the URLs generated by default.
In addition to that, we require the `id` parameter in the URL of the `GET` operation to be an integer, and we configure the status code generated after successful `POST` request to be `301`:
```php
'\d+'],
defaults: ['color' => 'brown'],
options: ['my_option' => 'my_option_value'],
schemes: ['https'],
host: '{subdomain}.api-platform.com'
),
new Post(
uriTemplate: '/grimoire',
status: 301
)
])]
class Book
{
//...
}
```
```yaml
# api/config/api_platform/resources.yaml
resources:
App\Entity\Book:
operations:
ApiPlatform\Metadata\Post:
uriTemplate: '/grimoire'
status: 301
ApiPlatform\Metadata\Get:
uriTemplate: '/grimoire/{id}'
requirements:
id: '\d+'
defaults:
color: 'brown'
host: '{subdomain}.api-platform.com'
schemes: ['https']
options:
my_option: 'my_option_value'
```
```xml
\d+
brown
https
brown
```
## Prefixing All Routes of All Operations
Sometimes it's also useful to put a whole resource into its own "namespace" regarding the URI. Let's say you want to
put everything that's related to a `Book` into the `library` so that URIs become `library/book/{id}`. In that case
you don't need to override all the operations to set the path but configure the `routePrefix` attribute for the whole entity instead:
```php
```
## Defining Which Operation to Use to Generate the IRI
Using multiple operations on your resource, you may want to specify which operation to use to generate the IRI, instead
of letting API Platform use the first one it finds.
Let's say you have 2 resources in relationship: `Company` and `User`, where a company has multiple users. You can declare
the following routes:
- `/users`
- `/users/{id}`
- `/companies/{companyId}/users`
- `/companies/{companyId}/users/{id}`
The first routes (`/users...`) are only accessible by the admin, and the others by regular users. Calling
`/companies/{companyId}/users` should return IRIs matching `/companies/{companyId}/users/{id}` to not expose an admin
route to regular users.
To do so, use the `itemUriTemplate` option only available on `GetCollection` and `Post` operations:
```php
```
API Platform will find the operation matching this `itemUriTemplate` and use it to generate the IRI.
If this option is not set, the first `Get` operation is used to generate the IRI.
## Expose a Model Without Any Routes
Sometimes, you may want to expose a model, but want it to be used through subrequests only, and never through item or collection operations.
Because the OpenAPI standard requires at least one route to be exposed to make your models consumable, let's see how you can manage this kind
of issue.
Let's say you have the following entities in your project:
```php
decorated = $decorated;
}
public function __invoke(array $context = []): OpenApi
{
$openApi = $this->decorated->__invoke($context);
$paths = $openApi->getPaths()->getPaths();
$filteredPaths = new Model\Paths();
foreach ($paths as $path => $pathItem) {
// If a prefix is configured on API Platform's routes, it must appear here.
if ($path === '/weathers/{id}') {
continue;
}
$filteredPaths->addPath($path, $pathItem);
}
return $openApi->withPaths($filteredPaths);
}
}
```
That's it: your route is gone!