Serverless Upload API

Serverless most often refers to applications that don't require you to provision or manage any servers. You can focus on your core product and business logic instead of responsibilities like operating system (OS) access control, OS patching, provisioning, right-sizing, scaling, and availability.

It's a revolution in the current technological trend. A detailed post on serverless can be found below:

AWS Serverless
Let’s see what does Serverless means!

Today, we will see how to create a serverless upload API using Serverless and AWS Java SDK


Prerequisites

Bootstrap serverless project
  • The first step is to bootstrap the serverless project
    Use the below code to generate the bootstrapped codebase
serverless create --template aws-java-maven --name uploads-api -p uploads-api
  • Update the pom file with the correct artifactId and name and package name as per requirement
  • Try building the application

You should see the application build successfully with the command -
mvn clean package


Restructure codebase
  • Restructure the app to look something like below
  • Folder details -
    dto - Data Transfer Objects(POJO) goes here
    entity - Bean Java POJO goes here
    handler - Lambda Handler goes here
    service - Service Interface and Implementation goes here
    utility - The utility code goes here

Create the Handler
  • We will create a new handler CreatePersignedURLHandler under the handler package
  • Handler is the entry point for the Lambda function

Create S3 Utility

Now we will create the most important part of the code which will help us create the Presigned URL

  • First, add the required dependency
<dependency>
    <groupId>com.amazonaws</groupId>
    <artifactId>aws-java-sdk-s3</artifactId>
    <version>1.12.249</version>
</dependency>
  • Next, we will create a class singleton S3Util
  • This will contain a function createPresignedURL which accepts - bucket name, key/name of the file, and time to live for the presigned URL

Add Service
  • Let's first create an Interface called UploadService which will hold the contract for generating the presigned URL
public interface UploadService {
    AppResponse processCreatePresignedURL(Map<String, Object> input);
}
  • Next, we will implement this Interface for actual functionality
    For detailed code see -
serverless-uploads-api/UploadServiceImpl.java at main · tirthankarkundu17/serverless-uploads-api
Contribute to tirthankarkundu17/serverless-uploads-api development by creating an account on GitHub.
  • Now we will call this service from our handler
  • The response from the service will be responded to calling the client

Serverless Config

Finally, we need the serverless.yml config which will create the necessary resources in AWS for our serverless stack

Our serverless.yml will look like this -

# This name will appear in AWS cloud formation
service: uploads-api

# Serverless Framework Version
frameworkVersion: '3'
custom:
  bucketName: test-upload-bucket123
  region: us-east-2

# General Lambda configuration goes here
provider:
  name: aws
  runtime: java8.al2 # AWS Linux 2 Corretto 8
  stage: dev
  region: ${self:custom.region}
  # IAM Policies for the function goes here
  iam:
    role:
      statements:
        - Effect: "Allow"
          Action:
            - "s3:*"
          Resource: !Sub
            - arn:aws:s3:::${self:custom.bucketName}/*
            - BucketName: !Ref UploadBucket

# Package artifact to use for deployment
package:
  artifact: target/uploads-api-dev.jar

# Lambda function configuration
functions:
  hello:
    # Handler location
    handler: in.bitmaskers.handler.GetPresignedURLHandler
    # Environment variables
    environment:
      BUCKET_NAME: ${self:custom.bucketName}
      REGION: ${self:custom.region}
    # Use AWS Gateway HTTP API as lambda trigger
    events:
      - httpApi:
          path: /create-presigned-url # Path of API
          method: post # Method type

# Cloudformation Resource Template
resources:
  Resources:
    UploadBucket:
      Type: AWS::S3::Bucket
      Properties:
        BucketName: ${self:custom.bucketName}

The comments added will give details on each node in yml


Deployment

Deployment is the easiest part for a serverless app since everything is managed by AWS.

After configuring everything, we need to package our app and deploy it.

  • Package the app
mvn clean package
  • Serverless deploy
sls deploy

We will see stack getting created and deployed


Let's take it for a spin 🚀
  • Pass required param as JSON and do a POST call
  • Use the presigned URL returned to do another PUT operation with the binary file. We will receive a 200 status code, which means the file was uploaded successfully 🎊
  • PUT call on presigned URL after TTL expiry throws error as below
  • We can now see the file created in S3

That's all folks! Pretty lengthy blog but hope you liked it.

Cheer! 🍻

Tirthankar Kundu

Tirthankar Kundu