Laravel Context: Share Data Across Your Request Lifecycle

Laravel Context is a powerful feature that allows you to capture, retrieve, and share information throughout your application's request lifecycle, including jobs and queued tasks.

February 1, 20264 min readssbhattarai

What is Laravel Context?

Think of Context as a request-scoped container that automatically propagates data across your entire application - from HTTP requests to queued jobs, logs, and beyond.

When to Use Context

  1. Multi-tenant Applications
    • Store company/tenant ID for the entire request
    • Automatically scope database queries
    • Include tenant context in logs
  2. Request Tracking
    • Add unique trace IDs to track requests across microservices
    • Debug distributed systems
    • Monitor request flows
  3. User Context
    • Store authenticated user information
    • Pass user preferences to queued jobs
    • Include user data in logs without repetition
  4. Feature Flags & Permissions
    • Store feature flags for the request
    • Cache user permissions
    • Share A/B test variants

Basic Syntax & Import

// Import the Context facade
use Illuminate\Support\Facades\Context;

// Basic usage
Context::add('key', 'value');           // Add data
$value = Context::get('key');            // Retrieve data
Context::has('key');                     // Check if exists
Context::forget('key');                  // Remove data

Available in: Laravel 11.x and 12.x

Quick Start Examples

Example 1: Multi-Tenant Company ID

// In middleware (App/Http/Middleware/SetCompanyContext.php)
use Illuminate\Support\Facades\Context;

class SetCompanyContext
{
    public function handle(Request $request, Closure $next)
    {
        // Get company from header or authenticated user
        $companyId = $request->header('X-Company-ID') 
                     ?? auth()->user()?->company_id;
        
        if ($companyId) {
            Context::add('company_id', $companyId);
        }
        
        return $next($request);
    }
}

Now, anywhere in your application:

// In a model query
$companyId = Context::get('company_id');
$users = User::where('company_id', $companyId)->get();

// In a job (automatically available!)
class SendReport implements ShouldQueue
{
    public function handle()
    {
        $companyId = Context::get('company_id');
        // Context is automatically passed to queued jobs!
    }
}

Example 2: Request Tracking with Trace ID

// In middleware
use Illuminate\Support\Str;

Context::add('trace_id', Str::uuid()->toString());
Context::add('url', $request->url());

// Now all logs automatically include trace_id
Log::info('Processing order', ['order_id' => $order->id]);
// Output: Processing order. {"order_id":123} {"trace_id":"abc-123","url":"https://..."}

Example 3: User Preferences

// In middleware after authentication
Context::add('locale', auth()->user()->locale);
Context::add('timezone', auth()->user()->timezone);

// In a queued notification
class OrderShipped extends Notification implements ShouldQueue
{
    public function handle()
    {
        $locale = Context::get('locale');
        App::setLocale($locale); // User gets notification in their language!
    }
}
 

Advanced Tips & Tricks

1. Hidden Context (Sensitive Data)

//Use hidden context for data you need but don't want in logs:
// Won't appear in logs
Context::addHidden('user_id', auth()->id());
Context::addHidden('api_key', $request->header('X-API-Key'));

// Retrieve when needed
$userId = Context::getHidden('user_id');

Context::get('user_id'); // null

//All available options related to hidden
Context::addHidden(/* ... */);
Context::addHiddenIf(/* ... */);
Context::pushHidden(/* ... */);
Context::getHidden(/* ... */);
Context::pullHidden(/* ... */);
Context::popHidden(/* ... */);
Context::onlyHidden(/* ... */);
Context::exceptHidden(/* ... */);
Context::allHidden(/* ... */);
Context::hasHidden(/* ... */);
Context::missingHidden(/* ... */);
Context::forgetHidden(/* ... */);
 

2. Conditional Context

The when method may be used to add data to the context based on a given condition. The first closure provided to the when method will be invoked if the given condition evaluates to true, while the second closure will be invoked if the condition evaluates to false:

Context::when(
    auth()->user()->isAdmin(),
    fn($ctx) => $ctx->add('is_admin', true),
    fn($ctx) => $ctx->add('is_admin', false)
);

3. Stacks for Breadcrumbs

// Track execution flow
Context::push('breadcrumbs', 'UserController@store');
Context::push('breadcrumbs', 'CreateUserAction');
Context::push('breadcrumbs', 'UserRepository@create');

// Later retrieve the execution path
$executionPath = Context::get('breadcrumbs');
// ['UserController@store', 'CreateUserAction', 'UserRepository@create']

We can accomplish a lot by using context as needed. For more details, please refer to the official documentation (Laravel 12).

Quick Reference

// Add data
Context::add('key', 'value');
Context::addHidden('secret', 'value');
Context::addIf('key', 'value'); // Only if not exists

// Retrieve
Context::get('key');
Context::getHidden('secret');
Context::all();
Context::only(['key1', 'key2']);

// Check existence
Context::has('key');
Context::missing('key');

// Remove
Context::forget('key');
Context::pull('key'); // Get and remove

// Stacks
Context::push('stack', 'value');
Context::pop('stack');
Context::stackContains('stack', 'value');

// Counters
Context::increment('count');
Context::decrement('count');

Conclusion

Laravel Context is perfect for:

  • Multi-tenant applications (company/tenant IDs)
  • Request tracing and debugging
  • Passing data to queued jobs automatically
  • Reducing repetitive code for cross-cutting concerns

Start simple with a trace ID or company ID, and expand as you see the benefits!


Pro Tip: Check your logs after implementing Context - you'll be amazed at how much easier debugging becomes when every log entry includes your trace ID and tenant context automatically.

Happy Coding !!