Getting Started

Configuration

Every key of config/rest.php, with its type, its default value and its effect.

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'),
    ],
],
KeyTypeDefaultEffect
authorizations.strictbooleanfalse (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.enabledbooleantrue (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.storenull or stringnull (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:

phpunit.xml
<env name="REST_AUTHORIZATIONS_STRICT" value="true"/>
There is no switch to turn the policies or the gates off. Override 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',
    ],
],
KeyTypeDefaultEffect
gates.keynon-empty stringgatesThe 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.namesmap of stringsthe six names aboveRenames 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',
],
KeyTypeDefaultEffect
morph.type_keynon-empty string@typeThe 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' => [
    'select_columns' => true,

    'more_key' => '@more',
],
KeyTypeDefaultEffect
search.select_columnsbooleantrueA 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_keynon-empty string@moreThe 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),
],
KeyTypeDefaultEffect
transactions.attemptsinteger of 1 or more1 (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.

KeyDefaultCovers
limits.sizes.filters100search.filters, each nested group, and the filters of includes and aggregates
limits.sizes.sorts20sorts
limits.sizes.selects100selects
limits.sizes.scopes10scopes
limits.sizes.aggregates20aggregates
limits.sizes.includes20includes
limits.sizes.instructions10instructions
limits.sizes.fields100The fields of an action and of each instruction
limits.sizes.values100The 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.mutate100The entries of mutate
limits.sizes.relation_entries100The entries of each to-many relation in mutate
limits.sizes.resources1000The keys of destroy, restore and forceDestroy. An action uses its $maxResources property instead.
limits.sizes.jobs100The 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.rows10000The 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.groups100The 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.

KeyDefaultCovers
limits.depths.filters1nested filter groups. 1 allows one group of filters. A text search run by a search engine never nests filters.
limits.depths.includes3Include levels, each relation of a dotted include counting as one level, and the relations of a dotted filter field or aggregate path
limits.depths.mutations3Relations nested in a mutate entry

Totals

Totals across the lists of one request.

KeyDefaultCovers
limits.totals.filters500The filters of one search, at every level and in its includes and aggregates
limits.totals.entries1000The entries of every list of sizes and of every gates list in one request, except resources
limits.totals.relations20The 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.patterns10The like, not like, ilike and not ilike filters of one search, at every level and in its includes and aggregates. At least 1.

Lengths

KeyDefaultCovers
limits.lengths.patterns255The characters of the value of a like, not like, ilike or not ilike filter, at every level
limits.lengths.values1000The 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.
A key under 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,
],
KeyTypeDefaultEffect
precognition.enabledbooleanfalseAdds 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.

KeysAccepted valuesMessage on another value
authorizations.strict, authorizations.cache.enabled, search.select_columns, precognition.enabledtrue, false, or the strings "1", "0", "true" and "false"The [rest.authorizations.strict] option must be a boolean.
each value of limitsan integer of 0 or more, or a string of digitsThe [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.valuesan integer of 1 or more, or a string of digitsThe [rest.limits.sizes.rows] option must be an integer of 1 or more.
transactions.attemptsan integer of 1 or more, or a string of digitsThe [rest.transactions.attempts] option must be an integer of 1 or more.
gates.key, morph.type_key, search.more_keya string that is not emptyThe [rest.gates.key] option must be a non-empty string.
authorizations.cache.storenull or a string that is not emptyThe [rest.authorizations.cache.store] option must be null or a non-empty string.
gates.namesa map of strings that holds the six built-in namesThe [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.