> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apivalk.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Sorting

> Apivalk provides built-in support for route-level sorting, allowing clients to specify how the results should be sorted.

<Tip>For a walkthrough with client-side examples and resource integration, see the [Add sorting](/how-to/sorting) how-to.</Tip>

## Route Configuration

To enable sorting for a specific route, you use the `sorting()` method on the `Route` object. This defines which fields can be used for sorting in the request.

```php theme={null}
use apivalk\apivalk\Router\Route\Sort\Sort;
use apivalk\apivalk\Router\Route\Route;

public static function getRoute(): Route
{
    return Route::get('/pets')
        ->sorting([
            new Sort('id'),
            new Sort('name'),
            new Sort('created_at'),
        ]);
}
```

By default, an `Sort` object just defines that a field is "sortable".

***

## Usage in Controller

When a route has sorting enabled, you can access the resolved sortings from the request via the `sorting()` method, which returns a `SortBag`.

The bag is **always populated** for declared fields. If the client omits `order_by`, the bag holds the route's defaults; if the client sends `order_by`, the requested sorts come first in iteration order, followed by any remaining defaults as tiebreakers.

```php theme={null}
public function __invoke(ApivalkRequestInterface $request): AbstractApivalkResponse
{
    $sorting = $request->sorting();

    // Iterate over all sortings (user-requested first, then route defaults).
    // Perfect for building an ORDER BY: the user's primary intent comes first.
    foreach ($sorting as $field => $sort) {
        $direction = $sort->isAsc() ? 'ASC' : 'DESC';
        // apply to your query...
    }

    // Or read a specific field directly via the magic getter.
    $direction = $sorting->name->isAsc() ? 'ASC' : 'DESC';

    // ...
}
```

### Did the user pick this sort, or is it a default?

Each `Sort` knows whether it came from the user (`?order_by=…`) or from the route declaration. Use `isRequested()` when behavior should depend on user intent:

```php theme={null}
if ($sorting->status->isRequested()) {
    // The user explicitly asked to sort by status.
}
```

`SortBag::getRequested()` returns only the user-submitted sorts in the order they were sent — useful when you need user intent without route defaults mixed in:

```php theme={null}
foreach ($sorting->getRequested() as $sort) {
    // Only user-submitted sorts. Empty if the client did not send order_by.
}
```

<Note>
  `has($field)` only tells you whether the field is in the bag at all (which, for declared fields, is always `true`). To check whether the **user** requested a field, use `isRequested()` on the corresponding `Sort`.
</Note>

***

## Client-Side Usage

Apivalk supports sorting using the `order_by` query parameter. Clients can specify multiple fields and their sort direction using `+` (ascending, default) or `-` (descending) prefixes, separated by commas.

**Example: sort by name ascending and then by id descending**
`GET /pets?order_by=name,-id`

## Validation

`RequestValidationMiddleware` rejects any sort field that isn't declared in the route's `sorting([...])` list. A request like `GET /pets?order_by=hacked_field` returns `422 Unprocessable Entity` with a validation error on the `order_by` parameter. Routes without any declared sortings skip this check entirely.

***

## OpenAPI Documentation

When you configure sortings on a route, Apivalk automatically generates the corresponding OpenAPI documentation:

* An `order_by` query parameter of type `string`.
* The documentation describes the supported fields and the usage of `+`/`-` prefixes.
