Automate Your API Gateway Setup with Boto3: Rendering HTML from Lambda
Creating RESTful endpoints using the AWS Management Console can quickly become frustrating — especially when dealing with API Gateway. If you've ever felt like the Console experience is too slow for repeatable tasks, you're not alone.
In this post, we’ll walk through a Python script using the boto3
library that automates the creation of an AWS API Gateway REST API backed by a Lambda function — and returns HTML, not JSON. This makes it ideal for rendering mobile-friendly forms or web content directly from serverless logic.
Why Use Python Over the Console?
- Repeatability: Run your script anytime without clicking through dozens of console menus.
- Version Control: Store and track API configurations in Git.
- Speed: Automate CORS setup, deployment stages, permissions — in seconds.
- Precision: Avoid human error by defining everything in code.
Use Case
This script will:
- Create a REST API in API Gateway.
- Add a
/form
route with aGET
method. - Integrate it with a Lambda function that renders raw HTML (not JSON).
- Enable CORS for cross-origin usage.
- Deploy the API and output the public URL.
Python Script
Paste the following into a file, such as deploy_form_api.py
:
import boto3
region = 'us-east-1'
lambda_arn = 'arn:aws:lambda:us-east-1:8145480xxxxx:function:FormTestRender'
account_id = '814548xxxxxx'
apigateway = boto3.client('apigateway', region_name=region)
lambda_client = boto3.client('lambda', region_name=region)
def create_rest_api(name):
print(f"Creating REST API '{name}'...")
response = apigateway.create_rest_api(
name=name,
description='API with Lambda Proxy Integration and full CORS support',
endpointConfiguration={'types': ['REGIONAL']}
)
api_id = response['id']
print(f"Created REST API with id: {api_id}")
return api_id
def get_root_resource_id(rest_api_id):
resources = apigateway.get_resources(restApiId=rest_api_id)
for r in resources['items']:
if r['path'] == '/':
return r['id']
raise Exception("Root resource not found")
def create_resource(rest_api_id, parent_id, path_part):
print(f"Creating resource '/{path_part}'...")
response = apigateway.create_resource(
restApiId=rest_api_id,
parentId=parent_id,
pathPart=path_part
)
resource_id = response['id']
print(f"Resource '/{path_part}' created with id: {resource_id}")
return resource_id
def add_lambda_permission(rest_api_id):
statement_id = f'APIInvokePermission-{rest_api_id}'
source_arn = f'arn:aws:execute-api:{region}:{account_id}:{rest_api_id}/*/GET/formv4'
try:
print("Adding permission to Lambda for API Gateway invoke...")
lambda_client.add_permission(
FunctionName=lambda_arn,
StatementId=statement_id,
Action='lambda:InvokeFunction',
Principal='apigateway.amazonaws.com',
SourceArn=source_arn
)
print("Permission added.")
except lambda_client.exceptions.ResourceConflictException:
print("Lambda permission already exists, skipping.")
def put_method(rest_api_id, resource_id, http_method):
print(f"Creating {http_method} method with no authorization...")
apigateway.put_method(
restApiId=rest_api_id,
resourceId=resource_id,
httpMethod=http_method,
authorizationType='NONE',
apiKeyRequired=False
)
print(f"{http_method} method created.")
def put_integration(rest_api_id, resource_id, http_method, integration_type, uri=None, request_templates=None):
print(f"Setting up {http_method} integration...")
params = dict(
restApiId=rest_api_id,
resourceId=resource_id,
httpMethod=http_method,
type=integration_type
)
if integration_type == 'AWS_PROXY':
params['integrationHttpMethod'] = 'POST'
params['uri'] = uri
if integration_type == 'MOCK':
params['requestTemplates'] = request_templates
apigateway.put_integration(**params)
print(f"{http_method} integration created.")
def put_method_response(rest_api_id, resource_id, http_method):
print(f"Setting method response for {http_method}...")
apigateway.put_method_response(
restApiId=rest_api_id,
resourceId=resource_id,
httpMethod=http_method,
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Headers': True,
'method.response.header.Access-Control-Allow-Methods': True,
'method.response.header.Access-Control-Allow-Origin': True
}
)
print(f"Method response for {http_method} set.")
def put_integration_response(rest_api_id, resource_id, http_method):
print(f"Setting integration response for {http_method}...")
apigateway.put_integration_response(
restApiId=rest_api_id,
resourceId=resource_id,
httpMethod=http_method,
statusCode='200',
responseParameters={
'method.response.header.Access-Control-Allow-Headers': "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'",
'method.response.header.Access-Control-Allow-Methods': "'GET,OPTIONS'",
'method.response.header.Access-Control-Allow-Origin': "'*'"
}
)
print(f"Integration response for {http_method} set.")
def deploy_api(rest_api_id, stage_name):
print(f"Deploying API to stage '{stage_name}'...")
apigateway.create_deployment(
restApiId=rest_api_id,
stageName=stage_name,
description='Deployment for Lambda Proxy Integration API with CORS'
)
print("Deployment complete.")
def main():
api_name = 'FormRenderAPIv4'
stage_name = 'formv4'
rest_api_id = create_rest_api(api_name)
root_id = get_root_resource_id(rest_api_id)
resource_id = create_resource(rest_api_id, root_id, 'formv4')
add_lambda_permission(rest_api_id)
# GET method setup
put_method(rest_api_id, resource_id, 'GET')
uri = f'arn:aws:apigateway:{region}:lambda:path/2015-03-31/functions/{lambda_arn}/invocations'
put_integration(rest_api_id, resource_id, 'GET', 'AWS_PROXY', uri=uri)
put_method_response(rest_api_id, resource_id, 'GET')
put_integration_response(rest_api_id, resource_id, 'GET')
# OPTIONS method setup for CORS preflight
put_method(rest_api_id, resource_id, 'OPTIONS')
put_integration(rest_api_id, resource_id, 'OPTIONS', 'MOCK', request_templates={'application/json': '{"statusCode": 200}'})
put_method_response(rest_api_id, resource_id, 'OPTIONS')
put_integration_response(rest_api_id, resource_id, 'OPTIONS')
deploy_api(rest_api_id, stage_name)
invoke_url = f"https://{rest_api_id}.execute-api.{region}.amazonaws.com/{stage_name}/formv4"
print(f"\nYour API Gateway is ready! Invoke URL:\n{invoke_url}")
if __name__ == '__main__':
main()
What You’ll See
If your Lambda function returns raw HTML (not JSON), the browser will render the HTML directly — which is perfect for mobile-friendly forms or visual outputs.
Example Use Case
- Create a mobile data collection form using Bootstrap
- Render it with Lambda
- Serve it using this API Gateway endpoint
Final Thoughts
Using boto3 and a script-first approach to AWS API Gateway gives you control, speed, and automation — all while avoiding the web console. It’s cleaner, repeatable, and production-ready.
Comments
Post a Comment