Documentation
Use cases
Advanced HTTP use cases

Advanced HTTP use-cases

💡

If you are getting started with Bref, read the guides for Laravel (Get started), Symfony (Get started), or other PHP frameworks (Get started).

This documentation is for advanced use-cases.

Alternative AWS architectures

By default, Bref uses API Gateway v2 HTTP APIs to run HTTP applications.

However, there are other ways to run HTTP applications on AWS Lambda: using API Gateway (opens in a new tab), Lambda Function URLs (opens in a new tab), or AWS Application Load Balancer (ALB) (opens in a new tab):

  • API Gateway v1 "REST" + Lambda
  • API Gateway v2 "HTTP" + Lambda
  • Lambda Function URL
  • AWS ALB (Application Load Balancer) + Lambda

Bref supports all 4, but API Gateway v2 "HTTP" is the default because it is the simplest option that supports both HTTP APIs and websites with custom domains.

💡

If you are getting started, stay with the defaults. The documentation and integrations will be much simpler to follow.

You can change the architecture later if you need to.

Here is a summary of the differences between the 4 options:

API Gateway v1 RESTAPI Gateway v2 HTTPFunction URLALB
Pricing (Lambda cost excluded)$3.5/million requests$1/million requestsFree (no extra costs)Starts at $22/mo
Custom domainNo, but possible with CloudFront
AuthorizersIAM, Lambda, CognitoIAM, Lambda, Cognito, OAuth 2IAMOIDC, Cognito
CORS
CloudWatch metrics
CloudWatch access logs
Caching
API key management
Request transformation
Request/response validation
Maximum HTTP response timeout29s30s15 minutes15 minutes
Added latency to HTTP responses25ms15ms10ms

You can read more details in the "Choosing between REST APIs and HTTP APIs" AWS documentation (opens in a new tab).

API Gateway v2 HTTP API

The simplest way to set up API Gateway is to have all incoming requests sent to our application in one Lambda function:

serverless.yml
functions:
    hello:
        handler: index.php
        # ...
        events:
            - httpApi: '*'

That works well with frameworks like Symfony or Laravel that have a single entrypoint (e.g. public/index.php) combined with the PHP-FPM runtime.

You can look at more advanced API Gateway routing options in the serverless documentation (opens in a new tab).

Lambda Function URLs

Since 2022, AWS Lambda can respond to HTTP requests via Lambda Function URLs (opens in a new tab). This is a new way to invoke Lambda functions via HTTP without using API Gateway.

You can deploy a Lambda Function URL via the following configuration (opens in a new tab):

serverless.yml
functions:
    hello:
        handler: index.php
        # ...
        url: true

API Gateway v1 REST API

The syntax is slightly different from API Gateway v2 HTTP APIs as we must use a different events configuration. Here is an example that sends all requests to a single Lambda function:

serverless.yml
functions:
    hello:
        handler: index.php
        # ...
        events:
            - http: 'ANY /'
            - http: 'ANY /{proxy+}'

Learn more in the Serverless Framework documentation (opens in a new tab).

Application Load Balancer

Application Load Balancer (ALB) is a managed load balancer that can be used to route HTTP requests to Lambda functions. It is a more advanced option that is interesting at high scale as ALB can be much cheaper than API Gateway.

serverless.yml
functions:
    hello:
        handler: index.php
        # ...
        events:
            -   alb:
                    listenerArn: arn:aws:elasticloadbalancing:us-east-1:12345:listener/app/my-load-balancer/50dc6c495c0c9188/
                    priority: 1
                    conditions:
                        path: '/*'

Learn more in the Serverless Framework documentation (opens in a new tab).

PHP handlers

💡

This section applies to all 4 approaches: API Gateway v1, v2, ALB, and Function URLs.

Bref abstracts the differences so that the same code can be used with all 4 solutions.

There are two ways to handle HTTP events with PHP:

Here is a full comparison between both approaches:

