Read actions operate on an `Ash.Query`. Read actions always return lists of data. The act of [pagination](#pagination), or returning a [single result](#ash-get), is handled as part of the interface, and is not a concern of the action itself. Here is an example of a read action:
```elixir
# Giving your actions informative names is always a good idea
read :ticket_queue do
# Use arguments to take in values you need to run your read action.
# Arguments can be used in preparations and filters
filter expr(status == :open and priority in ^arg(:priorities))
end
```
## Ash.get!
The `Ash.get!` function is a convenience function for running a read action, filtering by a unique identifier, and expecting only a single result. It is equivalent to the following code:
```elixir
Ash.get!(Resource, 1)
# is roughly equivalent to
Resource
|> Ash.Query.filter(id == 1)
|> Ash.Query.limit(2)
|> Ash.read!()
|> case do
[] -> # raise not found error
[result] -> result
[_, _] -> # raise too many results error
end
```
## Ash.read_one!
The `Ash.read_one!` function is a similar convenience function to `Ash.get!`, but it does not take a unique identifier. It is useful when you expect an action to return only a single result, and want to enforce that and return a single result.
Keyset pagination is done via providing an `after` or `before` option, as well as a `limit`.
* The `limit` determines how many records should be returned in the query.
* The `after` or `before` value should be a `keyset` value that has been returned from a previous request. Keyset values are returned whenever there is any read action on a resource that supports keyset pagination, and they are stored in the `__metadata__` key of each record.
> #### Keysets are directly tied to the sorting applied to the query {: .warning}
>
> You can't change the sort applied to a request being paginated, and use the same keyset. If you want to change the sort, but *keep* the record who's keyset you are using in the `before` or `after` option, you must first request the individual record, with the new sort applied. Then, you can use the new keyset.
Add the `pagination` macro call to the [action](/documentation/topics/reference/glossary.md#action) of the [resource](/documentation/topics/reference/glossary.md#resource) that you want to be paginated.
For all available pagination options, see `d:Ash.Resource.Dsl.actions.read|pagination`.
> #### Check the updated query return type! {: .info}
> Pagination will modify the return type of calling the query action.
>
> Without pagination, Ash will return a list of records.
>
> But _with_ pagination, Ash will return an `Ash.Page.Offset` struct (for offset pagination) or `Ash.Page.Keyset` struct (for keyset pagination). Both structs contain the list of records in the `results` key of the struct.
These steps are trimmed down, and are aimed at helping users understand the general flow. Some steps are omitted.
- Run `Ash.Query.for_read/3` if it has not already been run
- [Apply tenant filters for attribute](/documentation/topics/multitenancy.md)
- Apply [pagination](#pagination) options
- Run before action hooks
- Multi-datalayer filter is synthesized. We run queries in other data layers to fetch ids and translate related filters to `(destination_field in ^ids)`
- Strict Check & Filter Authorization is run
- Data layer query is built and validated
- Field policies are added to the query
- Data layer query is Run
- Authorizer "runtime" checks are run (you likely do not have any of these)
The following steps happen while(asynchronously) or after the main data layer query has been run
- If paginating and count was requested, the count is determined at the same time as the query is run.
- Any calculations & aggregates that were able to be run outside of the main query are run
- Relationships, calculations, and aggregates are loaded