How to Validate Your Laravel App's Config

Published on by

How to Validate Your Laravel App's Config image

As your Laravel projects change and grow, it's likely that you'll add new config fields and onboard new team members to work alongside you. Especially for junior developers, setting up config and environment variables on your local development machine for the first time for each project can sometimes (but not always) be quite confusing.

You can sometimes find yourself scratching your head thinking "have I put the right API key here?", or "is this the right URL for this environment variable?", and "what are the different values that this config field can be?".

Of course, these types of issues can be avoided with well written and up-to-date documentation. But, wouldn't it be better if these questions could be answered, and enforced, programmatically before running your app rather than by eye or waiting until your code fails during runtime?

What is Laravel Config Validator?

Laravel Config Validator is a package that I built with the aim of trying to solve these issues by providing the ability for developers to define rules that can be used to validate config fields. It allows you to validate your config fields using an Artisan command so that you and other developers can be sure that they have the correct config set up.

The main benefit of using this package is that it encourages code-as-documentation, because you can update your config validation rules each time your config changes rather than updating a boring markdown file with your project's documentation that's likely hidden away and never looked at.

It also allows you to write complex rules relatively easily. For example, let's say that you had a config field that stored your Mailgun API keys. You could write a simple rule to make sure that the key is present and is a string. But, you could take it one step further and write a rule that makes a HTTP request to the API to confirm that the keys are actually valid and can be used to make the requests in your application.

To get a better idea of how this might look in your codebase, here's a basic example of how we could validate that a services.mailchimp.api_key field could be validated:

use AshAllenDesign\ConfigValidator\Services\Rule;
use App\Rules\ValidMailchimpKey;
 
return [
Rule::make('mailchimp.api_key')
->rules(['required', 'string', new ValidMailchimpKey()]),
];

After defining a rule like above, we could then run the package's provided command (php artisan config:validate). If the services.mailchimp.api_key field is valid, the validation will pass. Otherwise, the command will return a list of the errors so that we can spot the incorrect config value, similar to in this example screenshot below:

Laravel config validator failure output

The best part about the package (in my opinion) is that you don't need to learn any new syntax! Under the hood, the package uses the Validator provided by Laravel. So, you can use the same rules that you would typically use in your request validation.

Now, let's take a look at how you can install the package and start using it in your own projects.

Installing the Package

To get started with using the package, you'll need to make sure that your Laravel project is using at least Laravel 8.0 and PHP 8.0.

You can install the package via Composer using the following command:

composer require ashallendesign/laravel-config-validator

The package ships with some handy rulesets that you can use to validate some of the default Laravel config fields. These are completely optional, but they can provide a good starting point for validating your app's config. If you want to use the default rulesets, you can publish them using the following command:

php artisan vendor:publish --tag=config-validator-defaults

The above command will copy the validation files and place them in a config-validation folder in your project's root. These rules are just to get you started, so there will likely be some rules that aren't needed for your project. So, once you've published them, feel free to delete them or edit them as much as you'd like.

Defining Config Validation Rulesets

Now that we have the package installed, we can start creating our own rulesets.

Creating new rulesets is really simple using the package's provided command. For example, if we wanted to create a ruleset for validating the config in the config/app.php file, we could run the following command:

php artisan make:config-validation app

Running the above command would create a file in config-validation/app.php ready for you to start adding your config validation.

It's important to remember that your config validation files' names need to match the config file that you want to validate. For example, if you want to validate your config/mail.php file, your validation file would need to be stored as config-validation/mail.php.

Adding Rules to a Ruleset

Once you have your ruleset file created in the config-validation directory, we can start adding our validation rules.

Under the hood, the package uses the built-in Validator class that ships with Laravel and makes use of all the existing validation rules available in Laravel, so working with it should seem pretty familiar.

Let's take a basic example and create a new validation rule for the driver field in the app/mail.php file. We'll define a rule that says the field must be one of the following: smtp, sendmail, mailgun, ses, or postmark. To do this, we could write the following rule:

use AshAllenDesign\ConfigValidator\Services\Rule;
 
return [
 
// ...
 
Rule::make('driver')->rules(['in:smtp,sendmail,mailgun,ses,postmark']),
 
// ...
 
];

As you can see, it's really simply to define a rule!

Custom Validation Error Messages

There may be times when you want to override the default error message for a specific validation rule. This is simple to do using the messages method on the Rule object when defining your rulesets.

For example, if we wanted to override the error message for our mail.driver config validation rule that we've created, we could do it like so:

