Repository: aws-powertools/powertools-lambda-python

Issue:
Docs: Improved clarity on options for "different" HTTP verbs
### What were you searching in the docs?

The ability to support a HEAD request in an API Gateway REST API.

### Is this related to an existing documentation section?

https://docs.powertools.aws.dev/lambda/python/latest/core/event_handler/api_gateway/#http-methods

### How can we improve?

There is already a section in the docs that shows how multiple verbs can be handled by a single function. This same syntax will allow the support for verbs (such as HEAD) that do not have their own dedicated decorator.
It might be nice to draw attention to this option in this section of the docs.

### Got a suggestion in mind?

_No response_

### Acknowledgment

- [X] I understand the final update might be different from my proposed suggestion, or refused.
Feature request: Add new decorator to support HEAD verb on REST API
### Use case

I am creating a REST API on API Gateway and would like to implement the HEAD method

### Solution/User Experience

I would like to be able to use `@app.head("/sources")` to decorate my function method in exactly the same way that i can use `@app.get("/sources")` to decorate a GET request.

### Alternative solutions

```markdown
I am currently using the route option `@app.route("/sources", method=["HEAD"])` which works fine but it would be nice to support HEAD in the same way as the other verbs you currently support.

Thank you!
```


### Acknowledgment

- [X] This feature request meets [Powertools for AWS Lambda (Python) Tenets](https://docs.powertools.aws.dev/lambda/python/latest/#tenets)
- [X] Should this be considered in other Powertools for AWS Lambda languages? i.e. [Java](https://github.com/aws-powertools/powertools-lambda-java/), [TypeScript](https://github.com/aws-powertools/powertools-lambda-typescript/), and [.NET](https://github.com/aws-powertools/powertools-lambda-dotnet/)


Discussion:
Thanks for opening your first issue here! We'll come back to you as soon as we can.
In the meantime, check out the #python channel on our Powertools for AWS Lambda Discord: [Invite link](https://discord.gg/B8zZKbbyET)



Resolving patch:
diff --git a/aws_lambda_powertools/event_handler/api_gateway.py b/aws_lambda_powertools/event_handler/api_gateway.py
--- a/aws_lambda_powertools/event_handler/api_gateway.py
+++ b/aws_lambda_powertools/event_handler/api_gateway.py
@@ -1222,6 +1222,63 @@ def lambda_handler(event, context):
             middlewares,
         )
 
+    def head(
+        self,
+        rule: str,
+        cors: Optional[bool] = None,
+        compress: bool = False,
+        cache_control: Optional[str] = None,
+        summary: Optional[str] = None,
+        description: Optional[str] = None,
+        responses: Optional[Dict[int, OpenAPIResponse]] = None,
+        response_description: str = _DEFAULT_OPENAPI_RESPONSE_DESCRIPTION,
+        tags: Optional[List[str]] = None,
+        operation_id: Optional[str] = None,
+        include_in_schema: bool = True,
+        security: Optional[List[Dict[str, List[str]]]] = None,
+        middlewares: Optional[List[Callable]] = None,
+    ):
+        """Head route decorator with HEAD `method`
+
+        Examples
+        --------
+        Simple example with a custom lambda handler using the Tracer capture_lambda_handler decorator
+
+        ```python
+        from aws_lambda_powertools import Tracer
+        from aws_lambda_powertools.event_handler import APIGatewayRestResolver, Response, content_types
+
+        tracer = Tracer()
+        app = APIGatewayRestResolver()
+
+        @app.head("/head-call")
+        def simple_head():
+            return Response(status_code=200,
+                            content_type=content_types.APPLICATION_JSON,
+                            headers={"Content-Length": "123"})
+
+        @tracer.capture_lambda_handler
+        def lambda_handler(event, context):
+            return app.resolve(event, context)
+        ```
+        """
+        return self.route(
+            rule,
+            "HEAD",
+            cors,
+            compress,
+            cache_control,
+            summary,
+            description,
+            responses,
+            response_description,
+            tags,
+            operation_id,
+            include_in_schema,
+            security,
+            middlewares,
+        )
+
     def _push_processed_stack_frame(self, frame: str):
         """
         Add Current Middleware to the Middleware Stack Frames


Test patch:
diff --git a/tests/functional/event_handler/test_api_gateway.py b/tests/functional/event_handler/test_api_gateway.py
--- a/tests/functional/event_handler/test_api_gateway.py
+++ b/tests/functional/event_handler/test_api_gateway.py
@@ -291,12 +291,16 @@ def delete_func():
     def patch_func():
         raise RuntimeError()
 
+    @app.head("/no_matching_head")
+    def head_func():
+        raise RuntimeError()
+
     def handler(event, context):
         return app.resolve(event, context)
 
     # Also check the route configurations
     routes = app._static_routes
-    assert len(routes) == 5
+    assert len(routes) == 6
     for route in routes:
         if route.func == get_func:
             assert route.method == "GET"
@@ -308,6 +312,8 @@ def handler(event, context):
             assert route.method == "DELETE"
         elif route.func == patch_func:
             assert route.method == "PATCH"
+        elif route.func == head_func:
+            assert route.method == "HEAD"
 
     # WHEN calling the handler
     # THEN return a 404