Search code examples
phplaravellambdaserverlesslaravel-facade

Laravel facade problem when deployed on aws lambda


I'm using laravel 8 and bref to deploy it on lambda. After making a cron job function to send email. When I deploy it, there's a problem with the facade

{
  "errorType": "RuntimeException",
  "errorMessage": "A facade root has not been set.",
  "stackTrace": [
    "#0 /var/task/app/functions/sendTestMail.php(11): Illuminate\\Support\\Facades\\Facade::__callStatic()",
    "#1 /var/task/vendor/bref/bref/src/Runtime/Invoker.php(34): Bref\\Runtime\\FileHandlerLocator->App\\Functions\\{closure}()",
    "#2 /var/task/vendor/bref/bref/src/Runtime/LambdaRuntime.php(102): Bref\\Runtime\\Invoker->invoke()",
    "#3 /opt/bref/bootstrap.php(43): Bref\\Runtime\\LambdaRuntime->processNextEvent()",
    "#4 {main}"
  ]
}

here is my directory structure and the function: sendTestMail.php

serverless.yml:

service: test
provider:
  name: aws
  # The AWS region in which to deploy (us-east-1 is the default)
  region: ap-southeast-1
  # The stage of the application, e.g. dev, production, staging… ('dev' is the default)
  stage: dev
  runtime: provided.al2

package:
  # Directories to exclude from deployment
  exclude:
    - node_modules/**
    - public/storage
    - resources/assets/**
    - storage/**
    - tests/**

functions:
  # This function runs the Laravel website/API
  web:
    handler: public/index.php
    timeout: 28 # in seconds (API Gateway has a timeout of 29 seconds)
    layers:
      - ${bref:layer.php-80-fpm}
    events:
      - httpApi: "*"
  # This function lets us run artisan commands in Lambda
  artisan:
    handler: artisan
    timeout: 120 # in seconds
    layers:
      - ${bref:layer.php-80} # PHP
      - ${bref:layer.console} # The "console" layer
  cron:
    handler: app/functions/sendTestMail.php
    layers:
      - ${bref:layer.php-80}
    events:
      - schedule: rate(5 minutes)

plugins:
  # We need to include the Bref plugin
  - ./vendor/bref/bref

Anyone know how to resolve this issue? And btw, how can I test a handler function on my local machine before deploying? Thank you


Solution

  • I believe this is due to the issue that in your web function the handler is public/index.php. This properly initializes the Laravel application. Your cron functions handler is app/functions/sendTestMail.php so index.php never gets called and the Laravel kernel never handles the request.

    I don't have a great solution at the moment because I feel like it is breaking a lot of best practices and rules in Laravel and want to experiment more with it. But I was able to take the entire index.php content and pasted it above my return function in the file that Lambda was calling.

    So in other words I think if you paste this

    use Illuminate\Contracts\Http\Kernel;
    use Illuminate\Http\Request;
    
    use App\Models\Task;
    define('LARAVEL_START', microtime(true));
    if (file_exists(__DIR__.'/storage/framework/maintenance.php')) {
        require __DIR__.'/storage/framework/maintenance.php';
    }
    require __DIR__.'/vendor/autoload.php';
    
    $app = require_once __DIR__.'/bootstrap/app.php';
    $kernel = $app->make(Kernel::class);
    
    $response = tap($kernel->handle(
        $request = Request::capture()
    ))->send();
    
    $kernel->terminate($request, $response);
    

    As the first thing in your app/functions/sendTestMail.php file, it will likely work. Depending on what you have coded in your Middleware as that will run first.

    This worked for me in my application.