DEV Community

Cover image for Connecting AWS Lambda Function .NET Container to Amazon DynamoDB

Connecting AWS Lambda Function .NET Container to Amazon DynamoDB

Hello, everyone! This week, I was learning AWS Lambda Function, especially using .NET Container. I will continue my post and extends more use cases. I will connect the previous Lambda Function to Amazon DynamoDB.

Start Project

Please continue the project from this commit. In short, you may use this project directly to explore. If you want to check what's the changes, please visit here.

Update our Lambda Functions Code

Install Dependencies to Existing Project

You will need install Amazon DynamoDB dependencies before you use it. Please use this command: dotnet add src/SimpleAPI/ package AWSSDK.DynamoDBv2 --version 3.7.3.57. I use version 3.7.3.57.

Add Model

Please add this model file to src/SimpleAPI/Models/Note.cs. We will use a simple model in this case just for proof of the concept.

namespace SimpleAPI.Models;


using Amazon.DynamoDBv2.DataModel;

[DynamoDBTable("Notes")]
public class Note
{
    [DynamoDBHashKey]
    public Guid Id {get; set;}
    public String Message {get; set;}
} 
Enter fullscreen mode Exit fullscreen mode

Add Notes Controller

We will need add new controller to handle the API.

using Microsoft.AspNetCore.Mvc;
using Amazon.DynamoDBv2.DataModel;
using SimpleAPI.Models;
using Amazon.DynamoDBv2;

namespace SimpleAPI.Controllers;

[Route("api/[controller]")]
public class NotesController : ControllerBase
{
    private readonly AmazonDynamoDBClient _dynamoDBClient;
    private readonly DynamoDBContext _dbContext;
    public NotesController()
    {
        AmazonDynamoDBConfig clientConfig = new AmazonDynamoDBConfig();
        _dynamoDBClient = new AmazonDynamoDBClient(clientConfig);
        _dbContext = new DynamoDBContext(_dynamoDBClient);
    }

    [HttpGet]
    public async Task<IEnumerable<Note>> Get()
    {
        var notes = _dbContext.ScanAsync<Note>(new List<ScanCondition>());
        var results = await notes.GetRemainingAsync();
        return results;
    }

    [HttpGet("{id}")]
    public async Task<Note?> Get(Guid id)
    {
        var notes = _dbContext.QueryAsync<Note>(id);
        var results = await notes.GetRemainingAsync();
        return results.FirstOrDefault();
    }

    [HttpPost]
    public async Task<Note> Post([FromBody] Note value)
    {
        var batch = _dbContext.CreateBatchWrite<Note>();
        value.Id = Guid.NewGuid();
        batch.AddPutItem(value);
        await batch.ExecuteAsync();
        return value;
    }

    [HttpPut("{id}")]
    public async Task<IResult> Put(Guid id, [FromBody] Note value)
    {
        var notes = _dbContext.QueryAsync<Note>(id);
        var results = await notes.GetRemainingAsync();
        var first = results.FirstOrDefault();
        if (first == null)
        {
            return Results.NotFound();
        }
        first.Message = value.Message;
        var batch = _dbContext.CreateBatchWrite<Note>();
        batch.AddPutItem(first);
        await batch.ExecuteAsync();
        return Results.Ok(first);
    }

    [HttpDelete("{id}")]
    public async Task<IResult> Delete(Guid id)
    {
        var notes = _dbContext.QueryAsync<Note>(id);
        var results = await notes.GetRemainingAsync();
        var first = results.FirstOrDefault(); if (first == null)
        {
            return Results.NotFound();
        }
        var batch = _dbContext.CreateBatchWrite<Note>();
        batch.AddDeleteItem(first);
        await batch.ExecuteAsync();
        return Results.Ok();
    }
} 
Enter fullscreen mode Exit fullscreen mode

Update our Terraform Code

  • You will need to add this permission to the execution role of the lambda:
inline_policy {
    name = "lambda-custom"
    policy = jsonencode({
      Version = "2012-10-17"
      Statement = [{
        Effect = "Allow"
        Action = [
          "logs:CreateLogGroup",
          "logs:CreateLogStream",
          "logs:PutLogEvents"
        ]
        Resource = "*"
        }, {
        Effect = "Allow",
        Action = [
          "dynamodb:DescribeTable",
          "dynamodb:GetRecords",
          "dynamodb:GetShardIterator",
          "dynamodb:ListTables",
          "dynamodb:Scan",
          "dynamodb:Query",
          "dynamodb:BatchWriteItem",
          "dynamodb:PutItem",
          "dynamodb:UpdateItem",
          "dynamodb:DeleteItem"
        ]
        Resource = ["*"]
    }] })
  }
Enter fullscreen mode Exit fullscreen mode
  • I also extend the timeout to make sure it's not terminated when I call the API.
  tracing_config {
    mode = "Active"
  }

  timeout = 60
Enter fullscreen mode Exit fullscreen mode
  • Please make sure your aws_lambda_function_url already allows all methods.
allow_methods = ["*"]
Enter fullscreen mode Exit fullscreen mode
  • Add Amazon DynamoDB resource.
resource "aws_dynamodb_table" "lambda_container_demo_dev" {
  name           = "Notes"
  billing_mode   = "PROVISIONED"
  read_capacity  = 20
  write_capacity = 20
  hash_key       = "Id"

  attribute {
    name = "Id"
    type = "S"
  }

  attribute {
    name = "Message"
    type = "S"
  }

  global_secondary_index {
    name               = "NoteMessageIndex"
    hash_key           = "Message"
    write_capacity     = 10
    read_capacity      = 10
    projection_type    = "INCLUDE"
    non_key_attributes = ["Id"]
  }

  ttl {
    attribute_name = "TimeToExist"
    enabled        = true
  }

  tags = {
    Name        = "dynamodb-table-1"
    Environment = "dev"
  }
} 
Enter fullscreen mode Exit fullscreen mode

Done!

GitHub logo bervProject / lambda-sharp

Lambda use C# (.NET) Docker Image

Time to deploy your code to Amazon ECR and run terraform apply so your resources are provisioned.

You may look at these screenshots for my testing the CRUD of Notes Functions.

  • Get All Notes

GET

  • Create a Note

POST

  • Update a Note

PUT

  • Get a Note by Id

Get By Id

  • Delete

Delete

Thank you!

So, what do you think? It's quite simple, right? Let's share what's in your mind!

GO GIF

Top comments (0)