AWS makes it easy to set up a REST service with authentication using Lambda, the AWS API Gateway, and IAM. Using these technologies through AWS doesn’t require hosting cost for the Lambda and API Gateway service and you pay per Lambda call. You also benefit from Lambda auto-scaling depending on the request volume and concurrency.
We’ll use Java in the example below where we create a simple REST service to evaluate if a number is prime or not.
This function will implement the Lambda handler function.
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
public class Hello {
public String myHandler(int input, Context context) {
LambdaLogger logger = context.getLogger();
StringBuilder result = new StringBuilder("");
result.append("Input Parameter " + input + " is ");
if(isPrime(input)) {
result.append("a PRIME");
} else {
result.append("NOT a PRIME");
}
logger.log(result.toString());
return result.toString();
}
private boolean isPrime(long n) {
if(n < 2) return false;
if(n == 2 || n == 3) return true;
if(n%2 == 0 || n%3 == 0) return false;
long sqrtN = (long)Math.sqrt(n)+1;
for(long i = 6L; i <= sqrtN; i += 6) {
if(n%(i-1) == 0 || n%(i+1) == 0) return false;
}
return true;
}
}
When creating the Lambda function, specify a handler that will be invoked when a Lambda function is triggered. There are 2 invocation methods supported by Lambda: Event and RequestResponse. In our example, we’ll use the RequestResponse invocation method over HTTPS using API Gateway.
In this example, we invoke the myHandler Java function over REST using the API Gateway. The first parameter within that function–(int input)–is the input to the handler sent through the REST service. Even though we are using an int Java data type in this example, the input could be any simple Java type, POJO, or Stream type.
The second parameter--Context context--is a Context object and allows us to interact with the Lambda execution environment. Here we are using it to get the CloudWatch log stream associated with the Lambda function. The private function, isPrime, helps to determine if the input is a prime number or not. The last two lines of the myHandler function outputs the result of the function to CloudWatch and returns that output to the invoker (REST Service) of the myHandler function.
Your deployment package can be a .zip file or a standalone .jar. In our example, we will be creating a standalone .jar using the Maven Shade Plugin. You will need to create a Maven pom.xml with the ‘aws-lambda-java-core’ dependency and use the Maven Shade Plugin to build a standalone .jar as shown below. After this, you will need to create a Lambda function through the AWS console.
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-lambda-java-core</artifactId>
<version>1.1.0</version>
</dependency>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
When you package and upload this code to create your Lambda function, you will specify the package.class-name::handler method reference as the handler.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
}
]
}
Now, let’s create an REST service to invoke this Lambda function using API Gateway.
You must have assigned API Gateway access permission (like AmazonAPIGatewayInvokeFullAccess) to an IAM user. The IAM user also needs full access to work with Lambda (AWSLambdaBasicExecutionRole or AWSLambdaFullAccess).
In any rest API client, test this API by passing the Invoke URL and integer number as a parameter within the REST request body.
https://my-api-id.execute-api.region-id.amazonaws.com/production/prime
Now that we have created REST service using API Gateway, lets figure out how to setup an authentication mechanism using IAM.
Now, a user who has the AmazonAPIGatewayInvokeFullAccess policy through IAM can only invoke this REST service. To authenticate, you will need to pass your AWS access key and secret key. Below is a standard AmazonAPIGatewayInvokeFullAccess IAM policy. If you want to further restrict the user to only access a specific API Gateway REST service, update the resource section of the IAM policy to reference the specific resource.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"execute-api:Invoke"
],
"Resource": "arn:aws:execute-api:*:*:*"
}
]
}
Now before we can call this REST service from other applications outside of this domain, we need to enable CORS for this service.
Follow this Amazon documentation that explains how to enable CORS for a service created through API Gateway.
Now we can call this REST service using JavaScript through a web application.
Follow the steps provided on this Generate an SDK for an API in API Gateway page to setup JavaScript SDK.
Make sure you follow the steps to enable CORS described earlier. Once you generate the JavaScript SDK, include the JavaScript libraries generated as shown below.
<script type="text/javascript" src="lib/axios/dist/axios.standalone.js"></script>
<script type="text/javascript" src="lib/CryptoJS/rollups/hmac-sha256.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/hmac.js"></script>
<script type="text/javascript" src="lib/CryptoJS/components/enc-base64.js"> </script>
<script type="text/javascript" src="lib/moment/moment.js"></script>
<script type="text/javascript" src="lib/url-template/url-template.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/sigV4Client.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/apiGatewayClient.js"></script>
<script type="text/javascript" src="lib/apiGatewayCore/utils.js"></script>
<script type="text/javascript" src="apigClient.js"></script >
Then, write JavaScript using the apigClient object to interact with the REST service created. To pass the access key and secret key, you need to create new client using following code assuming elements accessKey and secretKey are HTML inputs with the AWS access key and secret key.
var apigClient = apigClientFactory.newClient({
accessKey: document.getElementById("accessKey").value,
secretKey: document.getElementById("secretKey").value,
});
You can review the Amazon documentation on this here.
Note: If you created the API Gateway REST service without IAM authentication, anyone can invoke it. You can pass any number through an HTML element and any additional parameters you need to invoke the service as shown below.
var body = document.getElementById("number").value;
var params = {
// This is where any modeled request parameters should be added.
// The key is the parameter name, as it is defined in the API in API Gateway.
//param0: '',
//param1: ''
};
var additionalParams = {
// If there are any unmodeled query parameters or headers that must be
// sent with the request, add them here.
headers: {
//param0: '',
//param1: ''
},
queryParams: {
//param0: '',
//param1: ''
}
};
Finally, call the REST Service by passing in the parameters, which contain the integer (params, body) that will be evaluated by the service and any other additionalParams as shown below. The result will update the HTML element res with the REST service output.
apigClient.prime(params, body, additionalParams)
.then(function(result){
res.innerHTML = JSON.stringify(result);
}).catch(function(result){
res.innerHTML = "Sorry, API Gateway is not responding";
});
By using AWS, you can quickly create a simple REST service with auto scaling and integrated authentication in a very short amount of time. This solution is one of the ways to implement services that conform to the Microservice Architecture.