Deploying the Python Masonite framework on Lambda
Create Lambda function¶
AWS Lambda Home - region us-east-1
- Click 'Create function'
- Input 'Function name'
- Runtime: Python 3.11
Lambda handler¶
Create the lambda_function.py
file in the root directory of the project.
from wsgi import application
import sys
import base64
from io import BytesIO
from urllib.parse import urlencode
def lambda_handler(event, context):
headers = event.get('headers', {})
if event['headers'].get('X-Forwarded-Proto'):
scheme = event['headers']['X-Forwarded-Proto']
elif event['headers'].get('CloudFront-Forwarded-Proto'):
scheme = event['headers']['CloudFront-Forwarded-Proto']
else:
scheme = 'https'
if scheme == 'http':
port = '80'
else:
port = '443'
environ = {
'REQUEST_METHOD': event['httpMethod'],
'PATH_INFO': event['path'],
'QUERY_STRING': urlencode(event.get('queryStringParameters', '') or ''),
# 'CONTENT_TYPE': '',
# 'CONTENT_LENGTH': '',
'SERVER_NAME': event['requestContext']['domainName'],
'SERVER_PORT': port,
'SERVER_PROTOCOL': event['requestContext']['protocol'],
'wsgi.version': (1, 0),
'wsgi.url_scheme': scheme,
'wsgi.input': BytesIO((event.get('body') or '').encode('utf-8')),
'wsgi.errors': sys.stderr,
'wsgi.multithread': False,
'wsgi.multiprocess': False,
'wsgi.run_once': False,
}
for header, value in headers.items():
header_key = 'HTTP_' + header.upper().replace('-', '_')
environ[header_key] = value
response_data = {'status': None, 'headers': None}
def start_response(status, response_headers, exc_info=None):
if exc_info:
try:
if response_data['status'] is not None:
raise exc_info[1].with_traceback(exc_info[2])
finally:
exc_info = None
response_data['status'] = status
response_data['headers'] = response_headers
return lambda body: None
try:
response_iter = application(environ, start_response)
response_body = b''.join(response_iter)
if response_data['status'] is None:
raise RuntimeError('start_response() was not called')
status_code = int(response_data['status'].split(' ')[0])
headers = dict(response_data['headers'])
is_base64_encoded = False
content_type = headers.get('Content-Type', 'application/octet-stream')
is_text = (
content_type.startswith('text/') or content_type in {
'application/json',
'application/xml',
'application/javascript',
'application/css'
} or
'charset' in content_type.lower() or
content_type == 'image/svg+xml'
)
if not is_text and response_body:
response_body = base64.b64encode(response_body).decode('utf-8')
is_base64_encoded = True
return {
'statusCode': status_code,
'headers': headers,
'body': response_body,
'isBase64Encoded': is_base64_encoded
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': 'Internal Server Error'
}
GitHub¶
Actions secrets and variables¶
Repositorie -> Settings -> Secrets and Variables -> Actions
- Add
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
(Secrets). - Add
LAMBDA_FUNC_NAME
(Variables).
Actions¶
Create the .github/workflows/lambda.yml
within the project (Supports manual deployment or automatic deployment when pushed to the main branch).
name: AWS Lambda Deploy
on:
push:
branches:
- main
workflow_dispatch:
jobs:
Start:
runs-on: ubuntu-22.04
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt --target ./
- name: Package application
run: |
cp .env-example .env
python craft key
zip -r source-code-package.zip . -x ".git*" "__pycache__/*"
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: To Lambda
run: aws lambda update-function-code --function-name ${{ vars.LAMBDA_FUNC_NAME }} --zip-file fileb://source-code-package.zip
- name: Verify deployment
run: |
aws lambda get-function --function-name ${{ vars.LAMBDA_FUNC_NAME }} --query 'Configuration.[LastModified, State, LastUpdateStatus]' --output table
Domain¶
AWS API Gateway¶
Create resource¶
Notes:
- {proxy+}
- Enable CORS
- Lambda proxy integration
- Setting: Binary media types (e.g. */*)
Custom domain name¶
After creating a custom domain name on the AWS API Gateway, Add the domain dns record.
- type: CNAME
- value: API Gateway domain name (e.g. *.cloudfront.net)