☕ ⚡ Serverless Development with TuskLang Java
⚡ Serverless Development with TuskLang Java
"We don't bow to any king" - Serverless Java Edition
TuskLang Java enables building serverless applications with AWS Lambda, Azure Functions, and cloud-native serverless patterns that scale automatically and pay only for what you use.
🎯 Serverless Architecture Overview
Serverless Configuration
// serverless-app.tsk
[serverless]
platform: "aws"
runtime: "java17"
memory_size: 512
timeout: 30
environment: "production"[functions]
user_management: {
name: "user-management-function"
handler: "com.tusklang.serverless.UserManagementHandler::handleRequest"
memory_size: 1024
timeout: 60
environment: {
DB_HOST: @env("DB_HOST", "localhost")
DB_PORT: @env("DB_PORT", "5432")
DB_NAME: @env("DB_NAME", "serverless_app")
DB_USER: @env("DB_USER", "lambda_user")
DB_PASSWORD: @env.secure("DB_PASSWORD")
JWT_SECRET: @env.secure("JWT_SECRET")
}
events: [
{
type: "http"
path: "/users"
method: "GET"
cors: true
authorizer: "jwt_authorizer"
},
{
type: "http"
path: "/users"
method: "POST"
cors: true
authorizer: "jwt_authorizer"
},
{
type: "http"
path: "/users/{id}"
method: "PUT"
cors: true
authorizer: "jwt_authorizer"
},
{
type: "http"
path: "/users/{id}"
method: "DELETE"
cors: true
authorizer: "jwt_authorizer"
}
]
vpc: {
security_groups: ["sg-12345678"]
subnet_ids: ["subnet-12345678", "subnet-87654321"]
}
layers: ["arn:aws:lambda:us-east-1:123456789012:layer:tusklang-java:1"]
}
order_processing: {
name: "order-processing-function"
handler: "com.tusklang.serverless.OrderProcessingHandler::handleRequest"
memory_size: 2048
timeout: 120
environment: {
DB_HOST: @env("DB_HOST", "localhost")
DB_PORT: @env("DB_PORT", "5432")
DB_NAME: @env("DB_NAME", "serverless_app")
DB_USER: @env("DB_USER", "lambda_user")
DB_PASSWORD: @env.secure("DB_PASSWORD")
PAYMENT_API_KEY: @env.secure("PAYMENT_API_KEY")
INVENTORY_API_URL: @env("INVENTORY_API_URL")
}
events: [
{
type: "http"
path: "/orders"
method: "POST"
cors: true
authorizer: "jwt_authorizer"
},
{
type: "http"
path: "/orders/{id}"
method: "GET"
cors: true
authorizer: "jwt_authorizer"
},
{
type: "s3"
bucket: "order-uploads"
events: ["s3:ObjectCreated:*"]
filter: {
suffix: ".json"
}
},
{
type: "sqs"
arn: "arn:aws:sqs:us-east-1:123456789012:order-queue"
batch_size: 10
maximum_batching_window: 5
}
]
vpc: {
security_groups: ["sg-12345678"]
subnet_ids: ["subnet-12345678", "subnet-87654321"]
}
layers: ["arn:aws:lambda:us-east-1:123456789012:layer:tusklang-java:1"]
}
notification_service: {
name: "notification-service-function"
handler: "com.tusklang.serverless.NotificationHandler::handleRequest"
memory_size: 512
timeout: 30
environment: {
SES_REGION: @env("SES_REGION", "us-east-1")
SES_FROM_EMAIL: @env("SES_FROM_EMAIL", "noreply@example.com")
SNS_TOPIC_ARN: @env("SNS_TOPIC_ARN")
}
events: [
{
type: "sns"
arn: "arn:aws:sns:us-east-1:123456789012:notification-topic"
},
{
type: "eventbridge"
schedule: "rate(5 minutes)"
}
]
layers: ["arn:aws:lambda:us-east-1:123456789012:layer:tusklang-java:1"]
}
[api_gateway]
name: "serverless-api"
stage: @env("API_STAGE", "prod")
cors: {
allowed_origins: ["https://app.example.com", "https://admin.example.com"]
allowed_methods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
allowed_headers: ["Content-Type", "Authorization", "X-Requested-With"]
allow_credentials: true
max_age: 3600
}
authorizers: {
jwt_authorizer: {
type: "jwt"
issuer: @env("JWT_ISSUER", "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_123456789")
audience: @env("JWT_AUDIENCE", "serverless-app")
}
}
[databases]
primary: {
type: "aurora_serverless"
cluster_identifier: "serverless-cluster"
engine: "aurora-postgresql"
engine_version: "13.7"
database_name: @env("DB_NAME", "serverless_app")
master_username: @env("DB_USER", "lambda_user")
master_password: @env.secure("DB_PASSWORD")
vpc_security_group_ids: ["sg-12345678"]
db_subnet_group_name: "serverless-subnet-group"
scaling_configuration: {
min_capacity: 1
max_capacity: 16
auto_pause: true
seconds_until_auto_pause: 300
}
}
cache: {
type: "elasticache"
engine: "redis"
node_type: "cache.t3.micro"
num_cache_nodes: 1
vpc_security_group_ids: ["sg-12345678"]
subnet_group_name: "serverless-subnet-group"
}
[storage]
s3: {
buckets: [
{
name: "user-uploads"
versioning: true
encryption: "AES256"
lifecycle: {
transition_days: 30
expiration_days: 365
}
},
{
name: "order-uploads"
versioning: true
encryption: "AES256"
lifecycle: {
transition_days: 7
expiration_days: 90
}
}
]
}
[monitoring]
cloudwatch: {
enabled: true
log_retention: 30
metrics: [
"duration",
"errors",
"throttles",
"invocations"
]
}
xray: {
enabled: true
sampling_rate: 0.1
}
[alerts]
error_rate: {
enabled: true
threshold: 0.05
period: 300
evaluation_periods: 2
actions: [
{
type: "sns"
arn: "arn:aws:sns:us-east-1:123456789012:alerts-topic"
}
]
}
duration: {
enabled: true
threshold: 5000
period: 300
evaluation_periods: 2
actions: [
{
type: "sns"
arn: "arn:aws:sns:us-east-1:123456789012:alerts-topic"
}
]
}
🚀 AWS Lambda Implementation
Lambda Handler Base Class
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.tusklang.java.TuskLang;
import org.tusklang.java.annotations.TuskConfig;
import java.util.Map;public abstract class BaseLambdaHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {
protected final TuskLang tuskLang;
protected final ObjectMapper objectMapper;
protected final Map<String, Object> config;
public BaseLambdaHandler() {
this.tuskLang = new TuskLang();
this.objectMapper = new ObjectMapper();
this.config = loadConfiguration();
}
protected Map<String, Object> loadConfiguration() {
try {
return tuskLang.parseFile("config/serverless.tsk");
} catch (Exception e) {
log.error("Failed to load configuration", e);
return Map.of();
}
}
@Override
public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent input, Context context) {
try {
// Add correlation ID for tracing
String correlationId = input.getHeaders().get("X-Correlation-ID");
if (correlationId == null) {
correlationId = java.util.UUID.randomUUID().toString();
}
// Log request
log.info("Processing request: {} {} with correlation ID: {}",
input.getHttpMethod(), input.getPath(), correlationId);
// Process request
APIGatewayProxyResponseEvent response = processRequest(input, context);
// Add correlation ID to response
response.getHeaders().put("X-Correlation-ID", correlationId);
// Log response
log.info("Request completed with status: {} and correlation ID: {}",
response.getStatusCode(), correlationId);
return response;
} catch (Exception e) {
log.error("Error processing request", e);
return createErrorResponse(500, "Internal Server Error", e.getMessage());
}
}
protected abstract APIGatewayProxyResponseEvent processRequest(
APIGatewayProxyRequestEvent input, Context context);
protected APIGatewayProxyResponseEvent createSuccessResponse(Object body) {
try {
String jsonBody = objectMapper.writeValueAsString(body);
return new APIGatewayProxyResponseEvent()
.withStatusCode(200)
.withHeaders(Map.of(
"Content-Type", "application/json",
"Access-Control-Allow-Origin", "*",
"Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With",
"Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"
))
.withBody(jsonBody);
} catch (Exception e) {
log.error("Error serializing response", e);
return createErrorResponse(500, "Internal Server Error", "Failed to serialize response");
}
}
protected APIGatewayProxyResponseEvent createErrorResponse(int statusCode, String error, String message) {
Map<String, String> errorBody = Map.of(
"error", error,
"message", message,
"timestamp", java.time.Instant.now().toString()
);
try {
String jsonBody = objectMapper.writeValueAsString(errorBody);
return new APIGatewayProxyResponseEvent()
.withStatusCode(statusCode)
.withHeaders(Map.of(
"Content-Type", "application/json",
"Access-Control-Allow-Origin", "*",
"Access-Control-Allow-Headers", "Content-Type,Authorization,X-Requested-With",
"Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS"
))
.withBody(jsonBody);
} catch (Exception e) {
log.error("Error serializing error response", e);
return new APIGatewayProxyResponseEvent()
.withStatusCode(statusCode)
.withBody("{\"error\":\"Internal Server Error\"}");
}
}
protected <T> T parseRequestBody(APIGatewayProxyRequestEvent input, Class<T> clazz) {
try {
if (input.getBody() == null) {
throw new IllegalArgumentException("Request body is required");
}
return objectMapper.readValue(input.getBody(), clazz);
} catch (Exception e) {
log.error("Error parsing request body", e);
throw new IllegalArgumentException("Invalid request body format");
}
}
protected String getPathParameter(APIGatewayProxyRequestEvent input, String name) {
Map<String, String> pathParameters = input.getPathParameters();
if (pathParameters == null) {
throw new IllegalArgumentException("Path parameter '" + name + "' is required");
}
String value = pathParameters.get(name);
if (value == null) {
throw new IllegalArgumentException("Path parameter '" + name + "' is required");
}
return value;
}
protected String getQueryParameter(APIGatewayProxyRequestEvent input, String name) {
Map<String, String> queryStringParameters = input.getQueryStringParameters();
if (queryStringParameters == null) {
return null;
}
return queryStringParameters.get(name);
}
}
User Management Lambda Handler
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Map;@Component
public class UserManagementHandler extends BaseLambdaHandler {
private final UserService userService;
private final JwtAuthorizer jwtAuthorizer;
public UserManagementHandler(UserService userService, JwtAuthorizer jwtAuthorizer) {
this.userService = userService;
this.jwtAuthorizer = jwtAuthorizer;
}
@Override
protected APIGatewayProxyResponseEvent processRequest(
APIGatewayProxyRequestEvent input, Context context) {
// Authorize request
String token = extractToken(input);
if (token == null) {
return createErrorResponse(401, "Unauthorized", "Missing or invalid authorization token");
}
try {
UserPrincipal user = jwtAuthorizer.authorize(token);
if (user == null) {
return createErrorResponse(401, "Unauthorized", "Invalid token");
}
} catch (Exception e) {
log.error("Authorization failed", e);
return createErrorResponse(401, "Unauthorized", "Invalid token");
}
// Route request based on HTTP method
switch (input.getHttpMethod()) {
case "GET":
return handleGetUsers(input, context);
case "POST":
return handleCreateUser(input, context);
case "PUT":
return handleUpdateUser(input, context);
case "DELETE":
return handleDeleteUser(input, context);
default:
return createErrorResponse(405, "Method Not Allowed",
"HTTP method " + input.getHttpMethod() + " is not supported");
}
}
private APIGatewayProxyResponseEvent handleGetUsers(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Get query parameters
String page = getQueryParameter(input, "page");
String size = getQueryParameter(input, "size");
String search = getQueryParameter(input, "search");
int pageNum = page != null ? Integer.parseInt(page) : 0;
int pageSize = size != null ? Integer.parseInt(size) : 20;
// Get users from service
Page<User> users = userService.getUsers(pageNum, pageSize, search);
// Create response
Map<String, Object> response = Map.of(
"users", users.getContent(),
"page", users.getNumber(),
"size", users.getSize(),
"totalElements", users.getTotalElements(),
"totalPages", users.getTotalPages()
);
return createSuccessResponse(response);
} catch (Exception e) {
log.error("Error getting users", e);
return createErrorResponse(500, "Internal Server Error", "Failed to retrieve users");
}
}
private APIGatewayProxyResponseEvent handleCreateUser(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Parse request body
CreateUserRequest request = parseRequestBody(input, CreateUserRequest.class);
// Validate request
if (request.getEmail() == null || request.getEmail().trim().isEmpty()) {
return createErrorResponse(400, "Bad Request", "Email is required");
}
if (request.getFirstName() == null || request.getFirstName().trim().isEmpty()) {
return createErrorResponse(400, "Bad Request", "First name is required");
}
if (request.getLastName() == null || request.getLastName().trim().isEmpty()) {
return createErrorResponse(400, "Bad Request", "Last name is required");
}
// Create user
User user = userService.createUser(request);
return createSuccessResponse(user);
} catch (IllegalArgumentException e) {
return createErrorResponse(400, "Bad Request", e.getMessage());
} catch (Exception e) {
log.error("Error creating user", e);
return createErrorResponse(500, "Internal Server Error", "Failed to create user");
}
}
private APIGatewayProxyResponseEvent handleUpdateUser(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Get user ID from path
String userId = getPathParameter(input, "id");
// Parse request body
UpdateUserRequest request = parseRequestBody(input, UpdateUserRequest.class);
// Update user
User user = userService.updateUser(userId, request);
if (user == null) {
return createErrorResponse(404, "Not Found", "User not found");
}
return createSuccessResponse(user);
} catch (IllegalArgumentException e) {
return createErrorResponse(400, "Bad Request", e.getMessage());
} catch (Exception e) {
log.error("Error updating user", e);
return createErrorResponse(500, "Internal Server Error", "Failed to update user");
}
}
private APIGatewayProxyResponseEvent handleDeleteUser(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Get user ID from path
String userId = getPathParameter(input, "id");
// Delete user
boolean deleted = userService.deleteUser(userId);
if (!deleted) {
return createErrorResponse(404, "Not Found", "User not found");
}
return createSuccessResponse(Map.of("message", "User deleted successfully"));
} catch (Exception e) {
log.error("Error deleting user", e);
return createErrorResponse(500, "Internal Server Error", "Failed to delete user");
}
}
private String extractToken(APIGatewayProxyRequestEvent input) {
String authorization = input.getHeaders().get("Authorization");
if (authorization == null || !authorization.startsWith("Bearer ")) {
return null;
}
return authorization.substring(7);
}
}
Order Processing Lambda Handler
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.amazonaws.services.lambda.runtime.events.SQSEvent;
import com.amazonaws.services.lambda.runtime.events.S3Event;
import org.springframework.stereotype.Component;
import java.util.List;@Component
public class OrderProcessingHandler extends BaseLambdaHandler {
private final OrderService orderService;
private final PaymentService paymentService;
private final InventoryService inventoryService;
private final NotificationService notificationService;
public OrderProcessingHandler(OrderService orderService,
PaymentService paymentService,
InventoryService inventoryService,
NotificationService notificationService) {
this.orderService = orderService;
this.paymentService = paymentService;
this.inventoryService = inventoryService;
this.notificationService = notificationService;
}
// HTTP API Gateway handler
public APIGatewayProxyResponseEvent handleHttpRequest(
APIGatewayProxyRequestEvent input, Context context) {
return processRequest(input, context);
}
// SQS handler
public void handleSqsEvent(SQSEvent event, Context context) {
for (SQSEvent.SQSMessage message : event.getRecords()) {
try {
log.info("Processing SQS message: {}", message.getMessageId());
// Parse order from SQS message
OrderRequest orderRequest = objectMapper.readValue(
message.getBody(), OrderRequest.class);
// Process order
Order order = orderService.processOrder(orderRequest);
// Send notification
notificationService.sendOrderConfirmation(order);
log.info("Successfully processed SQS message: {}", message.getMessageId());
} catch (Exception e) {
log.error("Error processing SQS message: {}", message.getMessageId(), e);
// Message will be retried by SQS
throw e;
}
}
}
// S3 handler
public void handleS3Event(S3Event event, Context context) {
for (S3Event.S3EventNotificationRecord record : event.getRecords()) {
try {
String bucketName = record.getS3().getBucket().getName();
String key = record.getS3().getObject().getKey();
log.info("Processing S3 event for bucket: {} key: {}", bucketName, key);
// Download and parse order file
String orderJson = downloadS3Object(bucketName, key);
OrderRequest orderRequest = objectMapper.readValue(orderJson, OrderRequest.class);
// Process order
Order order = orderService.processOrder(orderRequest);
// Send notification
notificationService.sendOrderConfirmation(order);
log.info("Successfully processed S3 event for key: {}", key);
} catch (Exception e) {
log.error("Error processing S3 event", e);
throw e;
}
}
}
@Override
protected APIGatewayProxyResponseEvent processRequest(
APIGatewayProxyRequestEvent input, Context context) {
// Authorize request
String token = extractToken(input);
if (token == null) {
return createErrorResponse(401, "Unauthorized", "Missing or invalid authorization token");
}
try {
UserPrincipal user = jwtAuthorizer.authorize(token);
if (user == null) {
return createErrorResponse(401, "Unauthorized", "Invalid token");
}
} catch (Exception e) {
log.error("Authorization failed", e);
return createErrorResponse(401, "Unauthorized", "Invalid token");
}
// Route request based on HTTP method
switch (input.getHttpMethod()) {
case "POST":
return handleCreateOrder(input, context);
case "GET":
return handleGetOrder(input, context);
default:
return createErrorResponse(405, "Method Not Allowed",
"HTTP method " + input.getHttpMethod() + " is not supported");
}
}
private APIGatewayProxyResponseEvent handleCreateOrder(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Parse request body
CreateOrderRequest request = parseRequestBody(input, CreateOrderRequest.class);
// Validate request
if (request.getItems() == null || request.getItems().isEmpty()) {
return createErrorResponse(400, "Bad Request", "Order items are required");
}
// Check inventory
for (OrderItem item : request.getItems()) {
boolean available = inventoryService.checkAvailability(
item.getProductId(), item.getQuantity());
if (!available) {
return createErrorResponse(400, "Bad Request",
"Product " + item.getProductId() + " is not available in requested quantity");
}
}
// Process payment
PaymentResult paymentResult = paymentService.processPayment(request.getPayment());
if (!paymentResult.isSuccess()) {
return createErrorResponse(400, "Payment Failed", paymentResult.getErrorMessage());
}
// Create order
Order order = orderService.createOrder(request, paymentResult.getTransactionId());
// Update inventory
for (OrderItem item : order.getItems()) {
inventoryService.updateStock(item.getProductId(), item.getQuantity());
}
// Send notification
notificationService.sendOrderConfirmation(order);
return createSuccessResponse(order);
} catch (IllegalArgumentException e) {
return createErrorResponse(400, "Bad Request", e.getMessage());
} catch (Exception e) {
log.error("Error creating order", e);
return createErrorResponse(500, "Internal Server Error", "Failed to create order");
}
}
private APIGatewayProxyResponseEvent handleGetOrder(
APIGatewayProxyRequestEvent input, Context context) {
try {
// Get order ID from path
String orderId = getPathParameter(input, "id");
// Get order
Order order = orderService.getOrder(orderId);
if (order == null) {
return createErrorResponse(404, "Not Found", "Order not found");
}
return createSuccessResponse(order);
} catch (Exception e) {
log.error("Error getting order", e);
return createErrorResponse(500, "Internal Server Error", "Failed to retrieve order");
}
}
private String downloadS3Object(String bucketName, String key) {
// Implementation for downloading S3 object
// This would use AWS SDK for S3
return ""; // Placeholder
}
private String extractToken(APIGatewayProxyRequestEvent input) {
String authorization = input.getHeaders().get("Authorization");
if (authorization == null || !authorization.startsWith("Bearer ")) {
return null;
}
return authorization.substring(7);
}
}
🔧 Best Practices
1. Lambda Function Design
- Keep functions small and focused - Use environment variables for configuration - Implement proper error handling - Use connection pooling for databases2. Performance Optimization
- Use Lambda layers for shared dependencies - Implement cold start optimization - Use async processing for long-running tasks - Optimize memory allocation3. Security
- Use IAM roles with least privilege - Implement proper input validation - Use VPC for database access - Encrypt sensitive data4. Monitoring and Observability
- Use CloudWatch for logging and metrics - Implement distributed tracing with X-Ray - Set up proper alarms and alerts - Monitor cold start times5. Cost Optimization
- Use provisioned concurrency for critical functions - Implement proper timeout handling - Use SQS for async processing - Monitor and optimize memory usage🎯 Summary
TuskLang Java serverless development provides:
- AWS Lambda Integration: Complete serverless function support - Event-Driven Architecture: SQS, S3, and API Gateway integration - Serverless Patterns: Cold start optimization and cost management - Security: IAM roles, VPC, and encryption - Monitoring: CloudWatch, X-Ray, and comprehensive observability
The combination of TuskLang's executable configuration with Java's serverless capabilities creates a powerful platform for building scalable, cost-effective, and maintainable serverless applications.
"We don't bow to any king" - Build serverless applications that scale automatically!