PHP-FPM runtimeEvent-Driven Function handler
What are the use cases?To build websites, APIs, etc. This should be the default approach as it's compatible with mature PHP frameworks and tools.Build event-driven microservices, or run Laravel Octane, or Symfony with a keep-alive process (like Roadrunner/Swoole).
Why does that solution exist?For out-of-the-box compatibility with existing applications and frameworks.To match how other languages run in AWS Lambda, i.e. to build very specialized HTTP endpoints.
How it runs under the hoodUsing PHP-FPM.Using the PHP CLI.
What does the routing (i.e. separate pages)?Your PHP framework (one Lambda receives all the URLs).API Gateway: we define one Lambda and one handler class per route.
How to read the request?$_GET, $_POST, etc.The $request parameter (PSR-7 request).
How to write a response?echo, header() function, etc.Returning a PSR-7 response from the handler class.
How does it work?Bref turns an API Gateway event into a FastCGI (PHP-FPM) request.Bref turns an API Gateway event into a PSR-7 request.
Is each request handled in a separate PHP process?Yes (that's how PHP-FPM works).Yes by default (Bref replicates that to avoid surprises) but can be disabled for optimal performances.

With the PHP-FPM runtime

This is perfect for most use-cases: PHP works like on any server with PHP-FPM. HTTP routing based on the URL is done by the application/the framework.

This approach is already covered by most of the Bref documentation, so we won't go into details here.

You can read more about the PHP-FPM runtime here.

With the Event-Driven Function runtime

This is more advanced, as PHP does not run in a traditional PHP-FPM environment. It can be used with or without a PHP framework.

When used with a framework, understand that the whole HTTP stack (like HTTP middlewares) of the framework does not run. You are responsible for invoking the PHP code that should run.

💡

Note: this approach is used to run Laravel Octane or Symfony with a keep-alive process (like Roadrunner/Swoole). These use cases are not detailed here, read about Laravel Octane or about Symfony "Keep-Alive" instead.

The handler must be a PHP function, or a PSR-15 implementation. Indeed, Bref natively supports the PSR-15 (opens in a new tab) and PSR-7 (opens in a new tab) standards. Here is an example:

<?php
 
namespace App;
 
use Nyholm\Psr7\Response;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
 
class MyHttpHandler implements RequestHandlerInterface
{
    public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $name = $request->getQueryParams()['name'] ?? 'world';
 
        return new Response(200, [], "Hello $name");
    }
}

Then, create a Lambda function that listens to HTTP events with the handler you created:

serverless.yml
functions:
    # ...
    handler: App\MyHttpHandler
    runtime: php-81
    # Lambda Function URL
    url: true
    # Or API Gateway
    events:
        # API Gateway v2
        - httpApi: 'GET /hello'
        # API Gateway v1
        - http: 'GET hello'

The App\MyHttpHandler class will be instantiated by Laravel's service container.

Since a handler is a controller for a specific route, we can use the API Gateway routing to deploy multiple functions:

serverless.yml
functions:
    create-article:
        handler: App\CreateArticleController
        runtime: php-81
        events:
            - httpApi: 'POST /articles'
    get-article:
        handler: App\GetArticleController
        runtime: php-81
        events:
            - httpApi: 'GET /articles/{id}'

Path parameters (e.g. {id} in the example above) are available as request attributes in the PSR-7 request:

$id = $request->getAttribute('id');

Full reference of HTTP events in serverless.yml (opens in a new tab).

Lambda event and context

The API Gateway event and Lambda context are available as attributes on the PSR-7 request:

/** @var $event Bref\Event\Http\HttpRequestEvent */
$event = $request->getAttribute('lambda-event');
 
/** @var $context Bref\Context\Context */
$context = $request->getAttribute('lambda-context');

If you're looking for the request context array, for example when using a Lambda authorizer (opens in a new tab):

$requestContext = $request->getAttribute('lambda-event')->getRequestContext();

Cold starts

AWS Lambda automatically destroys Lambda containers that have been unused for 10 minutes. Warming up a new container can take 250ms or more, especially if your application is large. This delay is called cold start (opens in a new tab).

To mitigate cold starts for HTTP applications, you can periodically send an event to your Lambda including a {warmer: true} payload. This will trigger the Lambda function. Bref recognizes this event and immediately responds with a Status: 100 without executing your code.

You can set up such events using a schedule (read this article for more details (opens in a new tab)):

serverless.yml
        events:
            - httpApi: '*'
            - schedule:
                rate: rate(5 minutes)
                input:
                    warmer: true

You can learn more how AWS Lambda scales and runs in the Serverless Visually Explained (opens in a new tab) course.