Skip to content

Tech debt: limited response type handling #5326

Open
@tobocop2

Description

@tobocop2

Why is this needed?

Problem Statement

The AWS Lambda Powertools for Python currently provides several options for managing HTTP responses, such as:

  • Response object: Offers flexibility but lacks strong typing and adds additional boilerplate for status codes and headers management.
  • Tuple[Dict, int] (Dict + status code): Common but can lead to juggling multiple return values without type safety.
  • Dict: Simplest option yet does not handle status codes or headers well when managed manually.

These methods pose several challenges:

  1. Lack of type safety: Inconsistency in response types increases the risk of malformed responses and potential errors.
  2. Inconsistent response handling: Switching between return types can make the codebase harder to maintain, leading to more errors or inconsistent patterns.
  3. Developer friction: Developers must manually manage status codes and serialize responses, increasing cognitive load and reducing productivity.

Proposed Solution

I propose a JsonResponse generic that inherits from the Response, allowing developers to return their responses as typed, JSON-serialized Pydantic models. This offers:

  • Type safety: Developers can return JsonResponse[MyModel] directly tied to a Pydantic model, ensuring valid responses and reducing errors.

  • Consistency: Standardizing the return type across the codebase leads to more maintainable and predictable API behavior.

  • Improved developer experience: With JsonResponse, there’s no need for manual handling of status codes or serialization—it's done automatically.

Example JsonResponse implementation:

from aws_lambda_powertools.event_handler.api_gateway import Response
from typing import Generic, TypeVar
from pydantic import BaseModel

T = TypeVar("T", bound=BaseModel)


class JsonResponse(Response, Generic[T]):
    def __init__(self, content: T, status_code: int = 200):
        super().__init__(
            status_code=status_code,
            content_type="application/json",
            body=content.model_dump_json(),
        )

Here's an example of its usage:

@app.get("/thing")
def handler() -> JsonResponse[PaginatedThingResponse]:
    response: PaginatedThingResponse = get_things()
    return JsonResponse(response, status_code=HTTPStatus.OK)

Benefits

This approach addresses several key issues:

  • Enhanced type safety: By using generics tied to Pydantic models, the system ensures that API responses match the expected structure.

  • Reduced boilerplate: No need for manual response serialization or status code management.

  • Increased consistency: All API responses follow a consistent pattern, making the codebase easier to maintain and less error-prone.

Summary

The introduction of JsonResponse addresses technical debt around response handling by enforcing consistent, type-safe responses across Lambda functions. This improvement reduces the
risk of errors, enhances the developer experience, and makes the system more maintainable.

I'd be happy to make an open source contribution if this makes sense.

Which area does this relate to?

No response

Suggestion

No response

Acknowledgment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Status

    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions