Deploying a Symfony Application to AWS Lambda
First, let’s understand what a serverless architecture is and when is it needed.
Serverless architecture allows you to execute snippets of code without the hassle of infrastructure: in this case, the cloud provider handles the management of the web server, physical hardware and administration, allowing you to focus solely on the code.
AWS Lambda provides high availability and you are billed only for the actual compute time spent. This service can be very useful for such tasks as starting cron jobs, sending notifications in real time, providing API access, handling some events when performing various operations, etc. You can find a lot of examples of using the service on the web. …
Our use case
Provide access to an API built with Symfonywhich posts messages to LinkedIn… The development process will include the stages from writing to deploying the code.
Writing the code
Symfony version 5 has a new component called Notifier, which makes it possible to send notifications through different services (Slack, Twitter, Twilio and etc.).
Symfony has no built-in support LinkedIn, so a few months ago I created a gateway on top of Notifier for publishing content on this social network. The source code of the gateway can be viewed link, we will use it in this demo as well.
Let’s get started
$ symfony new --full aws-lambda-linkedin-notifier
$ cd aws-lambda-linkedin-notifier
$ composer require eniams/linkedin-notifier
Turn on the gateway (see documentation)
<?php
// config/bundles.php
return [
// others bundles,
EniamsNotifierLinkedInLinkedInNotifierBundle::class => ['all' => true]];
// .env
LINKEDIN_DSN=
Content publishing logic
<?php
class PostContentController
{
/**
* @Route("/contents", name="post_content", methods="POST")
*/
public function __invoke(NotifierInterface $notifier, Request $request)
{
if(null !== $message = (json_decode($request->getContent(), true)['message'] ?? null)) {
$notifier->send(new Notification($message, ['chat/linkedin']));
return new JsonResponse('message posted with success', 201);
}
throw new BadRequestException('Missing "message" in body');
}
}
The logic is simple: we provide API access via a route /contents
which accepts a POST request with a message message
in his body.
On line 11, we send the post to LinkedIn – thanks to Symfony and the gateway, this is very easy!
As a professional developer, let’s cover this code with autotests:
<?php
class PostContentControllerTest extends WebTestCase
/**
* @dataProvider methodProvider
*/
public function testANoPostRequestShouldReturnA405(string $method)
{
$client = static::createClient();
$client->request($method, '/contents');
self::assertEquals(405, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithoutAMessageInBodyShouldReturnA400()
{
$client = static::createClient();
$client->request('POST', '/contents');
self::assertEquals(400, $client->getResponse()->getStatusCode());
}
public function testAPostRequestWithAMessageInBodyShouldReturnA201()
{
$request = new Request([],[],[],[],[],[], json_encode(['message' => 'Hello World']));
$notifier = new class implements NotifierInterface {
public function send(Notification $notification, Recipient ...$recipients): void
{
}
};
$controller = new PostContentController();
$response = $controller->__invoke($notifier, $request);
self::assertEquals(201, $response->getStatusCode());
}
public function methodProvider()
{
return [
['GET'],
['PUT'],
['DELETE'],
];
}
view rawTestPostContentController.php hosted with by GitHub
The code is ready! It’s time to turn it around
Stop! What?! AWS Lambda does not support PHP!
That’s right: AWS Lambda does not support all programming languages, but only a few, including Go, Java, Python, Ruby, NodeJS and .NET…
Now you need to learn a new language and rewrite the code? I hope not!
No need to rewrite anything thanks to Matthieu Nappoli, the creator Bref.sh, and a great team helping him maintain this project is open source…
Bref allows you to deploy PHP applications to AWS and run them on AWS Lambda…
Deployment configuration
Let’s add log processing to kernel.php:
<?php
// Kernel.php
public function getLogDir(): string
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/log/';
}
return parent::getLogDir();
}
public function getCacheDir()
{
if (getenv('LAMBDA_TASK_ROOT') !== false) {
return '/tmp/cache/'.$this->environment;
}
return parent::getCacheDir();
}
Let’s prepare the Serverless framework (see documentation):
$ npm install -g serverless
$ serverless config credentials --provider aws --key --secret
$ composer require bref/bref
Let’s set the configuration in the serverless.yaml file:
service: notifier-linkedin-api
provider:
name: aws
region: eu-west-3
runtime: provided
environment: # env vars
APP_ENV: prod
LINKEDIN_DSN: YOUR_DSN
plugins:
- ./vendor/bref/bref
functions:
website:
handler: public/index.php # bootstrap
layers:
- ${bref:layer.php-73-fpm} # https://bref.sh/docs/runtimes/index.html#usage
timeout: 28 # Timeout to stop the compute time
events:
- http: 'POST /contents' # Only POST to /contents are allowed
package:
exclude:
- 'tests/**'
view rawserverless.yaml hosted with by GitHub
We are deploying!
$ serverless deploy
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service notifier-linkedin-api.zip file to S3 (10.05 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
....................
Serverless: Stack update finished...
Service Information
service: notifier-linkedin-api
stage: dev
region: eu-west-3
stack: notifier-linkedin-api-dev
resources: 15
api keys:
None
endpoints:
POST - https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents
functions:
website: notifier-linkedin-api-dev-website
layers:
None
Serverless: Removing old service artifacts from S3...
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
Our code is now deployed to AWS Lambda and the API is available at https://xxx.execute-api.eu-west-3.amazonaws.com/dev/contents
…
Let’s try:
The translation of the material was prepared as part of the course “Symfony Framework”… If you are interested in learning more about the course, we invite you to Open Day online, where the teacher will talk about the format and training program.