{"id":20974928,"url":"https://github.com/usausa/amazon-lambda-extension","last_synced_at":"2025-03-13T09:16:41.295Z","repository":{"id":43922694,"uuid":"447799484","full_name":"usausa/amazon-lambda-extension","owner":"usausa","description":"Source Generator for AWS Lambda HTTP API.","archived":false,"fork":false,"pushed_at":"2024-04-16T05:31:17.000Z","size":230,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-27T04:23:02.685Z","etag":null,"topics":["amazon","amazon-api-gateway","amazon-lambda","aws","codegenerator","csharp","dotnet","serverless","source-generator","source-generators","sourcegenerator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/usausa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-14T00:56:51.000Z","updated_at":"2024-06-14T11:51:00.009Z","dependencies_parsed_at":"2023-12-01T00:25:40.085Z","dependency_job_id":"76cae628-e192-4422-b237-fd68ed8f3744","html_url":"https://github.com/usausa/amazon-lambda-extension","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usausa%2Famazon-lambda-extension","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usausa%2Famazon-lambda-extension/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usausa%2Famazon-lambda-extension/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/usausa%2Famazon-lambda-extension/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/usausa","download_url":"https://codeload.github.com/usausa/amazon-lambda-extension/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243374325,"owners_count":20280661,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amazon","amazon-api-gateway","amazon-lambda","aws","codegenerator","csharp","dotnet","serverless","source-generator","source-generators","sourcegenerator"],"created_at":"2024-11-19T04:35:23.912Z","updated_at":"2025-03-13T09:16:41.267Z","avatar_url":"https://github.com/usausa.png","language":"C#","readme":"# AWS Lambda Extension for .NET\n\n[![NuGet Badge](https://buildstats.info/nuget/AmazonLambdaExtension)](https://www.nuget.org/packages/AmazonLambdaExtension/)\n[![NuGet Badge](https://buildstats.info/nuget/AmazonLambdaExtension.SourceGenerator)](https://www.nuget.org/packages/AmazonLambdaExtension.SourceGenerator/)\n\n## What is this?\n\nSource Generator for AWS Lambda HTTP API inspired by [Amazon.Lambda.Annotations](https://github.com/aws/aws-lambda-dotnet/blob/master/Libraries/src/Amazon.Lambda.Annotations/README.md).\n\n## Supported features\n\n* Dependency Injection support\n* Parameter binding\n* Pre/Post action\n* Auto validation and return 400\n* Return 404 if response is null\n\n## Usage\n\nAdd reference to AmazonLambdaExtension and AmazonLambdaExtension.SourceGenerator .\n\n```xml\n\u003cItemGroup\u003e\n  \u003cPackageReference Include=\"AmazonLambdaExtension\" Version=\"1.3.4\" /\u003e\n  \u003cPackageReference Include=\"AmazonLambdaExtension.SourceGenerator\" Version=\"1.3.4\"\u003e\n    \u003cPrivateAssets\u003eall\u003c/PrivateAssets\u003e\n    \u003cIncludeAssets\u003eruntime; build; native; contentfiles; analyzers; buildtransitive\u003c/IncludeAssets\u003e\n  \u003c/PackageReference\u003e\n\u003c/ItemGroup\u003e\n```\n\nChange Handler in serverless.template to generated code.\n\n* Before\n\n```yaml\n\"CrudGet\": {\n  \"Type\": \"AWS::Serverless::Function\",\n  \"Properties\": {\n    \"Handler\": \"AmazonLambdaExtension.Example::AmazonLambdaExtension.Example.Functions.CrudFunction::Get\",\n...\n```\n\n* After\n\n```yaml\n\"CrudGet\": {\n  \"Type\": \"AWS::Serverless::Function\",\n  \"Properties\": {\n    \"Handler\": \"AmazonLambdaExtension.Example::AmazonLambdaExtension.Example.Functions.CrudFunction_Get::Handle\",\n...\n```\n\n## Basic\n\nSet `LambdaAttribute` to Function class.  \nSet `ApiAttribute` to method to handle HTTP API and `EventAttribute` for other.  \nThen the wrapper class will be generated.\n\n## Dependency Injection\n\nCreate class that has `GetService\u003cT\u003e()` method and set type to `ServiceResolverAttribute`.\n\n```csharp\npublic sealed class ServiceResolver\n{\n    private readonly IServiceProvider provider = new ServiceCollection()\n        .AddLogging(c =\u003e\n        {\n            c.ClearProviders();\n            c.AddLambdaLogger();\n        })\n        .BuildServiceProvider();\n\n    public T GetService\u003cT\u003e() =\u003e provider.GetService\u003cT\u003e();\n}\n```\n\n```csharp\n[Lambda]\n[ServiceResolver(typeof(ServiceResolver))]\npublic sealed class Function\n{\n    private readonly ILogger\u003cFunction\u003e logger;\n\n    public Function(ILogger\u003cFunction\u003e logger)\n    {\n        this.logger = logger;\n    }\n...\n}\n```\n\nBy default, Function classes are directly new.\nIf set `ResolveFunction = true` in `ServiceResolverAttribute`, Function class will also be resolved by Dependency Injection.\n\n## Pre/Post action\n\nCreate class with `OnFunctionExecuting()`/`OnFunctionExecuted()` methods and set type in `FilterAttribute`, method will be called before and after Function is processed.  \nIn the case of HTTP API, `APIGatewayProxyRequest` and `ILambdaContext` are arguments to method.  \nIn the case of Event, `ILambdaContext` is arguments to method.  \nIn the HTTP API filter, if `OnFunctionExecuting()` returns `APIGatewayProxyResponse`, the Function process will not be called and its value will be used as the response.  \nFilter methods support `async`.\n\n```csharp\npublic sealed class ApiFilter\n{\n    public APIGatewayProxyResponse OnFunctionExecuting(APIGatewayProxyRequest request, ILambdaContext context)\n    {\n        if (request.Headers?.ContainsKey(\"X-Lambda-Ping\") ?? false)\n        {\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 200 };\n        }\n\n        LambdaLoggerContext.RequestId = context.AwsRequestId;\n\n        return null;\n    }\n\n    public void OnFunctionExecuted(APIGatewayProxyRequest request, ILambdaContext context)\n    {\n        LambdaLoggerContext.RequestId = null;\n    }\n}\n```\n\n```csharp\n[Lambda]\n[Filter(typeof(ApiFilter))]\npublic sealed class Function\n{\n...\n}\n```\n\n## Parameter binding\n\n| Attribute   | Source               | HTTP API | Event |\n|:------------|:---------------------|:--------:|:-----:|\n| FromHeader  | HTTP header          | ✅ |    |\n| FromQuery   | Query string         | ✅ |    |\n| FromRoute   | Path parameter       | ✅ |    |\n| FromBody    | HTTP request body    | ✅ |    |\n| FromService | Dependency Injection | ✅ | ✅ |\n\nObjects with `FromBodyAttribute` is validated by DataAnnotations.\n\n## Source generation\n\nSamples of source and generated code.\n\n### Source\n\n```csharp\n[Lambda]\n[ServiceResolver(typeof(ServiceResolver))]\n[Filter(typeof(Filter))]\npublic sealed class CrudFunction\n{\n    private readonly ILogger\u003cCrudFunction\u003e logger;\n\n    private readonly IMapper mapper;\n\n    private readonly DataService dataService;\n\n    public CrudFunction(ILogger\u003cCrudFunction\u003e logger, IMapper mapper, DataService dataService)\n    {\n        this.logger = logger;\n        this.mapper = mapper;\n        this.dataService = dataService;\n    }\n\n    [Api]\n    public async ValueTask\u003cDataEntity?\u003e Get([FromRoute] string id)\n    {\n        return await dataService.QueryDataAsync(id).ConfigureAwait(false);\n    }\n\n    [Api]\n    public async ValueTask\u003cCrudCreateOutput\u003e Create([FromBody] CrudCreateInput input)\n    {\n        var entity = mapper.Map\u003cDataEntity\u003e(input);\n        entity.Id = Guid.NewGuid().ToString();\n        entity.CreatedAt = DateTime.Now;\n\n        await dataService.CreateDataAsync(entity).ConfigureAwait(false);\n\n        logger.LogInformation(\"Data created. id=[{Id}]\", entity.Id);\n\n        return new CrudCreateOutput { Id = entity.Id };\n    }\n\n    [Api]\n    public async ValueTask Delete([FromRoute] string id)\n    {\n        await dataService.DeleteDataAsync(id).ConfigureAwait(false);\n\n        logger.LogInformation(\"Data deleted. id=[{Id}]\", id);\n    }\n}\n```\n\n### Generated code\n\n```csharp\npublic sealed class CrudFunction_Get\n{\n    private readonly AmazonLambdaExtension.Example.ServiceResolver serviceResolver;\n\n    private readonly AmazonLambdaExtension.Example.Filter filter;\n\n    private readonly AmazonLambdaExtension.Serialization.IBodySerializer serializer;\n\n    private readonly AmazonLambdaExtension.Example.Functions.CrudFunction function;\n\n    public CrudFunction_Get()\n    {\n        serviceResolver = new AmazonLambdaExtension.Example.ServiceResolver();\n        filter = new AmazonLambdaExtension.Example.Filter();\n        serializer = serviceResolver.GetService\u003cAmazonLambdaExtension.Serialization.IBodySerializer\u003e() ?? AmazonLambdaExtension.Serialization.JsonBodySerializer.Default;\n        function = new AmazonLambdaExtension.Example.Functions.CrudFunction(serviceResolver.GetService\u003cMicrosoft.Extensions.Logging.ILogger\u003cAmazonLambdaExtension.Example.Functions.CrudFunction\u003e\u003e(),serviceResolver.GetService\u003cAutoMapper.IMapper\u003e(),serviceResolver.GetService\u003cAmazonLambdaExtension.Example.Services.DataService\u003e());\n    }\n\n    public async System.Threading.Tasks.Task\u003cAmazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\u003e Handle(Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest request, Amazon.Lambda.Core.ILambdaContext context)\n    {\n        var executingResult = filter.OnFunctionExecuting(request, context);\n        if (executingResult != null)\n        {\n            return executingResult;\n        }\n\n        try\n        {\n            if (!AmazonLambdaExtension.Helpers.BindHelper.TryBind\u003cstring\u003e(request.PathParameters, \"id\", out var p0))\n            {\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 400 };\n            }\n\n            var output = await function.Get(p0).ConfigureAwait(false);\n            if (output == null)\n            {\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 404 };\n            }\n\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\n            {\n                Body = serializer.Serialize(output),\n                Headers = new Dictionary\u003cstring, string\u003e { { \"Content-Type\", \"application/json\" } },\n                StatusCode = 200\n            };\n        }\n        catch (AmazonLambdaExtension.ApiException ex)\n        {\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = ex.StatusCode };\n        }\n        catch (System.Exception ex)\n        {\n            context.Logger.LogLine(ex.ToString());\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 500 };\n        }\n        finally\n        {\n            filter.OnFunctionExecuted(request, context);\n        }\n    }\n}\n```\n\n```csharp\npublic sealed class CrudFunction_Create\n{\n    private readonly AmazonLambdaExtension.Example.ServiceResolver serviceResolver;\n\n    private readonly AmazonLambdaExtension.Example.Filter filter;\n\n    private readonly AmazonLambdaExtension.Serialization.IBodySerializer serializer;\n\n    private readonly AmazonLambdaExtension.Example.Functions.CrudFunction function;\n\n    public CrudFunction_Create()\n    {\n        serviceResolver = new AmazonLambdaExtension.Example.ServiceResolver();\n        filter = new AmazonLambdaExtension.Example.Filter();\n        serializer = serviceResolver.GetService\u003cAmazonLambdaExtension.Serialization.IBodySerializer\u003e() ?? AmazonLambdaExtension.Serialization.JsonBodySerializer.Default;\n        function = new AmazonLambdaExtension.Example.Functions.CrudFunction(serviceResolver.GetService\u003cMicrosoft.Extensions.Logging.ILogger\u003cAmazonLambdaExtension.Example.Functions.CrudFunction\u003e\u003e(),serviceResolver.GetService\u003cAutoMapper.IMapper\u003e(),serviceResolver.GetService\u003cAmazonLambdaExtension.Example.Services.DataService\u003e());\n    }\n\n    public async System.Threading.Tasks.Task\u003cAmazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\u003e Handle(Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest request, Amazon.Lambda.Core.ILambdaContext context)\n    {\n        var executingResult = filter.OnFunctionExecuting(request, context);\n        if (executingResult != null)\n        {\n            return executingResult;\n        }\n\n        try\n        {\n            AmazonLambdaExtension.Example.Parameters.CrudCreateInput p0;\n            try\n            {\n                p0 = serializer.Deserialize\u003cAmazonLambdaExtension.Example.Parameters.CrudCreateInput\u003e(request.Body);\n            }\n            catch (System.Exception ex)\n            {\n                context.Logger.LogLine(ex.ToString());\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 400 };\n            }\n\n            if (!AmazonLambdaExtension.Helpers.ValidationHelper.Validate(p0))\n            {\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 400 };\n            }\n\n            var output = await function.Create(p0).ConfigureAwait(false);\n            if (output == null)\n            {\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 404 };\n            }\n\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\n            {\n                Body = serializer.Serialize(output),\n                Headers = new Dictionary\u003cstring, string\u003e { { \"Content-Type\", \"application/json\" } },\n                StatusCode = 200\n            };\n        }\n        catch (AmazonLambdaExtension.ApiException ex)\n        {\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = ex.StatusCode };\n        }\n        catch (System.Exception ex)\n        {\n            context.Logger.LogLine(ex.ToString());\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 500 };\n        }\n        finally\n        {\n            filter.OnFunctionExecuted(request, context);\n        }\n    }\n}\n```\n\n```csharp\npublic sealed class CrudFunction_Delete\n{\n    private readonly AmazonLambdaExtension.Example.ServiceResolver serviceResolver;\n\n    private readonly AmazonLambdaExtension.Example.Filter filter;\n\n    private readonly AmazonLambdaExtension.Example.Functions.CrudFunction function;\n\n    public CrudFunction_Delete()\n    {\n        serviceResolver = new AmazonLambdaExtension.Example.ServiceResolver();\n        filter = new AmazonLambdaExtension.Example.Filter();\n        function = new AmazonLambdaExtension.Example.Functions.CrudFunction(serviceResolver.GetService\u003cMicrosoft.Extensions.Logging.ILogger\u003cAmazonLambdaExtension.Example.Functions.CrudFunction\u003e\u003e(),serviceResolver.GetService\u003cAutoMapper.IMapper\u003e(),serviceResolver.GetService\u003cAmazonLambdaExtension.Example.Services.DataService\u003e());\n    }\n\n    public async System.Threading.Tasks.Task\u003cAmazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\u003e Handle(Amazon.Lambda.APIGatewayEvents.APIGatewayProxyRequest request, Amazon.Lambda.Core.ILambdaContext context)\n    {\n        var executingResult = filter.OnFunctionExecuting(request, context);\n        if (executingResult != null)\n        {\n            return executingResult;\n        }\n\n        try\n        {\n            if (!AmazonLambdaExtension.Helpers.BindHelper.TryBind\u003cstring\u003e(request.PathParameters, \"id\", out var p0))\n            {\n                return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 400 };\n            }\n\n            await function.Delete(p0).ConfigureAwait(false);\n\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse\n            {\n                StatusCode = 200\n            };\n        }\n        catch (AmazonLambdaExtension.ApiException ex)\n        {\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = ex.StatusCode };\n        }\n        catch (System.Exception ex)\n        {\n            context.Logger.LogLine(ex.ToString());\n            return new Amazon.Lambda.APIGatewayEvents.APIGatewayProxyResponse { StatusCode = 500 };\n        }\n        finally\n        {\n            filter.OnFunctionExecuted(request, context);\n        }\n    }\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusausa%2Famazon-lambda-extension","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fusausa%2Famazon-lambda-extension","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fusausa%2Famazon-lambda-extension/lists"}