Lumen request validation for JSON APIs without form requests

lamp, lights, light, a flashlight, lumen, led, orange, black, reflector, modern, petzl

How Lumen request validation works

Lumen request validation is built for small, stateless PHP APIs that need to reject invalid input before it reaches application logic. It uses much of Laravel’s validation foundation, but it does not behave exactly like a full Laravel application. In the official Lumen 11.x validation documentation, form requests are not supported, validation failures are returned as JSON, and session-based error flashing is not part of the default workflow.

That makes Lumen a practical fit for endpoints that receive JSON payloads from front ends, instruments, integrations, or internal services. It also means validation rules should be explicit, the error response shape should be tested, and Laravel form request examples should not be copied into Lumen without adjustment.

lamp, lights, light, a flashlight, lumen, led, orange, black, reflector, modern, petzl

For related technical notes in this category, see the service and validation section.

The key difference between Lumen and Laravel validation

Many developers look up Lumen validation after working with Laravel’s form request classes. That is often where the confusion starts. Laravel applications commonly place rules inside custom request classes so controllers stay thin. Lumen’s official validation documentation states that form requests are not supported in Lumen and recommends the full Laravel framework for applications that require that pattern.

In Lumen, validation is usually placed in a route closure, a controller method, or a separate application class called from the controller. This does not mean validation has to become disorganized. Small endpoints can validate inline. Larger APIs can move rule arrays, custom messages, and normalization logic into dedicated classes while still invoking validation from the controller layer.

Area Lumen behavior Why it matters
Form requests Not supported by Lumen Do not copy Laravel FormRequest examples directly
Validation failure response JSON response with error messages Fits API clients, but differs from browser redirects
Sessions and flashed errors Not supported out of the box The Laravel $errors view variable is not available by default
exists and unique rules Require Eloquent to be enabled Database-backed validation depends on application bootstrap configuration
Testing validation errors Errors are returned at the root of the JSON object Tests should assert against the Lumen response shape

Using $this->validate in a controller

The simplest Lumen validation pattern is to call $this->validate with the incoming request and a rule array. This works well when the endpoint is small and the rules are easy to read in one place. The official Lumen validation documentation describes this helper as available through the framework’s base controller convenience methods.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ValidationRequestController extends Controller
{
    public function store(Request $request)
    {
        $this->validate($request, [
            'instrument_id' => 'required|string|max:64',
            'requested_at' => 'required|date',
            'contact_email' => 'required|email',
            'measurements' => 'required|array',
            'measurements.*.name' => 'required|string|max:100',
            'measurements.*.value' => 'required|numeric',
        ]);

        return response()->json([
            'status' => 'accepted'
        ], 201);
    }
}

This example validates a JSON request that could be sent to an API endpoint for service, calibration, or measurement-related data. The field names are illustrative, but the pattern is the important part: required fields are declared at the endpoint boundary, arrays are validated explicitly, and nested values use dot notation.

As a rule of thumb, keep inline validation only while the rules remain readable. If a controller method grows because of conditional rules, repeated arrays, or custom messages, extract the rule set into a dedicated class or method. Lumen does not provide Laravel form requests, but ordinary PHP classes can still keep validation logic separated and maintainable.

When to use Validator::make instead

Lumen also allows manual validator instances through Laravel’s validation component. This is useful when an endpoint needs custom error handling, conditional branching, or additional response fields. It can also help when the same validation flow is shared by multiple routes without introducing a full form request layer.

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

$router->post('/validation-requests', function (Request $request) {
    $rules = [
        'instrument_id' => 'required|string|max:64',
        'contact_email' => 'required|email',
        'priority' => 'nullable|in:normal,urgent',
    ];

    $messages = [
        'contact_email.email' => 'The contact email must be a valid email address.',
    ];

    $validator = Validator::make($request->all(), $rules, $messages);

    if ($validator->fails()) {
        return response()->json([
            'message' => 'The given data was invalid.',
            'errors' => $validator->errors(),
        ], 422);
    }

    return response()->json(['status' => 'accepted'], 201);
});

The manual approach gives developers more control over the response body. That control matters for public APIs where client applications depend on a stable error contract. One team may accept the default Lumen error structure, while another may require every validation response to include a top-level message, an errors object, a request identifier, or an internal error code.

The trade-off is responsibility. Once a project customizes validation responses, that format should be documented and tested. Otherwise, API clients may break when one endpoint returns root-level field errors and another wraps the same errors inside a different object.

Handling database-backed rules such as unique and exists

The official Lumen validation documentation notes an important setup requirement for the exists and unique rules: Eloquent must be enabled in bootstrap/app.php. In many Lumen projects, developers intentionally keep the framework lean, so this line may be commented out during initial setup. If database-backed validation does not behave as expected, Eloquent configuration should be one of the first items to check.

