| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Name | Name | Last commit date | ||
|---|---|---|---|---|
English | 日本語
Source Generator for AWS Lambda HTTP API / Event handlers, inspired by Amazon.Lambda.Annotations.
AmazonLambdaExtension is a library that uses .NET Source Generators to auto-generate boilerplate code for AWS Lambda functions. It lets you declaratively describe parameter binding, the filter pipeline, and DI integration for HTTP API (API Gateway v2).
<ItemGroup>
<PackageReference Include="AmazonLambdaExtension" Version="x.x.x" />
<PackageReference Include="AmazonLambdaExtension.SourceGenerator" Version="x.x.x">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>[Lambda]
[ServiceResolver(typeof(ServiceResolver))]
public partial class CrudFunctions
{
private readonly DataService data;
public CrudFunctions(DataService data)
{
this.data = data;
}
[HttpApi(LambdaHttpMethod.Get, "/items/{id}")]
public async ValueTask<IHttpResult> GetItem(
[FromRoute] string id,
[FromQuery] int page,
ILambdaContext context)
{
var item = await data.GetAsync(id, page);
return item is null ? HttpResults.NotFound() : HttpResults.Ok(item);
}
[HttpApi(LambdaHttpMethod.Post, "/items")]
public async ValueTask<IHttpResult> CreateItem([FromBody] CreateItemInput input)
{
var created = await data.CreateAsync(input);
return HttpResults.Created($"/items/{created.Id}", created);
}
}An HTTP handler ([HttpApi] / [FunctionUrl]) may return any of the following:
| Return value | Behavior |
|---|---|
| IHttpResult / HttpResult (via HttpResults.*) | Converted to APIGatewayHttpApiV2ProxyResponse |
| APIGatewayHttpApiV2ProxyResponse | Returned as-is |
| Any other type (POCO) | Wrapped into a 200 OK JSON response (equivalent to HttpResults.Ok(value)) |
Task<T> / ValueTask<T> wrappers and synchronous returns are all supported.
The Source Generator calls ServiceResolver.ConfigureServices() to build the DI container.
public static class ServiceResolver
{
public static IServiceCollection ConfigureServices()
{
var services = new ServiceCollection();
// Lambda serializer (AOT-compatible — uses JsonSerializable source generation)
services.AddSingleton<ILambdaSerializer>(
new SourceGeneratorLambdaJsonSerializer<AppJsonContext>());
// Body serializer (AOT-compatible — no reflection by passing a JsonSerializerContext)
services.AddSingleton<IBodySerializer>(new JsonBodySerializer(AppJsonContext.Default));
services.AddSingleton<DataService>();
return services;
}
}
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(CreateItemInput))]
[JsonSerializable(typeof(Item))]
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyResponse))]
internal sealed partial class AppJsonContext : JsonSerializerContext;The handler name generated by the Source Generator is {Namespace}.{ClassName}::{Method}_Handler.
"CrudGet":
Type: AWS::Serverless::Function
Properties:
Handler: "MyApp::MyApp.CrudFunctions::GetItem_Handler"| Attribute | Binds from | HTTP API | Event |
|---|---|---|---|
| [FromRoute] | Path parameter | ✅ | |
| [FromQuery] | Query string | ✅ | |
| [FromHeader("name")] | HTTP header | ✅ | |
| [FromBody] | Request body (JSON) | ✅ | |
| [FromServices] / [FromServices("key")] | DI container | ✅ | ✅ |
| [FromAuthorizer("key")] | Lambda authorizer context | ✅ |
Apply classes implementing ILambdaFilter to a class with [Filter<T>(Order = N)]. They are chained in ascending Order, and you can write logic before and after await next(ctx).
public sealed class LoggingFilter : ILambdaFilter
{
public async ValueTask InvokeAsync(LambdaInvocationContext context, LambdaFilterDelegate next)
{
var sw = Stopwatch.StartNew();
await next(context);
Console.WriteLine($"Elapsed: {sw.ElapsedMilliseconds}ms");
}
}
public sealed class ApiKeyFilter : ILambdaFilter
{
public ValueTask InvokeAsync(LambdaInvocationContext context, LambdaFilterDelegate next)
{
var req = context.GetRequest<APIGatewayHttpApiV2ProxyRequest>();
if (!req.Headers.TryGetValue("x-api-key", out var key) || key != "expected")
{
context.Result = HttpResults.Unauthorized();
return default;
}
return next(context);
}
}
[Lambda]
[ServiceResolver(typeof(ServiceResolver))]
[Filter<LoggingFilter>(Order = 0)]
[Filter<ApiKeyFilter>(Order = 10)]
public partial class SecureFunctions
{
[HttpApi(LambdaHttpMethod.Get, "/secure/items/{id}")]
public ValueTask<HttpResult> GetItem([FromRoute] string id)
=> ValueTask.FromResult(HttpResults.Ok(new { id }));
}[Lambda]
[ServiceResolver(typeof(ServiceResolver))]
public partial class CrudFunctions
{
[HttpApi(LambdaHttpMethod.Post, "/items", Authorizer = nameof(Authorize))]
public async ValueTask<IHttpResult> CreateItem(
[FromBody] CreateItemInput input,
[FromAuthorizer("role")] string role)
{
if (role != "admin") return HttpResults.Forbid();
var created = await data.CreateAsync(input);
return HttpResults.Created($"/items/{created.Id}", created);
}
[HttpApiAuthorizer(EnableSimpleResponses = true)]
public async ValueTask<IAuthorizerResult> Authorize(
APIGatewayHttpApiV2ProxyRequest request,
ILambdaContext context)
{
if (!request.Headers.TryGetValue("authorization", out var token))
return AuthorizerResults.Deny();
return AuthorizerResults.Allow()
.WithPrincipalId("user-123")
.WithContext("role", "admin");
}
}Use [Event] for non-HTTP events (such as SQS).
[Lambda]
[ServiceResolver(typeof(ServiceResolver))]
public partial class QueueProcessor
{
private readonly IProcessor processor;
public QueueProcessor(IProcessor processor)
{
this.processor = processor;
}
[Event]
public async ValueTask Handle(SQSEvent ev, ILambdaContext context)
{
foreach (var record in ev.Records)
{
await processor.HandleAsync(record.Body);
}
}
}[Lambda]
public partial class HealthCheck
{
[FunctionUrl]
public IHttpResult Ping()
=> HttpResults.Ok(new { status = "ok", timestamp = DateTime.UtcNow });
}JsonBodySerializer has two constructors.
| Constructor | AOT support | Use |
|---|---|---|
| JsonBodySerializer(JsonSerializerContext) | ✅ | Pass a context generated by [JsonSerializable] (recommended) |
| JsonBodySerializer(JsonSerializerOptions) | ❌ | Uses reflection. Already marked with [RequiresDynamicCode] |
For AOT, just use the JsonSerializerContext constructor in your ServiceResolver and declare [JsonSerializable(typeof(T))] (see the ServiceResolver sample above).
HTTP handlers return APIGatewayHttpApiV2ProxyResponse, and authorizer handlers return APIGatewayCustomAuthorizerV2SimpleResponse / APIGatewayCustomAuthorizerV2IamResponse. These are serialized by the Lambda runtime serializer. For AOT, make sure your Lambda serializer's JsonSerializerContext also covers those response types (in addition to your DTOs).
This library only generates wrapper code (Source Generator output). The following are intentionally out of scope.
MIT
| Back | FazBrowse Home | New Git URL |