use AshAllenDesign\ConfigValidator\Services\Rule;
 
return [
 
// ...
 
Rule::make('driver')
->rules(['in:smtp,sendmail,mailgun,ses,postmark'])
->messages(['in' => 'Our custom error message here']),
 
// ...
 
];

Only Running in Specific App Environments

It's possible that you might want to create rules that should only ever be run in specific environments. For example, you might want to create a relaxed set of validation rules for your local development environment and have a stricter set of rules for production.

To specify the environment that a rule can be run in, you can use the environments method available on the Rule object. If no environment is defined, the rule will be run in all environments by default.

For example, let's imagine that when we're working on our project locally, we don't really care which mail driver we're using. But, when in production, we want to make sure that we are only using mailgun. So, we could create a relaxed rule to run locally and a stricter rule to run in production like so:

use AshAllenDesign\ConfigValidator\Services\Rule;
 
return [
Rule::make('driver')
->rules(['in:smtp,sendmail,mailgun,ses,postmark'])
->environments([Rule::ENV_LOCAL]),
 
Rule::make('driver')
->rules(['in:mailgun'])
->environments([Rule::ENV_PRODUCTION]),
];

Running the Config Validation

Now that we've learned how to define our own config validation rulesets, let's take a look at how to actually run the validation.

Using the Command

The most common (and easiest) way that you can run your config validation is using the Artisan command that ships with the package.

You can use it by running the following command:

php artisan config:validate

However, you might not always want to validate all the config values in your application. So, the command allows you to specify the exact config files that you want to validate. To do this, you can use the --files option.

For example, if we only wanted to validate the config/auth.php file, we could run the following command:

php artisan config:validate --files=auth

Likewise, if wanted to explicitly define more than one file to validate at once, we could pass multiple names to the --files option separated by commas. For example, to validate the config/auth.php and config/services.php files, we could run the following command:

php artisan config:validate --files=auth,services

Running the Validation Manually

There may be times when you'd prefer to run the validation command manually in your code, rather than using the provided command. To do this, you can simply call the run method on the AshAllenDesign\ConfigValidator\Services\ConfigValidator class like so:

use AshAllenDesign\ConfigValidator\Services\ConfigValidator;
 
(new ConfigValidator())->run();

By default, the run method will return true if all the validation checks pass. If any of the checks fail, an AshAllenDesign\ConfigValidator\Exceptions\InvalidConfigValueException will be thrown.

Similar to how we can define the specific validation rulesets that should be run using the command, we can also do the same with the ConfigValidator object. To do this, we can pass an array of config file names to the run method.

For example, if we wanted to only run the validation for the config/auth.php file, we could use the following code:

use AshAllenDesign\ConfigValidator\Services\ConfigValidator;
 
(new ConfigValidator())->run(['auth']);

Running in a Service Provider

A good example of where you might want to manually run your config validation could be in a service provider. This is an ideal place to run the config validation rules in your local environment when developing to make sure that you have all the valid values. This is particularly useful if you are jumping between Git branches often that include different fields with different required config fields.

Here's how we could automatically run the config validation on each request in our local environment, and ignore it when running in production or testing:

namespace App\Providers;
 
use AshAllenDesign\ConfigValidator\Services\ConfigValidator;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
 
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
if (App::environment() === 'local') {
(new ConfigValidator())->run();
}
}
}

Throwing and Preventing Exceptions

As we mentioned above, by default, the ConfigValidator class will throw an AshAllenDesign\ConfigValidator\Exceptions\InvalidConfigValueException exception if the validation fails. The exception will contain the error message of the first config value that failed the validation.

However, there may be times when you don't want to throw an exception on the first failure and would rather run all the rules at once. To do this, you can prevent the exception from being thrown and instead rely on the boolean return value of the run method by using the throwExceptionOnFailure method. If we disable exceptions from being thrown, the run method will return false if any of the validation checks fail.

By preventing any exceptions from being thrown, we can then get the failed validation errors using the errors method, which will return them as an array.

The example belows shows how you could prevent any exceptions from being thrown so that you can grab the errors:

$configValidator = new ConfigValidator();
 
$errors = $configValidator->throwExceptionOnFailure(false)
->run()
->errors();

This is actually how the package's command runs the validator so that it can display all the failures in the output at once.

Conclusion

Hopefully, this post should have given you an overview of how you can use the Laravel Config Validator package in your Laravel apps to validate your app's config values. If you're interested in checking out the code for the package, you can view it in the GitHub repo.

