
{"id":2012,"date":"2016-10-24T12:57:43","date_gmt":"2016-10-24T19:57:43","guid":{"rendered":"http:\/\/briantroy.com\/?p=2012"},"modified":"2025-01-03T04:44:00","modified_gmt":"2025-01-03T04:44:00","slug":"series-part-3-serverless-architecture-a-practical-implementation-serverless-rest-api","status":"publish","type":"post","link":"https:\/\/blogarchive.briantroy.com\/index.php\/2016\/10\/24\/series-part-3-serverless-architecture-a-practical-implementation-serverless-rest-api\/","title":{"rendered":"Series &#8211; Part 3: Serverless Architecture &#8211; a practical implementation: Serverless REST API"},"content":{"rendered":"<p>In <a href=\"http:\/\/briantroy.com\/2016\/10\/20\/series-part-2-serverless-architecture-a-practical-implementation-iot-device-data-collection-processing-and-user-interface\/\">part two of this series<\/a> I discussed creating a serverless data collection and processing fabric for an IoT deployment. To recap, we&#8217;ve now reviewed the local devices and controller\/gateway pattern for the security cameras deployed. We&#8217;ve also discussed the Amazon Web Services infrastructure deployed to collect, process and catalog the data generated by the security cameras.<\/p>\n<p>In this post we will cover the creation of a serverless REST API.<\/p>\n<p><!--more--><\/p>\n<p>The image below provides an overview of the serverless web application that will be implemented using this REST API:<\/p>\n<figure id=\"attachment_2024\" aria-describedby=\"caption-attachment-2024\" style=\"width: 1024px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2024 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/serverless-app-arch-001-2.png\" alt=\"serverless-app-arch-001\" width=\"1024\" height=\"768\" \/><figcaption id=\"caption-attachment-2024\" class=\"wp-caption-text\">Serverless Web Application &amp; REST API<\/figcaption><\/figure>\n<h2>Serverless REST API Creation<\/h2>\n<p>In the previous post we discussed getting our security videos and images to S3 and processing them via a Lambda function to generate a metadata catalog of the videos in DynamoDB. Now we can create a serverless REST API over the top of the data.<\/p>\n<h3>Creating a Lamdba Function for REST Services<\/h3>\n<p>As with the Lambda function created in part 2 of the series the first step is to create an IAM role enabling the access required. In order to do that we need to discuss the basic capabilities of the REST API Lambda function.<\/p>\n<ol>\n<li>Read the latest security videos &#8211; regardless of camera.<\/li>\n<li>Read the latest security videos for a particular camera.<\/li>\n<li>Generate singed URLs for the videos in S3 to allow playback.<\/li>\n<li>Log information to CloudWatch<\/li>\n<\/ol>\n<p>The first step is to create a new role &#8211; I called mine\u00a0<em>securityMicroserviceRole.\u00a0<\/em>Once the role is created you can create and assign each of the following policies.<\/p>\n<p>For all of the policies below you&#8217;ll want to replace the &#8220;Resource&#8221; ARN info with the ARN for your DynamoDB table or S3 Bucket.<\/p>\n<h4>Policy #1: Get the latest security videos<\/h4>\n<pre>{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"GetOnSecurityAlarmVideos\",\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"dynamodb:GetItem\",\n                \"dynamodb:BatchGetItem\",\n                \"dynamodb:Query\"\n            ],\n            \"Resource\": \"arn:aws:dynamodb:!!Your ARN Info!!:table\/security_video_timeline\"\n        }\n    ]\n}\n<\/pre>\n<h4 class=\"detail_tabs\">\u00a0Policy #2: Get the latest security videos by camera<\/h4>\n<pre>{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Sid\": \"GetOnSecurityAlarmVideos\",\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"dynamodb:GetItem\",\n                \"dynamodb:BatchGetItem\",\n                \"dynamodb:Query\"\n            ],\n            \"Resource\": \"arn:aws:dynamodb:!!Your ARN Info!!:table\/security_alarm_videos\"\n        }\n    ]\n}\n<\/pre>\n<h4>Policy #3: Generate singed URLs for videos in S3 allowing playback<\/h4>\n<pre>{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": [\n        \"s3:Get*\",\n        \"s3:List*\"\n      ],\n      \"Resource\": \"arn:aws:s3:!!Your ARN Info!!\/*\"\n    }\n  ]\n}\n<\/pre>\n<h4>Policy #4: Log information to CloudWatch<\/h4>\n<pre>{\n    \"Version\": \"2012-10-17\",\n    \"Statement\": [\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": \"logs:CreateLogGroup\",\n            \"Resource\": \"arn:aws:logs:!!Your ARN Info!!:*\"\n        },\n        {\n            \"Effect\": \"Allow\",\n            \"Action\": [\n                \"logs:CreateLogStream\",\n                \"logs:PutLogEvents\"\n            ],\n            \"Resource\": [\n                \"arn:aws:logs:!!Your ARN Info!!:log-group:\/aws\/lambda\/last5Videos:*\"\n            ]\n        }\n    ]\n}\n<\/pre>\n<p>Now we can create the Lambda function that will provide our REST API services.<\/p>\n<h4>Lambda Function Creation<\/h4>\n<p>The process is similar to what we did in part 2 of the series &#8211; but in this case we want our Lambda function to be triggered by the AWS API Gateway, not S3. Once again we will select the Blank Template.<\/p>\n<p>In the &#8220;Configure Triggers&#8221; section we will select API Gateway:<\/p>\n<figure id=\"attachment_2071\" aria-describedby=\"caption-attachment-2071\" style=\"width: 1978px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2071 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-21-14-33-32-2.png\" alt=\"screenshot-2016-10-21-14-33-32\" width=\"1978\" height=\"1200\" \/><figcaption id=\"caption-attachment-2071\" class=\"wp-caption-text\">Lambda Function Trigger<\/figcaption><\/figure>\n<p>You can use the default &#8220;Deployment stage&#8221; and\u00a0<em>AWS IAM\u00a0<\/em>in &#8220;Security&#8221;.<\/p>\n<p>In the &#8220;Configure Function&#8221; section we will name our function and select the Python runtime:<\/p>\n<figure id=\"attachment_2078\" aria-describedby=\"caption-attachment-2078\" style=\"width: 1922px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2078 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-21-14-35-51-2.png\" alt=\"screenshot-2016-10-21-14-35-51\" width=\"1922\" height=\"1114\" \/><figcaption id=\"caption-attachment-2078\" class=\"wp-caption-text\">Lambda Function Info<\/figcaption><\/figure>\n<p>Similar to in part 2 you will cut and <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/last5Videos.py\">paste the Lambda Function code found in GitHub here into the code box.<\/a><\/p>\n<p>In the &#8220;Lambda function handler and role&#8221; section we will select\u00a0<em>Choose an existing role<\/em> and then select the role created earlier in this post.<\/p>\n<p>In the advanced settings I would advise giving your function more execution time &#8211; 12 to 14 seconds should be sufficient for testing\/debugging. In our case (serverless) we do not need our Lambda function to connect to any EC2 instances. If you do require that select the VPC those instances reside in.<\/p>\n<p>Hit &#8220;Next&#8221; and &#8220;Create Function&#8221; and you have created the REST API Lambda Function.<\/p>\n<p><strong>Repeat these same Lambda Function Creation steps for the getCameraNames Lambda function <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/getCameraNames.py\">found in the GitHub repository here<\/a>.<\/strong><\/p>\n<p>For the moment we will skip testing the Lambda function and move on to configuring API Gateway to field API requests and invoke our function.<\/p>\n<h3>Configuring API Gateway for REST API Lambda Function<\/h3>\n<p>Before we start configuring our API it is worth an overview of the resources (API capabilities) we will expose:<\/p>\n<h4>GET \/cameras<\/h4>\n<p>The \/cameras resource will return a list of the camera names. This is done by walking the S3 bucket and finding all the camera names (see part 2 of the series for details). It is implemented by the following code in the <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/getCameraNames.py\">Lambda Function getCameraNames<\/a>:<\/p>\n<pre>\"\"\" Gets the current list of cameras by parsing the s3 bucket objects. \"\"\"\ndef lambda_handler(event, context):\n    \"\"\" Lambda Hander \"\"\"\n    import boto3\n\n    s3_client = boto3.client('s3')\n    items = s3_client.list_objects(Bucket='security-alarms', Prefix='patrolcams\/', Delimiter='\/')\n\n    out_list = []\n    for obj in items.get('CommonPrefixes', []):\n        camera = obj.get('Prefix')\n        camera = camera.replace('patrolcams\/', '')\n        camera = camera.replace('\/', '')\n        out_list.append(camera)\n        # end for\n\n    return out_list\n<\/pre>\n<h4>GET \/lastfive<\/h4>\n<p>The \/lastfive resource will return the last 5 (or any other configured number of) security videos regardless of camera name in order from newest to oldest. This method will also support optional GET parameters for:<\/p>\n<ul>\n<li>The number of videos to return &#8211; <em>num_results<\/em><\/li>\n<li>The date the video file was captured &#8211; <em>video_date<\/em><\/li>\n<\/ul>\n<p>It is implemented by the following code in the <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/last5Videos.py\">Lambda Function last5Videos<\/a>:<\/p>\n<pre>        # Must be a video timeline request\n\n        # defaults:\n        num_results = 10\n        video_date = time.strftime('%Y-%m-%d')\n        print(video_date)\n\n        if 'querystring' in event['params']:\n            if 'video_date' in event['params']['querystring']:\n                video_date = event['params']['querystring']['video_date']\n            # Fin\n            if 'num_results' in event['params']['querystring']:\n                num_results = int(event['params']['querystring']['num_results'])\n            # Fin\n        # Fin\n\n        # Execute the query\n\n        vid_table = dyndb.Table('security_alarm_videos')\n        response = vid_table.query(\n            TableName=\"security_video_timeline\",\n            Select='ALL_ATTRIBUTES',\n            KeyConditionExpression=Key('capture_date').eq(video_date),\n            ScanIndexForward=False,\n            Limit=num_results,\n        )\n<\/pre>\n<h4>GET \/lastfive\/{camera}<\/h4>\n<p>The \/lastfive\/{camera} resource will return the last 5 videos for the camera specified in the\u00a0<em>{camera}\u00a0<\/em>path variable (e.g,. \/lastfive\/office).\u00a0\u00a0It is implemented by the following code in the <a href=\"https:\/\/github.com\/briantroy\/sendftpfilestos3\/blob\/master\/lambda-functions\/last5Videos.py\">Lambda Function last5Videos<\/a>:<\/p>\n<pre>        camname = event['params']['path']['camera']\n        vid_table = dyndb.Table('security_alarm_videos')\n        response = vid_table.query(\n            TableName=\"security_alarm_videos\",\n            Select='ALL_ATTRIBUTES',\n            KeyConditionExpression=Key('camera_name').eq(camname),\n            ScanIndexForward=False,\n            Limit=5,\n        )\n<\/pre>\n<p>To summarize &#8211; our API will look like:<\/p>\n<pre>http:\/\/our.api.host.com\/api_name\/cameras\nhttp:\/\/our.api.host.com\/api_name\/lastfive\nhttp:\/\/our.api.host.com\/api_name\/lastfive\/{camera}\n<\/pre>\n<h4>Create the API<\/h4>\n<p>Open the AWS API Gateway console and select &#8220;Create API&#8221;.<\/p>\n<p>Select &#8220;New API&#8221; and provide an API name and description then press &#8220;Create API&#8221;.<\/p>\n<figure id=\"attachment_2144\" aria-describedby=\"caption-attachment-2144\" style=\"width: 1856px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2144 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-20-14-2.png\" alt=\"Screenshot 2016-10-24 11.20.14.png\" width=\"1856\" height=\"902\" \/><figcaption id=\"caption-attachment-2144\" class=\"wp-caption-text\">API Creation &#8211; API Gateway<\/figcaption><\/figure>\n<p>Now we can create our API resources &#8211; <em>lastfive<\/em> and <em>cameras<\/em>\u00a0&#8211; per the specification above.<\/p>\n<figure id=\"attachment_2150\" aria-describedby=\"caption-attachment-2150\" style=\"width: 1806px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2150 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-24-02-2.png\" alt=\"Screenshot 2016-10-24 11.24.02.png\" width=\"1806\" height=\"756\" \/><figcaption id=\"caption-attachment-2150\" class=\"wp-caption-text\">Create a Resource<\/figcaption><\/figure>\n<p>On the &#8220;Create Resource&#8221; screen fill in the resource name and resource path:<\/p>\n<figure id=\"attachment_2154\" aria-describedby=\"caption-attachment-2154\" style=\"width: 1860px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2154 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-26-06-2.png\" alt=\"Screenshot 2016-10-24 11.26.06.png\" width=\"1860\" height=\"1040\" \/><figcaption id=\"caption-attachment-2154\" class=\"wp-caption-text\">Resource Creation<\/figcaption><\/figure>\n<p>Also check &#8220;Enable API Gateway CORS&#8221; to allow API requests from other domains. Then click &#8220;Create Resource&#8221;.<\/p>\n<p>Initially the resource is created with only the OPTIONS method &#8211; we now need to create the GET method:<\/p>\n<figure id=\"attachment_2159\" aria-describedby=\"caption-attachment-2159\" style=\"width: 1328px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2159 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-29-42-2.png\" alt=\"screenshot-2016-10-24-11-29-42\" width=\"1328\" height=\"696\" \/><figcaption id=\"caption-attachment-2159\" class=\"wp-caption-text\">Create Resource Method<\/figcaption><\/figure>\n<figure id=\"attachment_2161\" aria-describedby=\"caption-attachment-2161\" style=\"width: 1298px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2161 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-29-56-2.png\" alt=\"screenshot-2016-10-24-11-29-56\" width=\"1298\" height=\"604\" \/><figcaption id=\"caption-attachment-2161\" class=\"wp-caption-text\">Add GET Method<\/figcaption><\/figure>\n<figure id=\"attachment_2163\" aria-describedby=\"caption-attachment-2163\" style=\"width: 1860px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2163 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-30-36-2.png\" alt=\"screenshot-2016-10-24-11-30-36\" width=\"1860\" height=\"940\" \/><figcaption id=\"caption-attachment-2163\" class=\"wp-caption-text\">Map GET Method to Lambda Function<\/figcaption><\/figure>\n<p>We then map our Resources GET method to our Lambda Function and press &#8220;Save&#8221;. You can then test the API resource &amp; method by selecting &#8220;Test&#8221;:<\/p>\n<figure id=\"attachment_2167\" aria-describedby=\"caption-attachment-2167\" style=\"width: 1860px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2167 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-39-36-2.png\" alt=\"screenshot-2016-10-24-11-39-36\" width=\"1860\" height=\"1100\" \/><figcaption id=\"caption-attachment-2167\" class=\"wp-caption-text\">Test new API Resource Method<\/figcaption><\/figure>\n<p>The test will output the HTTP response and camera list (provided your S3 bucket is created and the getCameraNames has the correct bucket and object path) as well as the logging of the request:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2173 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-42-30-2.png\" alt=\"Screenshot 2016-10-24 11.42.30.png\" width=\"1444\" height=\"378\" \/><\/p>\n<p>Now repeat these steps for the the\u00a0<i>lastfive <\/i>and the GET method for that resource. You should map this to the <em>securityRestAPI<\/em> Lambda function created above.<\/p>\n<figure id=\"attachment_2179\" aria-describedby=\"caption-attachment-2179\" style=\"width: 1860px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2179 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-11-47-13-2.png\" alt=\"Screenshot 2016-10-24 11.47.13.png\" width=\"1860\" height=\"1076\" \/><figcaption id=\"caption-attachment-2179\" class=\"wp-caption-text\">The\u00a0<i>lastfive<\/i> resource and GET method<\/figcaption><\/figure>\n<p>Testing this GET method however results in the following error:<\/p>\n<pre>{\n  \"stackTrace\": [\n    [\n      \"\/var\/task\/lambda_function.py\",\n      15,\n      \"lambda_handler\",\n      \"if 'camera' in event['params']['path']:\"\n    ]\n  ],\n  \"errorType\": \"KeyError\",\n  \"errorMessage\": \"'params'\"\n}\n<\/pre>\n<p>In order to fix this we need to configure the API Gateway to pass the correct information to our Lambda function.<\/p>\n<p>NOTE: This API was developed and deployed several months ago. Since that time AWS has enhanced the integration of API Gateway and Lambda. All of the methods described in this post are accurate as of the publish date.<\/p>\n<p>In order to do that we will need to configure the &#8220;Integration Request&#8221; section of the GET method:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-2191 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-04-59-2.png\" alt=\"Screenshot 2016-10-24 12.04.59.png\" width=\"2148\" height=\"1620\" \/><\/p>\n<p>At the bottom of this section select &#8220;Add mapping template&#8221; and type in the content type\u00a0<em>application\/json\u00a0<\/em>and select the check mark.<\/p>\n<figure id=\"attachment_2196\" aria-describedby=\"caption-attachment-2196\" style=\"width: 1500px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2196 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-07-23-2.png\" alt=\"screenshot-2016-10-24-12-07-23\" width=\"1500\" height=\"612\" \/><figcaption id=\"caption-attachment-2196\" class=\"wp-caption-text\">Body Mapping Template<\/figcaption><\/figure>\n<p>You will see a pop up asking that you secure the integration &#8211; click on &#8220;Yes, secure this integration&#8221;. Now we want to generate a template using &#8220;Method Request passthrough&#8221;:<\/p>\n<figure id=\"attachment_2200\" aria-describedby=\"caption-attachment-2200\" style=\"width: 1718px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2200 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-09-13-2.png\" alt=\"screenshot-2016-10-24-12-09-13\" width=\"1718\" height=\"1192\" \/><figcaption id=\"caption-attachment-2200\" class=\"wp-caption-text\">Method Request passthrough configuration<\/figcaption><\/figure>\n<p>The template will auto-generate (you don&#8217;t need to fill in the code box). Click &#8220;Save&#8221;, return to the\u00a0<i>lastfive\u00a0<\/i>resource GET method and test again. The test should be successful.<\/p>\n<p>We can now add the variable\u00a0<em>{camera}\u00a0<\/em>sub-resource of the\u00a0<i>lastfive<\/i> resource:<\/p>\n<figure id=\"attachment_2212\" aria-describedby=\"caption-attachment-2212\" style=\"width: 1178px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2212 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-13-47-2.png\" alt=\"screenshot-2016-10-24-12-13-47\" width=\"1178\" height=\"750\" \/><figcaption id=\"caption-attachment-2212\" class=\"wp-caption-text\">Create Child Resource of\u00a0<em>lastfive\u00a0<\/em>Resource<\/figcaption><\/figure>\n<figure id=\"attachment_2215\" aria-describedby=\"caption-attachment-2215\" style=\"width: 1724px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2215 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-14-12-2.png\" alt=\"Screenshot 2016-10-24 12.14.12.png\" width=\"1724\" height=\"1008\" \/><figcaption id=\"caption-attachment-2215\" class=\"wp-caption-text\">Configure Child Resource<\/figcaption><\/figure>\n<figure id=\"attachment_2217\" aria-describedby=\"caption-attachment-2217\" style=\"width: 2154px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2217 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-14-45-2.png\" alt=\"Screenshot 2016-10-24 12.14.45.png\" width=\"2154\" height=\"888\" \/><figcaption id=\"caption-attachment-2217\" class=\"wp-caption-text\">Configure GET method of\u00a0<em>{camera}\u00a0<\/em>child resource<\/figcaption><\/figure>\n<p>Before testing the child resource &#8211; make sure you configure &#8220;Integration Request&#8221; exactly like we did for the\u00a0<em>lastfive<\/em> resource.<\/p>\n<p>When you test the new\u00a0<em>lastfive\/{camera}<\/em> GET method you&#8217;ll be prompted to supply the camera name as follows:<\/p>\n<figure id=\"attachment_2226\" aria-describedby=\"caption-attachment-2226\" style=\"width: 1684px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2226 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-18-35-2.png\" alt=\"Screenshot 2016-10-24 12.18.35.png\" width=\"1684\" height=\"1350\" \/><figcaption id=\"caption-attachment-2226\" class=\"wp-caption-text\">Testing the GET Method &#8211; Supply Camera Name<\/figcaption><\/figure>\n<p>The last step it to configure the GET parameters on the\u00a0<em>lastfive<\/em><em>\u00a0<\/em>resource as follows:<\/p>\n<figure id=\"attachment_2231\" aria-describedby=\"caption-attachment-2231\" style=\"width: 2136px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2231 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-31-15-2.png\" alt=\"screenshot-2016-10-24-12-31-15\" width=\"2136\" height=\"1108\" \/><figcaption id=\"caption-attachment-2231\" class=\"wp-caption-text\">Click &#8220;Method Request&#8221; for the\u00a0<em>lastfive<\/em> Resource<\/figcaption><\/figure>\n<figure id=\"attachment_2234\" aria-describedby=\"caption-attachment-2234\" style=\"width: 2152px\" class=\"wp-caption alignnone\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-2234 size-full\" src=\"https:\/\/blogarchive.briantroy.com\/wp-content\/uploads\/2016\/10\/screenshot-2016-10-24-12-35-18-2.png\" alt=\"screenshot-2016-10-24-12-35-18\" width=\"2152\" height=\"1170\" \/><figcaption id=\"caption-attachment-2234\" class=\"wp-caption-text\">Query Parameter Configuration<\/figcaption><\/figure>\n<p>Expand the &#8220;URL Query String Parameters&#8221; section, click &#8220;Add query string&#8221; and create the\u00a0<em>num_results<\/em> and\u00a0<i>video_date<\/i> parameters.<\/p>\n<h2>Summary<\/h2>\n<p>We&#8217;ve now created a complete serverless REST API using AWS API Gateway and Lambda functions. This API gives applications access to the IoT device data <a href=\"http:\/\/briantroy.com\/2016\/10\/20\/series-part-2-serverless-architecture-a-practical-implementation-iot-device-data-collection-processing-and-user-interface\/\">we ingested into the system using serverless processing in part two of this series<\/a>.<\/p>\n<p>In <a href=\"http:\/\/briantroy.com\/2016\/10\/26\/series-part-4-serverless-architecture-a-practical-implementation-securing-a-serverless-rest-api\/\">part four of the series we will secure the REST API and put it in production<\/a> so we can build applications using it (in part 5).<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In part two of this series I discussed creating a serverless data collection and processing fabric for an IoT deployment. To recap, we&#8217;ve now reviewed the local devices and controller\/gateway pattern for the security cameras deployed. We&#8217;ve also discussed the Amazon Web Services infrastructure deployed to collect, process and catalog the data generated by the&hellip; <a class=\"more-link\" href=\"https:\/\/blogarchive.briantroy.com\/index.php\/2016\/10\/24\/series-part-3-serverless-architecture-a-practical-implementation-serverless-rest-api\/\">Continue reading <span class=\"screen-reader-text\">Series &#8211; Part 3: Serverless Architecture &#8211; a practical implementation: Serverless REST API<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":1744,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9,11,18,19,28],"tags":[236],"_links":{"self":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2012"}],"collection":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/comments?post=2012"}],"version-history":[{"count":2,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2012\/revisions"}],"predecessor-version":[{"id":3328,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/posts\/2012\/revisions\/3328"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/media\/1744"}],"wp:attachment":[{"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/media?parent=2012"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/categories?post=2012"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogarchive.briantroy.com\/index.php\/wp-json\/wp\/v2\/tags?post=2012"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}