Skip to content

Latest commit

 

History

History
267 lines (199 loc) · 7.91 KB

File metadata and controls

267 lines (199 loc) · 7.91 KB

Getting Started with Oproto Lambda OpenAPI

This guide will help you get started with Oproto Lambda OpenAPI in your AWS Lambda projects.

Prerequisites

  • .NET 6.0 or later
  • AWS Lambda Annotations package
  • An AWS Lambda project

Installation

Install the NuGet package in your AWS Lambda project:

dotnet add package Oproto.Lambda.OpenApi

Basic Usage

1. Decorate Your Lambda Functions

Add 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 })
    };
}

2. Build Your Project

When you build your project, the OpenAPI specification will be automatically generated:

dotnet build

This creates an openapi.json file in your project directory.

3. View the Generated Specification

The generated OpenAPI specification includes:

  • Endpoint definitions
  • Parameter schemas
  • Response schemas
  • Tags and descriptions

Available Attributes

Assembly-Level Attributes

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")]

Method-Level Attributes

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 for IHttpResult returns)
  • [OpenApiResponseHeader] - Documents response headers
  • [OpenApiExample] - Provides request/response examples
  • [OpenApiExternalDocs] - Links to external documentation (also assembly-level)

Property/Parameter Attributes

  • [OpenApiSchema] - Customizes type schemas (format, validation, examples)
  • [OpenApiIgnore] - Excludes properties from schemas

Parameter Attributes

  • [FromRoute] - Path parameters
  • [FromQuery] - Query parameters
  • [FromHeader] - Header parameters
  • [FromBody] - Request body (JSON)

Important Behaviors

FromServices Parameters Are Excluded

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);
}

API Gateway Integration Extension

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.0 for HTTP APIs ([HttpApi])
  • 1.0 for REST APIs ([RestApi])

Async Return Types

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.

IHttpResult Return Types

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);
}

Deprecation with [Obsolete]

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
}

Response Headers

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
}

Request and Response Examples

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
}

Operation IDs

Customize operation IDs for code generators using [OpenApiOperationId]:

[LambdaFunction]
[HttpApi(LambdaHttpMethod.Get, "/products")]
[OpenApiOperationId("listAllProducts")]
public Task<IEnumerable<Product>> GetProducts()
{
    // Implementation
}

AOT Compatibility

For Native AOT builds, the standard reflection-based extraction may not work. To enable AOT-compatible extraction:

  1. Enable compiler-generated files in your project:
<PropertyGroup>
  <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
  1. The build task will automatically parse the generated source file instead of using reflection.

Security Schemes

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.

Next Steps