// bootstrap/app.php

$app->withEloquent();

After Eloquent is enabled, database-aware rules can be used to check whether an ID exists or whether a field should be unique. These rules are valuable, but they should not be treated as the only protection against data conflicts. Validation runs before the write operation, while unique indexes and database constraints protect the data at the storage layer. In concurrent API traffic, both layers may be necessary.

$this->validate($request, [
    'instrument_id' => 'required|exists:instruments,id',
    'request_number' => 'required|unique:validation_requests,request_number',
]);

For APIs that handle service or validation records, this pattern helps reject references to missing records and prevent duplicate request identifiers. The table and column names should match the actual database schema, and sensitive implementation details should not be exposed in validation messages returned to external clients.

JSON error responses and API clients

Lumen’s validation behavior is oriented toward JSON APIs. The official documentation explains that $this->validate returns JSON error messages instead of redirecting back with flashed session data. That differs from the behavior many developers associate with full Laravel web applications. See also: analytical methods.

A typical validation failure may be represented as field names mapped to arrays of messages. The exact message text can vary based on rules, custom messages, language files, and framework version, so client code should rely on the documented response structure rather than one exact English sentence.

{
  "contact_email": [
    "The contact email must be a valid email address."
  ],
  "instrument_id": [
    "The instrument id field is required."
  ]
}

When designing an API, decide whether clients should consume the Lumen default structure or a custom wrapper. The default structure is concise and consistent with Lumen’s stateless design. A custom wrapper can be better when multiple services must share a common error schema. The important point is consistency: a mobile app, integration service, or front-end dashboard should not need a separate parser for every endpoint.

Testing validation behavior in Lumen

Validation rules are easy to write and easy to weaken by accident. A later code change can remove a required rule, rename a field, or change the shape of an error response. Lumen’s official testing documentation describes helpers for issuing JSON requests and checking responses, and it notes that validation errors are returned at the root of the JSON object in Lumen. For assertJsonValidationErrors, the response key should be passed as null when asserting this root-level structure.

public function test_contact_email_is_required()
{
    $response = $this->call('POST', '/validation-requests', [
        'instrument_id' => 'ABC-100'
    ]);

    $response->assertJsonValidationErrors('contact_email', null);
}

Good validation tests should cover more than missing fields. For a meaningful endpoint, include tests for invalid email formats, unacceptable enum values, malformed dates, nested array errors, duplicate identifiers, and successful requests. If an endpoint uses a custom error wrapper, test both the HTTP status code and the JSON structure expected by client applications.

Practical patterns for maintainable Lumen validation

Because Lumen does not support form requests, maintainability depends on discipline. The following patterns help keep validation understandable without adding unnecessary framework weight.

  • Validate at the boundary. Apply request validation before passing data into domain services, database writes, queues, or external integrations.
  • Keep rule names close to payload names. Use clear API field names and avoid unnecessary translation between request keys and internal variables.
  • Extract repeated rules. If multiple endpoints validate the same payload, place the rules in a reusable method or small rule provider class.
  • Separate normalization from validation. Trimming strings, converting units, or mapping aliases should be deliberate and visible rather than hidden inside unrelated business logic.
  • Do not rely on validation alone for security. Validation helps reject bad input, but authorization, authentication, database constraints, and output escaping are separate concerns.
  • Version public error contracts. If third-party clients depend on your validation response format, treat changes to that format as API changes.

For small internal services, the basic $this->validate pattern may be enough. For larger systems, a thin controller plus reusable rule classes can provide a better balance. The goal is not to recreate Laravel form requests inside Lumen. The goal is to keep validation explicit, predictable, and testable.

Frequently asked questions

Does Lumen support Laravel form request validation?

No. The official Lumen validation documentation states that form requests are not supported. If a project depends heavily on Laravel FormRequest classes for authorization and validation organization, the full Laravel framework is usually the better match.

Can Lumen validate JSON request bodies?

Yes. Lumen is commonly used for JSON APIs, and its validation helper can validate request input using familiar Laravel-style rules. Developers should design and test the JSON error response shape expected by their clients.

Why does my unique or exists rule not work in Lumen?

One common cause is that Eloquent has not been enabled. Lumen’s validation documentation notes that database-backed rules such as exists and unique require the $app->withEloquent() call in bootstrap/app.php.

Should I use $this->validate or Validator::make?

Use $this->validate for straightforward endpoint validation where the default JSON response is acceptable. Use Validator::make when you need custom messages, custom response envelopes, conditional handling, or a reusable validation flow.

What status code should a Lumen API return for validation errors?

Many JSON APIs use HTTP 422 for semantic validation failures. Lumen’s built-in validation exception behavior is aligned with API-style validation responses, but projects that customize responses should document the chosen status code and test it consistently.