Configuration
The configuration file
Publish config/rest.php to change a default:
php artisan vendor:publish --tag=rest-config
The file has seven blocks, in this order: authorizations, gates, morph, search, transactions, limits and precognition.
The package merges its defaults into your file at every level. A nested key that your file leaves out, such as gates.names.authorized_to_restore or limits.sizes.filters, keeps its default value. You can keep only the keys you change.
Authorizations
'authorizations' => [
'strict' => env('REST_AUTHORIZATIONS_STRICT', false),
'cache' => [
'enabled' => env('REST_AUTHORIZATIONS_CACHE_ENABLED', true),
'store' => env('REST_AUTHORIZATIONS_CACHE_STORE'),
],
],
| Key | Type | Default | Effect |
|---|---|---|---|
authorizations.strict | boolean | false (REST_AUTHORIZATIONS_STRICT) | Throws Lomkit\Rest\Exceptions\UnplannedCheckException when a query makes a policy check that the request did not make before validation. Turn it on in your test suite. |
authorizations.cache.enabled | boolean | true (REST_AUTHORIZATIONS_CACHE_ENABLED) | Keeps between requests the decisions that a resource declares in authorizationCache(), and the results of the metrics that declare cacheFor(). With false, nothing is kept. |
authorizations.cache.store | null or string | null (REST_AUTHORIZATIONS_CACHE_STORE) | The cache store of the kept decisions and metric results. null uses the default store. The name must match a store of config/cache.php. |
A resource keeps no decision until it declares one in authorizationCache(), so the cache does nothing in an application that declares none. See Decision cache.
To turn strict mode on in your tests, add the variable to phpunit.xml:
<env name="REST_AUTHORIZATIONS_STRICT" value="true"/>
isAuthorizingEnabled() or isGatingEnabled() on a resource, or on your base resource for every resource, and return false. See Policies.Gates
'gates' => [
'key' => 'gates',
'names' => [
'authorized_to_view' => 'authorized_to_view',
'authorized_to_create' => 'authorized_to_create',
'authorized_to_update' => 'authorized_to_update',
'authorized_to_delete' => 'authorized_to_delete',
'authorized_to_restore' => 'authorized_to_restore',
'authorized_to_force_delete' => 'authorized_to_force_delete',
],
],
| Key | Type | Default | Effect |
|---|---|---|---|
gates.key | non-empty string | gates | The key of each row that holds the answers to the gates of a search. The create answer goes under this key in the meta of the page. A resource cannot declare a field with this name. |
gates.names | map of strings | the six names above | Renames the answers. Each key is the default name of an answer, authorized_to_ and the ability in snake case. The six built-in names are required. |
Add a key to gates.names to rename a gate that a resource declares in gates(), by its default name: 'authorized_to_download' => 'can_download'. Each answer holds allowed and message. See Gates.
Morph
'morph' => [
'type_key' => '@type',
],
| Key | Type | Default | Effect |
|---|---|---|---|
morph.type_key | non-empty string | @type | The key that holds the morph type of each row that a MorphTo include returns, before its other keys. A resource that a MorphTo relation can load cannot declare a field with this name. |
See Relations.
Search
'search' => [
'select_columns' => true,
'more_key' => '@more',
],
| Key | Type | Default | Effect |
|---|---|---|---|
search.select_columns | boolean | true | A search loads only the columns that its rows need: the returned fields, the keys of its includes, the columns of alwaysSelect() and of each field's requires(). With false, every query loads every column. |
search.more_key | non-empty string | @more | The key of each row that lists the to-many includes holding more rows than they returned. A resource cannot declare a field with this name. |
When a policy, an accessor or the map() of a custom response reads a column that the search did not load, the search throws Lomkit\Rest\Exceptions\MissingColumnException. Add the column to the resource's alwaysSelect() or to the field's requires(), or set select_columns to false. See Fields.
Transactions
'transactions' => [
'attempts' => env('REST_TRANSACTION_ATTEMPTS', 1),
],
| Key | Type | Default | Effect |
|---|---|---|---|
transactions.attempts | integer of 1 or more | 1 (REST_TRANSACTION_ATTEMPTS) | How many times a transaction of the package runs when the database reports a deadlock or a lock timeout. 1 runs it once. |
Each write runs in a transaction: a mutation, a destroy, a restore, a forceDestroy, and each chunk or whole run of an action. Each attempt runs the whole write again, with its checks, its hooks, its events and the handle() method of the action. A transaction inside a transaction of your application is not run again. See Mutate.
Limits
The limits block bounds the size of a request and of what it makes the package do. A request over a limit gets a 422 on the list or the value that goes over it. A limit of 0 refuses every entry, or the first level of a depth. See Limits for the messages and the rules of each key.
'limits' => [
'sizes' => [
'filters' => 100,
'sorts' => 20,
'selects' => 100,
'scopes' => 10,
'aggregates' => 20,
'includes' => 20,
'instructions' => 10,
'fields' => 100,
'values' => 100,
'mutate' => 100,
'relation_entries' => 100,
'resources' => 1000,
'jobs' => 100,
'rows' => 10000,
'groups' => 100,
],
'depths' => [
'filters' => 1,
'includes' => 3,
'mutations' => 3,
],
'totals' => [
'filters' => 500,
'entries' => 1000,
'relations' => 20,
'patterns' => 10,
],
'lengths' => [
'patterns' => 255,
'values' => 1000,
],
],
Each value is an integer of 0 or more. sizes.jobs, sizes.rows, sizes.groups, totals.relations, totals.patterns and lengths.values must be at least 1.
Sizes
The most entries of each list, at every level of a request, then three bounds on the work of one request: the jobs of a queued action, the rows of a response, and the groups of a metric.
| Key | Default | Covers |
|---|---|---|
limits.sizes.filters | 100 | search.filters, each nested group, and the filters of includes and aggregates |
limits.sizes.sorts | 20 | sorts |
limits.sizes.selects | 100 | selects |
limits.sizes.scopes | 10 | scopes |
limits.sizes.aggregates | 20 | aggregates |
limits.sizes.includes | 20 | includes |
limits.sizes.instructions | 10 | instructions |
limits.sizes.fields | 100 | The fields of an action and of each instruction |
limits.sizes.values | 100 | The values of an in, not in, between or not between filter, and the key list of an entry of a to-many relation in mutate |
limits.sizes.mutate | 100 | The entries of mutate |
limits.sizes.relation_entries | 100 | The entries of each to-many relation in mutate |
limits.sizes.resources | 1000 | The keys of destroy, restore and forceDestroy. An action uses its $maxResources property instead. |
limits.sizes.jobs | 100 | The jobs that one run of a queued or batched action creates. When its rows need more jobs, each job takes the number of rows divided by this limit, rounded up, instead of $chunkCount. At least 1. |
limits.sizes.rows | 10000 | The rows that one search page or the response block of a mutation can render, included rows counted. It is counted from the limits that the request asks for, before any query. At least 1. |
limits.sizes.groups | 100 | The parts of a partition metric and the splits of a trend that sets no limit(). The other groups fold into other. At least 1. |
Depths
How many levels nest.
| Key | Default | Covers |
|---|---|---|
limits.depths.filters | 1 | nested filter groups. 1 allows one group of filters. A text search run by a search engine never nests filters. |
limits.depths.includes | 3 | Include levels, each relation of a dotted include counting as one level, and the relations of a dotted filter field or aggregate path |
limits.depths.mutations | 3 | Relations nested in a mutate entry |
Totals
Totals across the lists of one request.
| Key | Default | Covers |
|---|---|---|
limits.totals.filters | 500 | The filters of one search, at every level and in its includes and aggregates |
limits.totals.entries | 1000 | The entries of every list of sizes and of every gates list in one request, except resources |
limits.totals.relations | 20 | The relation filters of one search: each filter on a dotted field path and each relation group, at every level and in its includes and aggregates. Each one runs a subquery. At least 1. |
limits.totals.patterns | 10 | The like, not like, ilike and not ilike filters of one search, at every level and in its includes and aggregates. At least 1. |
Lengths
| Key | Default | Covers |
|---|---|---|
limits.lengths.patterns | 255 | The characters of the value of a like, not like, ilike or not ilike filter, at every level |
limits.lengths.values | 1000 | The characters of each string value of a filter, and of each string of a list, at every level. A pattern takes the smaller of the two lengths. At least 1. |
limits that the package does not know throws an exception that names it, so that a typo does not keep the default without notice: The [rest.limits.sizes.filter] option does not exist. An unknown key elsewhere is ignored.These limits are not the page sizes of a resource, which its limits() method returns. See Limits.
Precognition
'precognition' => [
'enabled' => false,
],
| Key | Type | Default | Effect |
|---|---|---|---|
precognition.enabled | boolean | false | Adds Laravel's HandlePrecognitiveRequests middleware to every route of the package. A request with the Precognition header runs its middleware, the policy checks made before validation and its validation, then stops before the controller. |
The middleware is added when the routes are registered: clear the route cache after you change this option. See Precognition.
Typed values
Each request reads the values once, and checks their type. A value of the wrong type throws Lomkit\Rest\Exceptions\InvalidConfigException, an InvalidArgumentException, that names the key.
| Keys | Accepted values | Message on another value |
|---|---|---|
authorizations.strict, authorizations.cache.enabled, search.select_columns, precognition.enabled | true, false, or the strings "1", "0", "true" and "false" | The [rest.authorizations.strict] option must be a boolean. |
each value of limits | an integer of 0 or more, or a string of digits | The [rest.limits.sizes.filters] option must be an integer of 0 or more. |
limits.sizes.jobs, limits.sizes.rows, limits.sizes.groups, limits.totals.relations, limits.totals.patterns, limits.lengths.values | an integer of 1 or more, or a string of digits | The [rest.limits.sizes.rows] option must be an integer of 1 or more. |
transactions.attempts | an integer of 1 or more, or a string of digits | The [rest.transactions.attempts] option must be an integer of 1 or more. |
gates.key, morph.type_key, search.more_key | a string that is not empty | The [rest.gates.key] option must be a non-empty string. |
authorizations.cache.store | null or a string that is not empty | The [rest.authorizations.cache.store] option must be null or a non-empty string. |
gates.names | a map of strings that holds the six built-in names | The [rest.gates.names] option must be a map of strings., or The [rest.gates.names.authorized_to_view] option must be a string. for a missing name |
The strings let env() values work as they are. null and an empty string are refused: a variable set without a value, such as REST_AUTHORIZATIONS_STRICT=, throws.
precognition.enabled is read when the routes are registered, limits when a request first reads its limits, and the other values together, the first time a request needs one of them.