Laravel pass id to controller using AJAX

Stefan Bogdanescu

Founder & Senior Architect · 2026-06-29

Laravel Company

Passing IDs to Controllers via AJAX: The Laravel Way

As developers building modern, dynamic web applications, interacting with backend resources asynchronously using AJAX is a fundamental skill. When you trigger an action on your frontend and need to send specific data—like a Post ID—to the backend controller, the way you structure that data transfer (the route, the request, and the response) is crucial.

Many developers run into issues when trying to map dynamic JavaScript variables directly into controller method arguments. This post will walk you through the correct, robust ways to pass IDs from an AJAX call to your Laravel controller, ensuring your data flow is clean and adheres to best practices.

Understanding the Pitfall in Your Setup

Let’s analyze the setup you provided:

Route: Route::post('approve', 'PostsController@approve');
JavaScript: Sends {'id' : post_id} in the request body to the URL /approve.
Controller: public function approve($id)

The reason this often fails is due to how Laravel handles route parameters versus request data. When you define a simple POST route like 'approve', Laravel expects that endpoint to receive some data, but it doesn't automatically map an arbitrary value from the body directly to a method argument named $id unless explicitly told to do so.

The solution lies in understanding the difference between Route Parameters (data embedded in the URL) and Request Data (data sent in the request body).

Method 1: The Recommended Approach – Using the Request Object

For actions triggered by AJAX, the most reliable method is to treat the incoming data as a standard HTTP request. You should inject the Illuminate\Http\Request object into your controller method and extract the necessary ID from the request body. This pattern scales well and keeps your routes simple.

Step 1: Update the Controller Method

Instead of expecting $id directly, accept the full Request object:

// app/Http/Controllers/PostsController.php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; // Make sure you import necessary facades

class PostsController extends Controller
{
    public function approve(Request $request)
    {
        // Retrieve the 'id' sent from the AJAX request body
        $postId = $request->input('id');

        if (!$postId) {
            return response()->json(['error' => 'Post ID is missing.'], 400);
        }

        // Perform the database operation using the retrieved ID
        DB::table('posts')
            ->where('id', $postId)
            ->update(['is_approved' => 1]);

        // Return a success response
        return response()->json(['message' => "Post ID {$postId} successfully approved."], 200);
    }
}

Step 2: Ensure the Route Matches

Your route remains simple, as it just targets the action:

// routes/web.php
Route::post('approve', 'PostsController@approve');

Why this works: This approach is superior because it cleanly separates the routing logic from the data fetching logic. It follows the principle of separation of concerns, which is a core tenet of good Laravel development (as promoted by sites like https://laravelcompany.com).

Method 2: Passing Data via Route Parameters (URL Structure)

If you prefer to embed the ID directly into the URL structure—which is useful for creating RESTful endpoints where the resource ID is part of the path—you should redefine your route and adjust your JavaScript accordingly.

Step 1: Update the Route Definition

Define a route with a parameter:

// routes/web.php
Route::post('approve/{id}', 'PostsController@approve');

Step 2: Update the Controller Method Signature

The controller method must now accept the ID as a direct argument from the URL match:

// app/Http/Controllers/PostsController.php

class PostsController extends Controller
{
    public function approve($id) // $id is now automatically populated by the route
    {
        DB::table('posts')
            ->where('id', $id)
            ->update(['is_approved' => 1]);

        return response()->json(['message' => "Post ID {$id} successfully approved."], 200);
    }
}

Step 3: Update the JavaScript AJAX Call

The JavaScript must now target the full URL path, including the ID:

// JavaScript (Example)
var post_id = $(this).closest('.post').data('post-id');

$.ajax({
    type: 'POST',
    url: 'approve/' + post_id, // Target the specific route with the ID
    data: null,                 // No data needed in the body now
    dataType: 'json',                   
    success: function(response){ 
        console.log(response);
        // Handle success...
    },
    error: function(jqXHR, textStatus, errorThrown) { 
        console.error("AJAX error:", textStatus, errorThrown);
    }
});

What Type of Response Should Be Returned?

When performing a state change (like approving a post), the best practice is to return a JSON response, along with an appropriate HTTP status code.

  1. Success: Return a 200 OK status code with a JSON body confirming the action was successful.
    {"message": "Post ID 6 successfully approved."}
    
  2. Client Error (e.g., Missing ID): If validation fails on the server side, return a 400 Bad Request.
  3. Not Found: If the requested post ID does not exist, return a 404 Not Found.

By using structured JSON responses, your frontend JavaScript can reliably parse the result and update the UI without needing to rely on vague text messages.

Conclusion

For passing dynamic IDs in AJAX requests to Laravel controllers, Method 1 (using $request->input('id')) is generally the most flexible and recommended approach for POST/PUT actions where data is submitted in the body. It keeps your routes clean and handles variable data submission robustly. If you are building a highly structured REST API, Method 2 (Route Parameters) provides a cleaner URL structure. Always prioritize clear status codes and JSON responses when communicating results back to your client. Keep learning about Laravel patterns; they make development much more efficient!