Python routing mini-framework for AWS Lambda with optional JSON-schema validation.
lambda_handler
function constructor with built-in dispatcher- Decorator to register functions to handle HTTP methods
- Optional JSON-schema input validation using same decorator
Install the package from PyPI using pip:
pip install lambdarest
This module helps you to handle different HTTP methods in your AWS Lambda.
from lambdarest import lambda_handler
@lambda_handler.handle("get")
def my_own_get(event):
return {"this": "will be json dumped"}
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"resource": "/"
}
result = lambda_handler(event=input_event)
assert result == {"body": '{"this": "will be json dumped"}', "statusCode": 200, "headers":{}}
Optionally you can validate an incoming JSON body against a JSON schema:
from lambdarest import lambda_handler
my_schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"body":{
"type": "object",
"properties": {
"foo": {
"type": "string"
}
}
}
}
}
@lambda_handler.handle("get", path="/with-schema/", schema=my_schema)
def my_own_get(event):
return {"this": "will be json dumped"}
##### TEST #####
valid_input_event = {
"body": '{"foo":"bar"}',
"httpMethod": "GET",
"resource": "/with-schema/"
}
result = lambda_handler(event=valid_input_event)
assert result == {"body": '{"this": "will be json dumped"}', "statusCode": 200, "headers":{}}
invalid_input_event = {
"body": '{"foo":666}',
"httpMethod": "GET",
"resource": "/with-schema/"
}
result = lambda_handler(event=invalid_input_event)
assert result == {"body": '"Validation Error"', "statusCode": 400, "headers":{}}
Query params are also analyzed and validatable with JSON schemas. Query arrays are expected to be comma separated, all numbers are converted to floats.
from lambdarest import lambda_handler
my_schema = {
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"query":{
"type": "object",
"properties": {
"foo": {
"type": "array",
"items": {
"type": "number"
}
}
}
}
}
}
@lambda_handler.handle("get", path="/with-params/", schema=my_schema)
def my_own_get(event):
return event["json"]["query"]
##### TEST #####
valid_input_event = {
"queryStringParameters": {
"foo": "1, 2.2, 3"
},
"httpMethod": "GET",
"resource": "/with-params/"
}
result = lambda_handler(event=valid_input_event)
assert result == {"body": '{"foo": [1.0, 2.2, 3.0]}', "statusCode": 200, "headers":{}}
You can also specify which path to react on for individual handlers using the path
param:
from lambdarest import lambda_handler
@lambda_handler.handle("get", path="/foo/bar/baz")
def my_own_get(event):
return {"this": "will be json dumped"}
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"resource": "/foo/bar/baz"
}
result = lambda_handler(event=input_event)
assert result == {"body": '{"this": "will be json dumped"}', "statusCode": 200, "headers":{}}
And you can specify path parameters as well, which will be passed as keyword arguments:
from lambdarest import lambda_handler
@lambda_handler.handle("get", path="/foo/<int:id>/")
def my_own_get(event, id):
return {"my-id": id}
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"resource": "/foo/1234/"
}
result = lambda_handler(event=input_event)
assert result == {"body": '{"my-id": 1234}', "statusCode": 200, "headers":{}}
Or you can specify more complex parametrized resource path and get parameteres as arguments:
from lambdarest import lambda_handler
@lambda_handler.handle("get", path="/object/<int:object_id>/props/<string:foo>/get")
def my_own_get(event, object_id, foo):
return [{"object_id": int(object_id)}, {"foo": foo}]
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"path": "/v1/object/777/props/bar/get",
"resource": "/object/{object_id}/props/{foo}/get",
"pathParameters": {
"object_id": "777",
"foo":"bar"
}
}
result = lambda_handler(event=input_event)
assert result == {"body": '[{"object_id": 777}, {"foo": "bar"}]', "statusCode": 200, "headers":{}}
Or use the Proxy APIGateway magic endpoint:
from lambdarest import lambda_handler
@lambda_handler.handle("get", path="/bar/<path:path>")
def my_own_get(event, path):
return {"path": path}
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"path": "/v1/bar/baz",
"resource": "/bar/{proxy+}",
"pathParameters": {
"proxy": "bar/baz"
}
}
result = lambda_handler(event=input_event)
assert result == {"body": '{"path": "bar/baz"}', "statusCode": 200, "headers":{}}
In order to use it with Application Load Balancer you need to create your own lambda_handler and not use the singleton:
from lambdarest import create_lambda_handler
lambda_handler = create_lambda_handler(application_load_balancer=True)
@lambda_handler.handle("get", path="/foo/<int:id>/")
def my_own_get(event, id):
return {"my-id": id}
##### TEST #####
input_event = {
"body": '{}',
"httpMethod": "GET",
"resource": "/foo/1234/"
}
result = lambda_handler(event=input_event)
assert result == {"body": '{"my-id": 1234}', "statusCode": 200, "headers":{}, "statusDescription": "HTTP OK", "isBase64Encoded": False}
Because of python unittests leaky test-cases it seems like you shall beware of this issue when using the singleton lambda_handler
in a multiple test-case scenario.
You can use pytest to run tests against your current Python version. To run tests for current python version run pytest
See setup.py
for test dependencies and install them with .[test].
sloev, svdgraaf, simongarnier, elviejokike, paddie, AlbertoTrinidade, martinbuberl