This guide will help you get started with Oproto Lambda OpenAPI in your AWS Lambda projects.
- .NET 6.0 or later
- AWS Lambda Annotations package
- An AWS Lambda project
Install the NuGet package in your AWS Lambda project:
dotnet add package Oproto.Lambda.OpenApiAdd OpenAPI attributes to your Lambda functions:
using Amazon.Lambda.Annotations;
using Oproto.Lambda.OpenApi.Attributes;
[LambdaFunction]
[OpenApiOperation("GetUser", "Retrieves user information by ID")]
[OpenApiTag("Users")]
public async Task<APIGatewayProxyResponse> GetUser(
[FromRoute] string userId,
[FromQuery] bool includeDetails = false)
{
// Your implementation
return new APIGatewayProxyResponse
{
StatusCode = 200,
Body = JsonSerializer.Serialize(new { UserId = userId, Details = includeDetails })
};
}When you build your project, the OpenAPI specification will be automatically generated:
dotnet buildThis creates an openapi.json file in your project directory.
The generated OpenAPI specification includes:
- Endpoint definitions
- Parameter schemas
- Response schemas
- Tags and descriptions
Configure your API at the assembly level (typically in AssemblyInfo.cs or any .cs file):
[OpenApiInfo]- Sets API title, version, and metadata[OpenApiSecurityScheme]- Defines security schemes (API Key, OAuth2, etc.)[OpenApiServer]- Defines server URLs (production, staging, etc.)[OpenApiTagDefinition]- Defines tags with descriptions[OpenApiExternalDocs]- Links to external API documentation
[assembly: OpenApiInfo("My API", "1.0.0", Description = "API for managing resources")]
[assembly: OpenApiServer("https://api.example.com/v1", Description = "Production")]
[assembly: OpenApiTagDefinition("Products", Description = "Product operations")]
[assembly: OpenApiExternalDocs("https://docs.example.com", Description = "Full documentation")]All attributes are located in the Oproto.Lambda.OpenApi.Attributes namespace:
[OpenApiOperation]- Defines operation metadata (summary, description, deprecated)[OpenApiOperationId]- Custom operation IDs for code generators[OpenApiTag]- Groups operations by tags[OpenApiResponseType]- Explicitly documents response types (useful forIHttpResultreturns)[OpenApiResponseHeader]- Documents response headers[OpenApiExample]- Provides request/response examples[OpenApiExternalDocs]- Links to external documentation (also assembly-level)
[OpenApiSchema]- Customizes type schemas (format, validation, examples)[OpenApiIgnore]- Excludes properties from schemas
[FromRoute]- Path parameters[FromQuery]- Query parameters[FromHeader]- Header parameters[FromBody]- Request body (JSON)
Parameters decorated with [FromServices] are automatically excluded from the OpenAPI specification. These are dependency injection parameters that are not part of the HTTP API contract:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/products")]
public async Task<IEnumerable<Product>> GetProducts(
[FromServices] IProductService productService, // Excluded from OpenAPI
[FromQuery] int limit = 10) // Included in OpenAPI
{
return await productService.GetProducts(limit);
}The generated OpenAPI specification includes the x-amazon-apigateway-integration extension for each operation. This extension is required for deploying to AWS API Gateway:
{
"x-amazon-apigateway-integration": {
"type": "aws_proxy",
"httpMethod": "POST",
"uri": "${LambdaFunctionArn}",
"payloadFormatVersion": "2.0"
}
}The payloadFormatVersion is automatically set based on the API type:
2.0for HTTP APIs ([HttpApi])1.0for REST APIs ([RestApi])
The generator automatically unwraps Task<T> and ValueTask<T> return types. For example:
// This method returns Task<Product>
public async Task<Product> GetProduct(string id) { ... }
// The OpenAPI response schema will be for Product, not Task<Product>Methods returning non-generic Task or ValueTask generate a 204 No Content response.
When your Lambda functions return IHttpResult (from Lambda Annotations), the generator cannot infer the actual response type. Use [OpenApiResponseType] to explicitly document responses:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/products/{id}")]
[OpenApiResponseType(typeof(Product), 200, Description = "Returns the product")]
[OpenApiResponseType(typeof(ErrorResponse), 404, Description = "Product not found")]
public async Task<IHttpResult> GetProduct(string id)
{
var product = await _service.GetProduct(id);
if (product == null)
return HttpResults.NotFound(new ErrorResponse { Message = "Not found" });
return HttpResults.Ok(product);
}The generator automatically detects the standard .NET [Obsolete] attribute and marks operations as deprecated:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Delete, "/products/{id}")]
[Obsolete("Use the archive endpoint instead. This will be removed in v2.0.")]
public Task DeleteProduct(string id)
{
// Implementation
}Document response headers using [OpenApiResponseHeader]:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/products")]
[OpenApiResponseHeader("X-Total-Count", Description = "Total products", Type = typeof(int))]
[OpenApiResponseHeader("X-Page-Size", Description = "Page size", Type = typeof(int))]
public Task<IEnumerable<Product>> GetProducts([FromQuery] int page = 1)
{
// Implementation
}Provide JSON examples using [OpenApiExample]:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Post, "/products")]
[OpenApiExample("Create Request",
"{\"name\": \"Widget\", \"price\": 19.99}",
IsRequestExample = true)]
[OpenApiExample("Success Response",
"{\"id\": \"123\", \"name\": \"Widget\", \"price\": 19.99}",
StatusCode = 200)]
public Task<Product> CreateProduct([FromBody] CreateProductRequest request)
{
// Implementation
}Customize operation IDs for code generators using [OpenApiOperationId]:
[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/products")]
[OpenApiOperationId("listAllProducts")]
public Task<IEnumerable<Product>> GetProducts()
{
// Implementation
}For Native AOT builds, the standard reflection-based extraction may not work. To enable AOT-compatible extraction:
- Enable compiler-generated files in your project:
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>- The build task will automatically parse the generated source file instead of using reflection.
Security schemes are only added to the OpenAPI specification when you define them using assembly-level attributes:
// In your project (e.g., AssemblyInfo.cs)
[assembly: OpenApiSecurityScheme("apiKey",
Type = OpenApiSecuritySchemeType.ApiKey,
ApiKeyName = "x-api-key",
ApiKeyLocation = ApiKeyLocation.Header)]See the Attribute Reference for more details.
- Attribute Reference - Complete attribute documentation
- Configuration Options - Advanced configuration
- Examples - Working example project with CRUD operations