1552639495
#image #aws #api
1552640544
In this guide we’ll be building an image processing service on AWS Lambda and API Gateway.
Now with that out of the way, let’s see how you can get a simple Python 3.6 application running that
accessible through an API that
all without
*Unless your traffic exceeds the generous Lambda free pricing tier.
First make sure you have Docker installed.
For this example we’ll use a service from AWS called Lambda that allows us to deploy our function and its dependencies and easily connect it to an API. In order to create the API we’ll use API Gateway — service also provided by AWS.
For the simplicity of this tutorial we’ll deploy our code by uploading it to Lambda via the AWS Web Console. We’ll also write our function code inside the AWS console to keep things simple. In any serious case you would do deployments via AWS CLI.
We’re naming our function lambda-demo
. Make sure to pick Python 3.6
as the Runtime and create a new role from AWS policy templates.
import json
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
You can invoke this function right away by configuring a test event. Click on Test and configure the first test event. For the purposes of this article, the default tempate works fine.
After creating the test event, click on Test. You should receive the following in the function logs:
{
"statusCode": 200,
"body": "\"Hello from Lambda!\""
}
Brilliant.
Now let’s create something more useful than that.
Let’s build a function that takes an image as input and turns it to grayscale. We will be using OpenCV for that, specifically its Python bindings. Although using OpenCV might be overkill for such a task, it demonstrates how such a useful library can be included in your Lambda environment with relative ease.
We will now
I’ve put together a dead simple tool — a Docker image that can gather any pip
package and generate a .ZIP we can upload to Lambda Layers. If you want to explore the tool you can find it from LambdaZipper.
If you have Docker installed you can open your terminal and just run
docker run --rm -v $(pwd):/package tiivik/lambdazipper opencv-python
That’s it! In your current working directory you’ll find opencv-python.zip
One of the most useful serverless toolkits is serverless. However we are not going to use it in this example. Re-inventing the wheel is rarely a good idea, with an exception when you want to learn how things work under the hood. Although mature frameworks such as serverless exist, it is a good idea to dig into some of the core functionalities these frameworks abstract.
Let’s explore what the tool abstracted from us.
If you take a look at package.sh then you can see that it performed a pip install
command with opencv-python
argument. All that was executed in amazonlinux:2017.03
environment that, to some extent, mimics the AWS Lambda environment. You can explore the execution environment in the Dockerfile.
Let’s upload the opencv-python.zip
to Lambda Layers so we can use that package from now on in all our functions. Think of Layers as data that can be used in any function you write. This can be Python modules, code snippets, binary files or anything.
Navigate to Layers panel in AWS Lambda and press Create layer.
Set up the layer name, description and upload the zip file. Make sure to select the correct runtime, in our case Python 3.6. Press Create layer.
As of writing this article, uploading the ZIP file from the web interface is limited to 50MB. Fortunately our opencv-python
package is less than that. In case your package exceeds that you can provide the package as a link from an S3 bucket. Bear in mind that Lambda sets deployment package limit at 250MB.
After creating the function you should be greeted with a message
Successfully created layer opencv-python version 1.
Yay!
Let’s go back to our lambda-demo
function and add the opencv-python
layer to our function execution environment. Click on Layers > Add a layer and select your opencv-python
layer.
Let’s try importing the library as usual:
import json
import cv2
def lambda_handler(event, context):
# TODO implement
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
Now let’s click on Test. We’re provided with the response:
Response:
{
"errorMessage": "Unable to import module 'lambda_function'"
}
For some reason Lambda wasn’t able to find our Python package. Let’s explore.
By default all Lambda layers are mounted to /opt
. Let’s comment out our cv2
import and take a look what’s inside /opt
.
import json
#import cv2
from os import listdir
def lambda_handler(event, context):
# TODO implement
print(listdir("/opt"))
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
In the function logs we can see our cv2
module in /opt
.
[‘bin’, ‘cv2’, ‘numpy’, ‘numpy-1.16.2.dist-info’, ‘opencv_python-4.0.0.21.dist-info’]
By default /opt/bin
is added to the $PATH
environment variable. You can reference that from AWS docs. However our layer modules exist in /opt/
not in/opt/bin
. So, let’s include /opt
into$PATH
as well so Lambda can see our package.
In Environment Variables section, add the following environment variable. Key: PYTHONPATH
Value: /opt/
Typically you can just import the package without altering the path, but in this case it’s necessary for Lambda environment to detect our package.
Let’s modify our code:
import json
import cv2
def lambda_handler(event, context):
# TODO implement
print(cv2.__version__)
return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}
Save your changes and click Test. We are greeted with4.0.0
in the console, informing us the OpenCV version used.
Brilliant, Python OpenCV running in Lambda!
Let’s continue implementing the core application logic — converting images into grayscale. Let’s modify our Lambda function code:
import json
import cv2
import base64
def write_to_file(save_path, data):
with open(save_path, "wb") as f:
f.write(base64.b64decode(data))
def lambda_handler(event, context):
# Write request body data into file
write_to_file("/tmp/photo.jpg", event["body"])
# Read the image
image = cv2.imread("/tmp/photo.jpg")
# Convert to grayscale
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Write grayscale image to /tmp
cv2.imwrite("/tmp/gray.jpg", gray)
# Convert grayscale image into utf-8 encoded base64
with open("/tmp/gray.jpg", "rb") as imageFile:
str = base64.b64encode(imageFile.read())
encoded_img = str.decode("utf-8")
# Return the data to API Gateway in base64.
# API Gateway will handle the conversion back to binary.
# Set content-type header as image/jpeg.
return {
"isBase64Encoded": True,
"statusCode": 200,
"headers": { "content-type": "image/jpeg"},
"body": encoded_img
}
The API which we will set up in a moment will accept a binary image from the client. The binary image will then be converted into base64 by AWS API Gateway and passed into the Lambda.
Of course the API Gateway is not set up yet so testing this code with our current test will fail. However, before we move into setting up an API that invokes this Lambda, we can test it in the Lambda console by providing a base64 encoded image in the event body.
Reconfigure the Test with this body. In case you’re wondering, this is a base64 encoded cat photo 😺 https://imgur.com/a/0NpkzzL
{
"body" : "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AAAAAAtjcm9wUmVjdFRvcGxvbmcAAAAAADhCSU0D7QAAAAAAEACQAAAAAQACAJAAAAABAAI4QklNBCYAAAAAAA4AAAAAAAAAAAAAP4AAADhCSU0EDQAAAAAABAAAAB44QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAAAAAABADhCSU0nEAAAAAAACgABAAAAAAAAAAI4QklNA/UAAAAAAEgAL2ZmAAEAbGZmAAYAAAAAAAEAL2ZmAAEAoZmaAAYAAAAAAAEAMgAAAAEAWgAAAAYAAAAAAAEANQAAAAEALQAAAAYAAAAAAAE4QklNA/gAAAAAAHAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAOEJJTQQIAAAAAAAQAAAAAQAAAkAAAAJAAAAAADhCSU0EHgAAAAAABAAAAAA4QklNBBoAAAAAA3cAAAAGAAAAAAAAAAAAAABAAAAAQAAAACEAUwBjAHIAZQBlAG4AcwBoAG8AdAAgADIAMAAxADkALQAwADMALQAxADAAIABhAHQAIAAwADAALgAyADYALgAyADcAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAABAAAAAAFJnaHRsb25nAAAAQAAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABAAAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAGb3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQAAAApFU2xpY2VUeXBlAAAAAEltZyAAAAAGYm91bmRzT2JqYwAAAAEAAAAAAABSY3QxAAAABAAAAABUb3Ag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MCIgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSIyOERCRTU2MDNFOTg1OTU0NjZEQjg5ODVFRTU2MEI0MCIgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0iZi5sdXggcHJvZmlsZSIgeG1wOkNyZWF0ZURhdGU9IjIwMTktMDMtMTBUMDA6MjY6MjkrMDI6MDAiIHhtcDpNb2RpZnlEYXRlPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIiB4bXA6TWV0YWRhdGFEYXRlPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6MDE4MDExNzQwNzIwNjgxMTgyMkE5MEM0MkExQjY0QzAiIHN0RXZ0OndoZW49IjIwMTktMDMtMTBUMDA6Mjc6MzArMDI6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCBDUzYgKE1hY2ludG9zaCkiIHN0RXZ0OmNoYW5nZWQ9Ii8iLz4gPHJkZjpsaSBzdEV2dDphY3Rpb249InNhdmVkIiBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTE4MjJBOTBDNDJBMUI2NEMwIiBzdEV2dDp3aGVuPSIyMDE5LTAzLTEwVDAwOjI3OjMwKzAyOjAwIiBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQaG90b3Nob3AgQ1M2IChNYWNpbnRvc2gpIiBzdEV2dDpjaGFuZ2VkPSIvIi8+IDwvcmRmOlNlcT4gPC94bXBNTTpIaXN0b3J5PiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg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"
}
Invoking this test should now succeed with:
Response:
{
"isBase64Encoded": true,
"statusCode": 200,
"headers": {
"content-type": "image/jpeg"
},
"body": "/9j/4AJRgAB.....P+WqHNf//Z" <- long base64 string of black and white image here
}
We’re now ready to set up an API that invokes this Lambda function.
Open AWS API Gateway console. Press Create API.
Create a new REST API, provide API name and description. In this case we’re calling our API lambda-demo
.
From Resources > Actions choose Create Method and define a POST method.
For the Integration type choose Lambda Function and pick your Lambda function from the dropdown menu. Enable Use Lambda Proxy integration and hit Save.
We want our API to be able to handle binary data.
From Settings > Binary Media Types click Add Binary Media Type and define the binary types as:
image/jpeg
image/png
*/*
Press Save Changes.
Navigate back to our POST method.
Under Method Response add a Content-Type
Response Header and set the type to image/jpeg
:
Before publishing the API you can test it by clicking on the Client Test button:
In this case we are providing the base64 image body itself, not a json object. For convenience you can try pasting the raw base64 string from the following link into the Request Body field cat_base64_body.
Response is a base64 string of the black and white photo.
Click on Actions >Deploy API.
Create a new Deployment stage, give it a descriptive name, for example development
and press Deploy.
The API is now live and functional! You’ll receive a url where your API is deployed:
[[https://XXXXX.execute-api.XXXX.amazonaws.com/development](https://XXXXX.execute-api.eu-central-1.amazonaws.com/development)](https://XXXXX.execute-api.XXXX.amazonaws.com/development](https://XXXXX.execute-api.eu-central-1.amazonaws.com/development) "https://XXXXX.execute-api.XXXX.amazonaws.com/development](https://XXXXX.execute-api.eu-central-1.amazonaws.com/development)")
Let’s try it out!
curl https://i.imgur.com/offvirS.jpg -o kitty.jpg
curl -X POST --data-binary @kitty.jpg https://XXXXX.execute-api.eu-central-1.amazonaws.com/development -o kitty_bw.jpg
Bear in mind that for the simplicity of this tutorial there is no error handling, request validation or authorization set up. For a production application these should be set up in API Gateway and your Lambda function code.
That’s it, I hope you found this guide useful!
☞ AWS Certified Solution Architect Associate
☞ Computer Vision Using OpenCV
☞ An A-Z of useful Python tricks
☞ A Complete Machine Learning Project Walk-Through in Python
☞ Learning Python: From Zero to Hero
☞ Build a Serverless App with AWS Lambda - Hands On!
☞ Amazon Web Services - Web Hosting & Cloud Computing With AWS
☞ Amazon Web Services - Learn 7 AWS Services in 7 days
☞ Complete Python Bootcamp: Go from zero to hero in Python 3
☞ Machine Learning A-Z™: Hands-On Python & R In Data Science
1552639495
1626931020
AWS API Gateway + Lambda /w TypeScript
#aws api #api gateway #api #aws #typescript #lambda
1599061560
APIs are a crucial part of any web application and there are different techniques for development and design. Serverless is one approach gaining popularity, because of its cost-efficiency, scalability and relative simplicity. As a leading serverless provider, Amazon Web Services (AWS) has made a huge contribution to the world of serverless development, and in this article, we will explain general API implementation concepts using AWS Lambda and other AWS services.
AWS Lambda is an AWS service that is responsible for running particular functions in response to particular triggers — events happening in the application. Those triggers could be HTTP calls; events from other AWS services like S3, Kinesis, or SNS; or just recurrent scheduled events. Functions are executed in some type of ephemeral containers, which are fully provisioned and scaled by AWS, so the development team can focus more on the code and functionality than on infrastructure.
Another attractive feature is the pay-as-you-go payment model, where you are charged only for the total execution time of your functions and do not pay for idle time. Of course, like any other service, Lambda has limits and is sometimes not suitable for certain tasks — such as very long-running jobs, heavy computing jobs, or processes that require control over the execution environment. However, AWS Lambda usually works perfectly for implementing APIs.
AWS API Gateway is a service allowing developers to create and manage HTTP endpoints, map them to particular AWS resources, and configure custom domains, authorizing mechanisms, caching and other features. API Gateway is the fundamental part of serverless API, because it is responsible for the connection between a defined API and the function handling requests to that API.
As mentioned, API Gateway includes a lot of functionality and integrations. At some point, though, Amazon realized that serverless developers usually do not require all of those features, but instead need a general simplification of the implementation process. That is probably why in late 2019, AWS announced the new HTTP APIs, a lite version of API Gateway, which dramatically simplifies the developer experience and provides better performance and lower costs for serverless APIs. Although it is simple, HTTP APIs still support such important features as configuring CORS for all endpoints, JWT integration, custom domains and VPC connections.
In order to easily understand the main concepts of serverless API implementation, we’ll build a very minimalistic example of a simple “virtual whiteboard” application, consisting of two simple endpoints: POST for writing messages on a whiteboard, and GET for fetching the three most recent messages. We will also consider other possible features — like path parameters, CORS, and authorizers — but we’ll keep the final implementation simple and clear to read.
We will make our project completely serverless, by using AWS DynamoDB for storing messages. This database corresponds to serverless principles, is easy to use, and offers a pay-per-request model that is really cost-effective. DynamoDB is a NoSQL key-value database offered by AWS, where your data is stored across AWS servers and fully managed by Amazon.
In order to continue further implementation, you’ll need an AWS account and AWS Serverless Application Model (SAM) installed and configured. SAM is a tool for creating, updating, and managing serverless applications and all the resources needed for the application to operate. With AWS SAM, you don’t need to create every single service manually via web console, but just to describe all the things needed in the special template file.
After you’ve installed the CLI, navigate to the directory you are going to work in and run this command:
$ sam init -r nodejs12.x -n whiteboard
Initializing new project
Select the first option, then select “Quick Start from Scratch.” This will create a “whiteboard” directory with a minimum of setup files inside.
#api management #aws #api #lambda #aws gateway #amazon web services
1614145832
It’s 2021, everything is getting replaced by a technologically emerged ecosystem, and mobile apps are one of the best examples to convey this message.
Though bypassing times, the development structure of mobile app has also been changed, but if you still follow the same process to create a mobile app for your business, then you are losing a ton of opportunities by not giving top-notch mobile experience to your users, which your competitors are doing.
You are about to lose potential existing customers you have, so what’s the ideal solution to build a successful mobile app in 2021?
This article will discuss how to build a mobile app in 2021 to help out many small businesses, startups & entrepreneurs by simplifying the mobile app development process for their business.
The first thing is to EVALUATE your mobile app IDEA means how your mobile app will change your target audience’s life and why your mobile app only can be the solution to their problem.
Now you have proposed a solution to a specific audience group, now start to think about the mobile app functionalities, the features would be in it, and simple to understand user interface with impressive UI designs.
From designing to development, everything is covered at this point; now, focus on a prelaunch marketing plan to create hype for your mobile app’s targeted audience, which will help you score initial downloads.
Boom, you are about to cross a particular download to generate a specific revenue through your mobile app.
#create an app in 2021 #process to create an app in 2021 #a complete process to create an app in 2021 #complete process to create an app in 2021 #process to create an app #complete process to create an app
1601381326
We’ve conducted some initial research into the public APIs of the ASX100 because we regularly have conversations about what others are doing with their APIs and what best practices look like. Being able to point to good local examples and explain what is happening in Australia is a key part of this conversation.
The method used for this initial research was to obtain a list of the ASX100 (as of 18 September 2020). Then work through each company looking at the following:
With regards to how the APIs are shared:
#api #api-development #api-analytics #apis #api-integration #api-testing #api-security #api-gateway