How to perform CRUD Operations via API Using Kotlin Micronaut with AWS
Building a CRUD REST API with Kotlin Micronaut, AWS Lambda, API Gateway, DynamoDB, and S3
Introduction
In this blog, we'll walk you through the process of building a CRUD REST API using Kotlin Micronaut, deploying it on AWS Lambda, and integrating it with AWS API Gateway, DynamoDB, and S3.
Pre-requisites
- Java JDK 11 or higher (Amazon Corretto recommended)
- Micronaut CLI (mn-cli, Latest Version)
- AWS Primary Account with root user access
- Need to install & configure AWS command line interface named aws-cli
Steps
1. Firstly install & verify Java JDK(Amazon Corretto), Micronaut CLI & AWS CLI
C:\Users\HP>D:
D:\>cd Prabha
D:\Prabha>mkdir car
D:\Prabha>cd car
# Verify Java installation
D:\Prabha\car>java --version
openjdk 11.0.16.1 2022-08-12 LTS
OpenJDK Runtime Environment Corretto-11.0.16.9.1 (build 11.0.16.1+9-LTS)
OpenJDK 64-Bit Server VM Corretto-11.0.16.9.1 (build 11.0.16.1+9-LTS, mixed mode)
# Verify Micronaut installation
D:\Prabha\car>mn --version
Micronaut Version: 3.7.2
# Verify AWS CLI installation
D:\Prabha\car>aws --version
aws-cli/2.8.3 Python/3.9.11 Windows/10 exe/AMD64 prompt/off
2. Create a Micronaut Application
Create a new Micronaut application using the Micronaut CLI via Command Prompt:
C:\Users\HP>D:
D:\>cd Prabha
D:\Prabha>cd car
D:\Prabha\car>mn
mn> create-app cars --lang kotlin --build gradle --test junit
| Application created at D:\Prabha\car\cars
mn> exit
Open the project folder in IntelliJ IDEA. Define the base package and create sub-packages: config
, model
, dao
, and controller
.
3. Configure Build File
Edit the build.gradle
file to set the runtime to "lambda" and add dependencies for DynamoDB and GSON:
micronaut { runtime("lambda") } dependencies { implementation("software.amazon.awssdk:dynamodb-enhanced:2.17.261") implementation("software.amazon.awssdk:dynamodb:2.17.261") implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.9' }
4. Configure DynamoDB
Create a DynamoDbConfig.kt
class in the config
package:
package cars.config
import io.micronaut.context.annotation.Bean
import io.micronaut.context.annotation.Factory
import jakarta.inject.Singleton
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient
import software.amazon.awssdk.services.dynamodb.DynamoDbClient
@Factory
class DynamoDbConfig {
@Singleton
fun dynamoDBClient(): DynamoDbClient = DynamoDbClient.builder().build()
@Bean
@Singleton
fun dynamoDBEnhancedClient(): DynamoDbEnhancedClient {
return DynamoDbEnhancedClient.builder().dynamoDbClient(dynamoDBClient()).build()
}
}
5. Define Model Class
Create a Car.kt
data class in the model
package:
package cars.model
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey
@DynamoDbBean
data class Car(
@get:DynamoDbPartitionKey
var car_id: String? = null,
var car_name: String? = null,
var car_brand: String? = null,
var car_price: String? = null
)
6. Define DAO Class
Create a CarDao.kt
class in the dao
package:
package cars.dao
import cars.model.Car
import jakarta.inject.Inject
import jakarta.inject.Singleton
import software.amazon.awssdk.enhanced.dynamodb.DynamoDbEnhancedClient
import software.amazon.awssdk.enhanced.dynamodb.TableSchema
import software.amazon.awssdk.enhanced.dynamodb.model.PageIterable
@Singleton
data class CarDao(
@Inject private val dynamoClient: DynamoDbEnhancedClient
) {
private val carTable = dynamoClient.table("car_info", TableSchema.fromBean(Car::class.java))
fun save(car: Car) {
return carTable.putItem(car)
}
fun update(car: Car): Car {
return carTable.updateItem(car)
}
fun delete(car: Car): Car {
return carTable.deleteItem(car)
}
fun getById(car: Car): Car {
return carTable.getItem(car)
}
fun getAll(): MutableList<Car> {
val carIdList: PageIterable<Car> = carTable.scan()
val carIdWholeList = mutableListOf<Car>()
carIdList.stream().forEach { fetch -> fetch.items().forEach { pay -> carIdWholeList.add(pay) } }
return carIdWholeList
}
}
7. Define Controller Class
Create a CarController.kt
class in the controller
package:
package cars.controller
import cars.dao.CarDao
import cars.model.Car
import com.google.gson.GsonBuilder
import io.micronaut.http.HttpResponse
import io.micronaut.http.annotation.*
import jakarta.inject.Inject
@Controller("/cars")
data class CarController(
@Inject private val carDao: CarDao
) {
companion object {
private val mapper = GsonBuilder().setPrettyPrinting().create()
}
@Post("/add")
fun addCar(@Body car: Car): HttpResponse<String> {
carDao.save(car)
return HttpResponse.ok("Saved Car Id is ${car.car_id} ok!")
}
@Put("/update")
fun updateCar(@Body car: Car): HttpResponse<String> {
carDao.update(car)
return HttpResponse.ok("Modified Car Id is ${car.car_id} ok!")
}
@Delete("/remove")
fun deleteCar(@Body car: Car): HttpResponse<String> {
carDao.delete(car)
return HttpResponse.ok("Removed Car Id is ${car.car_id} ok!")
}
@Get("/fetch/{id}")
fun getCarById(@PathVariable("id") carId: String): HttpResponse<Car> {
val car = carDao.getById(Car(carId))
return HttpResponse.ok(car)
}
@Get("/all")
fun getAllCars(): HttpResponse<String> {
val carIdList = carDao.getAll()
val jsonBody = mapper.toJson(carIdList)
return HttpResponse.ok(jsonBody)
}
}
AWS Setup
1. AWS Lambda
Create a Lambda function and set the runtime to Java 11 (Corretto). Configure the handler to io.micronaut.function.aws.proxy.MicronautLambdaHandler
.
2. AWS API Gateway
Create a REST API in API Gateway and configure it to integrate with your Lambda function. Deploy the API and note the Invoke URL.
3. DynamoDB
Create a DynamoDB table to store your car data. Ensure the primary key matches the field annotated with @DynamoDbPartitionKey
in your Car
class.
4. AWS Cloud Watch
Next, it’s the most important thing to do. Please remember that once we create a new table in DynamoDB then alarms in AWS cloud watch will get turned on and we need to delete it. That is a mandatory step to do or “otherwise we will be charged in Dollars” ok.
For each new table created in DynamoDB totally 8 alarms will get turned on in that there are 6 All Alarms and 2 In Alarms ok.
We need to delete those total (8) Alarms mandatorily or “otherwise we will be charged in Dollars” ok.
Firstly, we need to move towards AWS Cloud Watch by typing Cloud Watch in the Search Bar of our AWS management console as shown below,
5. AWS S3
Create an S3 bucket to store your Micronaut application JAR file.
6. IAM USER
Now, we just need to create a AWS CLI user to access Lambda and S3 via Command Prompt(cmd) with the help of that only we can put a JAR file of the application using the multi-line command.
Now, just use this command aws configure and set the credentials in your command prompt(cmd) to access S3 and Lambda with the help of AWS CLI for running the multi-line commands ok. Next, use this command aws sts get-caller-identity to verify that whether you correctly set the credentials or not for your respective CLI user.
Deploying and Testing
- Build and Upload JAR: Use the following multi-line command to build and upload the JAR file to S3:
gradlew clean && gradlew build && aws s3 cp build/libs/cars-0.1-all.jar s3://your-bucket-name && aws lambda update-function-code --function-name your-lambda-function-name --s3-bucket your-bucket-name --s3-key cars-0.1-all.jar
- Test Using Postman: Use the Invoke URL from API Gateway in Postman to test your API endpoints (
/cars/add
,/cars/update
,/cars/remove
,/cars/fetch/{id}
,/cars/all
).
Conclusion
In this tutorial, we have covered the steps to create a CRUD REST API using Kotlin Micronaut, deploy it to AWS Lambda, and integrate it with AWS API Gateway, DynamoDB, and S3. By following these steps, you can efficiently build and deploy serverless applications with robust CRUD capabilities. Happy Coding!
Comments
Post a Comment