PHPackages                             dive-be/laravel-dry-requests - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Queues &amp; Workers](/categories/queues)
4. /
5. dive-be/laravel-dry-requests

Abandoned → [laravel/framework](/?search=laravel%2Fframework)ArchivedLibrary[Queues &amp; Workers](/categories/queues)

dive-be/laravel-dry-requests
============================

Dry run your Laravel requests

2.3.0(3y ago)203264.3k—3.4%4MITPHPPHP ^8.1

Since Apr 4Pushed 3y ago2 watchersCompare

[ Source](https://github.com/dive-be/laravel-dry-requests)[ Packagist](https://packagist.org/packages/dive-be/laravel-dry-requests)[ Docs](https://github.com/dive-be/laravel-dry-requests)[ RSS](/packages/dive-be-laravel-dry-requests/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (6)Dependencies (8)Versions (7)Used By (0)

> **Warning** two months after the release of our package, [Taylor Otwell announced an almost identical functionality](https://youtu.be/f4QShF42c6E?t=20679) as a core package. Since this package has pretty much been made obsolete, we have decided to stop maintaining it.
>
> So, please consider migrating to [Laravel Precognition](https://github.com/laravel/framework/pull/44339).

[![Social Card of Laravel Dry Requests](https://github.com/dive-be/laravel-dry-requests/raw/master/art/socialcard.png?raw=true)](https://github.com/dive-be/laravel-dry-requests/blob/master/art/socialcard.png?raw=true)

X-Dry-Run your requests
=======================

[](#x-dry-run-your-requests)

[![Latest Version on Packagist](https://camo.githubusercontent.com/600e620715a4f08b77142075be7d0e2b879b6cd27148eebef9a321bf0604a88f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f646976652d62652f6c61726176656c2d6472792d72657175657374732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dive-be/laravel-dry-requests)[![Total Downloads](https://camo.githubusercontent.com/93a782be184e30a8b9d7bfb52a4c20fde457b39d27ed7f79da37ffd5844cbfca/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f646976652d62652f6c61726176656c2d6472792d72657175657374732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/dive-be/laravel-dry-requests)

This package allows you to check if your requests would pass validation if you executed them normally. The Laravel equivalent of `--dry-run` in various CLI tools, or what some devs call "preflight requests".

🚀 Hit the endpoint as users are entering information on the form to provide real-time feedback with 100% accuracy.

🚀 Validate only a subset of data of a multi-step form to guarantee success when the form is eventually submitted.

Showcase
--------

[](#showcase)

[![LDR Demo](./art/demo.gif)](./art/demo.gif)

What problem does this package solve?
-------------------------------------

[](#what-problem-does-this-package-solve)

A traditional approach to validating user input in JavaScript applications (Inertia / SPA / Mobile) is using a library such as **yup**to do the heavy lifting and delegating complex business validations to the server.

However, the client-side can never be trusted, so you can't simply omit the validation rules that ran on the front-end. This means that validation has to live in 2 distinct places and you will have to keep them in sync. This is very tedious and wasteful, so this is where this package comes into play.

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require dive-be/laravel-dry-requests
```

You can publish the config file with:

```
php artisan vendor:publish --provider="Dive\DryRequests\ServiceProvider" --tag="config"
```

This is the contents of the published config file:

```
return [
    /*
    |--------------------------------------------------------------------------
    | Default Dry Validation Behavior
    |--------------------------------------------------------------------------
    |
    | All dry requests are validated against a subset of the defined rules.
    | In other words only present fields are checked during the request.
    | You may choose to halt validation as soon as a failure occurs,
    | or continue validating all fields and return all failures.
    |
    | Supported: "all", "first"
    |
    */

    'validation' => 'first',
];
```

Behavior
--------

[](#behavior)

💡 `Controller` logic is not executed after a successful validation attempt. `200 OK` is returned upon a successful dry run.

💡 **Only present fields** are validated to ensure good UX. Other fields are skipped using the `sometimes` rule. This means that *you* are responsible for only sending the relevant fields for validating e.g. a step of a multi-step wizard.

Usage
-----

[](#usage)

Assume the following endpoint: `POST /users` and `Controller`.

### Option 1 - using `FormRequest`s

[](#option-1---using-formrequests)

`Controller` injecting a `StoreUserRequest`:

```
class UserController
{
    public function store(StoreUserRequest $request): UserResource
    {
        $user = User::create($request->validated());

        return new UserResource($user);
    }
}
```

Add the `DryRunnable` trait to your `FormRequest`:

```
class StoreUserRequest extends FormRequest
{
    use DryRunnable;

    public function rules(): array
    {
        return [
            'email' => ['required', 'email', 'max:255', 'unique:users'],
            'username' => ['required', 'string', 'min:2', 'max:255', 'unique:users'],
            'nickname' => ['nullable', 'string', 'min:2', 'max:255'],
        ];
    }
}
```

That's it 😎.

### Option 2 - using `validate` method on the `Request` object

[](#option-2---using-validate-method-on-the-request-object)

```
class UserController
{
    public function store(Request $request): UserResource
    {
        $validated = $request->validate([
            'email' => ['required', 'email', 'max:255', 'unique:users'],
            'username' => ['required', 'string', 'min:2', 'max:255', 'unique:users'],
            'nickname' => ['nullable', 'string', 'min:2', 'max:255'],
        ]);

        $user = User::create($request->validated());

        return new UserResource($user);
    }
}
```

You don't have to do anything at all 🤙.

### Front-end execution

[](#front-end-execution)

Now, hit the endpoint from the client-side like you normally would. But with the added `X-Dry-Run` header.

```
// 1. "Username has already been taken" validation error
axios.post('/users', { username: 'Agent007' }, { headers: { 'X-Dry-Run': true } })
     .then(response => response.status); // 422 Unprocessable Entity

// 2. Successful unique username check: Controller did not execute
axios.post('/users', { username: 'Asil Kan' }, { headers: { 'X-Dry-Run': true } })
     .then(response => response.status); // 200 OK

// 3. Successful unique e-mail check: Controller did not execute
axios.post('/users', { email: 'muhammed@dive.be' }, { headers: { 'X-Dry-Run': true } })
     .then(response => response.status); // 200 OK

// 4. Submit entire form: Controller executed
axios.post('/users', { email: 'muhammed@dive.be', username: 'Asil Kan' })
     .then(response => response.status); // 201 Created
```

### Inertia.js example

[](#inertiajs-example)

```
const { clearErrors, data, errors, setData } = useForm({
    email: '',
    password: '',
    password_confirmation: '',
});

const pick = (obj, fields) => fields.reduce((acc, cur) => (acc[cur] = obj[cur], acc), {});

const validateAsync = (...fields) => () => {
    Inertia.post(route('register'), pick(data, fields) , {
        headers: { 'X-Dry-Run': 'all' },
        onError: setError,
        onSuccess() { clearErrors(...fields); },
    });
}

// Somewhere in your template

```

### Fine-tuning Dry Validations: `AllFailures` / `FirstFailure`

[](#fine-tuning-dry-validations-allfailures--firstfailure)

- The default validation behavior for dry requests is halting validation as soon as an error is found. This is especially useful when handling async validation for a **single field**.
- The other option is to keep validating even if an error is encountered. This is especially useful for multi-step forms.

You can alter this behavior on 3 distinct levels.

1. Change `first` to `all` (or vice versa) in the `dry-request` config file. This will apply to all of your requests.
2. **FormRequest only** - Use the `Dive\DryRequests\Dry` attribute along with `Dive\DryRequests\Validation` on the `rules` method to force a specific `Validation` behavior for a particular `FormRequest`.

```
#[Dry(Validation::AllFailures)]
public function rules(): array
{
    return [...];
}
```

3. Dictate the behavior on the fly from the front-end using the `X-Dry-Run` header. Valid values: `all`, `first`.

```
axios.post('/users', { email: '...', username: '...' }, { headers: { 'X-Dry-Run': 'all' } })
     .then(response => response.status); // 200 OK
```

*Note: the header value will be ignored if you have explicitly set a validation behavior on the `FormRequest` using the `Dry` attribute.*

### Conflicting `FormRequest` methods

[](#conflicting-formrequest-methods)

The package makes use of the available methods `passedValidation` and `withValidator` available on `FormRequest` classes to enable its behavior.

If you define these in your own requests, you must call the "dry" methods manually:

```
class CreateUserRequest extends FormRequest
{
    protected function passedValidation()
    {
        $this->stopWhenDry(); // must be called first

        // your custom logic
    }

    protected function withValidator(Validator $instance)
    {
        $instance = /* your custom logic */;

        $this->withDryValidator($instance); // must be called last
    }
}
```

Testing
-------

[](#testing)

```
composer test
```

Upgrading
---------

[](#upgrading)

Please see [UPGRADING](UPGRADING.md) for details.

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

Security
--------

[](#security)

If you discover any security related issues, please email  instead of using the issue tracker.

Credits
-------

[](#credits)

- [Muhammed Sari](https://github.com/mabdullahsari)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

40

—

FairBetter than 88% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity49

Moderate usage in the ecosystem

Community12

Small or concentrated contributor base

Maturity62

Established project with proven stability

 Bus Factor1

Top contributor holds 77.8% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~79 days

Recently: every ~92 days

Total

6

Last Release

1112d ago

Major Versions

1.1.0 → 2.0.02022-06-16

### Community

Maintainers

![](https://www.gravatar.com/avatar/a265853c8db7c699f64f523ee782dc5ec095bfb456379222d1b0b97d90e4c735?d=identicon)[dive](/maintainers/dive)

---

Top Contributors

[![mabdullahsari](https://avatars.githubusercontent.com/u/24608797?v=4)](https://github.com/mabdullahsari "mabdullahsari (7 commits)")[![dive-michiel](https://avatars.githubusercontent.com/u/2650975?v=4)](https://github.com/dive-michiel "dive-michiel (2 commits)")

---

Tags

asynclaravelvalidationdryrequestsdive

###  Code Quality

TestsPest

Code StylePHP CS Fixer

### Embed Badge

![Health badge](/badges/dive-be-laravel-dry-requests/health.svg)

```
[![Health](https://phpackages.com/badges/dive-be-laravel-dry-requests/health.svg)](https://phpackages.com/packages/dive-be-laravel-dry-requests)
```

###  Alternatives

[toin0u/geotools-laravel

Geo-related tools PHP library for Laravel 4 &amp; 5

250388.0k1](/packages/toin0u-geotools-laravel)[cerbero/lazy-json-pages

Framework-agnostic package to load items from any paginated JSON API into a Laravel lazy collection via async HTTP requests.

19697.4k](/packages/cerbero-lazy-json-pages)[mpbarlow/laravel-queue-debouncer

A wrapper job for debouncing other queue jobs.

63714.4k1](/packages/mpbarlow-laravel-queue-debouncer)[rcrowe/raven

Raven client for Sentry that supports background processing through multiple providers.

3467.0k](/packages/rcrowe-raven)[tochka-developers/queue-promises

Promises for Laravel queue jobs

1912.3k](/packages/tochka-developers-queue-promises)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