If you enjoyed reading this post, I'd love to hear about it. Likewise, if you have any feedback to improve the future ones, I'd also love to hear that too.

Keep on building awesome stuff! 🚀

Ashley Allen photo

I am a freelance Laravel web developer who loves contributing to open-source projects, building exciting systems, and helping others learn about web development.

Cube

Laravel Newsletter

Join 40k+ other developers and never miss out on new tips, tutorials, and more.

Laravel Forge logo

Laravel Forge

Easily create and manage your servers and deploy your Laravel applications in seconds.

Laravel Forge
Tinkerwell logo

Tinkerwell

The must-have code runner for Laravel developers. Tinker with AI, autocompletion and instant feedback on local and production environments.

Tinkerwell
No Compromises logo

No Compromises

Joel and Aaron, the two seasoned devs from the No Compromises podcast, are now available to hire for your Laravel project. ⬧ Flat rate of $7500/mo. ⬧ No lengthy sales process. ⬧ No contracts. ⬧ 100% money back guarantee.

No Compromises
Laravel Idea for PhpStorm logo

Laravel Idea for PhpStorm

Ultimate PhpStorm plugin for Laravel developers, delivering lightning-fast code completion, intelligent navigation, and powerful generation tools to supercharge productivity.

Laravel Idea for PhpStorm
Kirschbaum logo

Kirschbaum

Providing innovation and stability to ensure your web application succeeds.

Kirschbaum
Shift logo

Shift

Running an old Laravel version? Instant, automated Laravel upgrades and code modernization to keep your applications fresh.

Shift
Bacancy logo

Bacancy

Supercharge your project with a seasoned Laravel developer with 4-6 years of experience for just $2500/month. Get 160 hours of dedicated expertise & a risk-free 15-day trial. Schedule a call now!

Bacancy
Lucky Media logo

Lucky Media

Get Lucky Now - the ideal choice for Laravel Development, with over a decade of experience!

Lucky Media
Lunar: Laravel E-Commerce logo

Lunar: Laravel E-Commerce

E-Commerce for Laravel. An open-source package that brings the power of modern headless e-commerce functionality to Laravel.

Lunar: Laravel E-Commerce
LaraJobs logo

LaraJobs

The official Laravel job board

LaraJobs
SaaSykit: Laravel SaaS Starter Kit logo

SaaSykit: Laravel SaaS Starter Kit

SaaSykit is a Multi-tenant Laravel SaaS Starter Kit that comes with all features required to run a modern SaaS. Payments, Beautiful Checkout, Admin Panel, User dashboard, Auth, Ready Components, Stats, Blog, Docs and more.

SaaSykit: Laravel SaaS Starter Kit
Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate logo

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate

Build your SaaS application in hours. Out-of-the-box multi-tenancy and seamless Stripe integration. Supports subscriptions and one-time purchases, allowing you to focus on building and creating without repetitive setup tasks.

Supercharge Your SaaS Development with FilamentFlow: The Ultimate Laravel Filament Boilerplate
JetShip - Laravel Starter Kit logo

JetShip - Laravel Starter Kit

A Laravel SaaS Boilerplate and a starter kit built on the TALL stack. It includes authentication, payments, admin panels, and more. Launch scalable apps fast with clean code, seamless deployment, and custom branding.

JetShip - Laravel Starter Kit
Rector logo

Rector

Your partner for seamless Laravel upgrades, cutting costs, and accelerating innovation for successful companies

Rector
MongoDB logo

MongoDB

Enhance your PHP applications with the powerful integration of MongoDB and Laravel, empowering developers to build applications with ease and efficiency. Support transactional, search, analytics and mobile use cases while using the familiar Eloquent APIs. Discover how MongoDB's flexible, modern database can transform your Laravel applications.

MongoDB

The latest

View all →
Streamlining Route Parameters in Laravel Using URL Defaults image

Streamlining Route Parameters in Laravel Using URL Defaults

Read article
Flexible Docker Images with PHP INI Environment Variables image

Flexible Docker Images with PHP INI Environment Variables

Read article
Dynamic Form Validation in Laravel with prohibited_if image

Dynamic Form Validation in Laravel with prohibited_if

Read article
Add Approvals to Your Laravel Application image

Add Approvals to Your Laravel Application

Read article
Enhancing Data Processing with Laravel's transform() Method image

Enhancing Data Processing with Laravel's transform() Method

Read article
Get Xdebug Working With Docker and PHP 8.4 in One Minute image

Get Xdebug Working With Docker and PHP 8.4 in One Minute

Read article