# Updates in the Athenaverse - New Component | Proxy Support | Form Data Deserialization

**URL:** <https://forum.crystal-lang.org/t/updates-in-the-athenaverse-new-component-proxy-support-form-data-deserialization/7656>\
**Category:** News\
**Tags:** release\
**Created:** [January 27, 2025, 2:36pm UTC](https://forum.crystal-lang.org/t/updates-in-the-athenaverse-new-component-proxy-support-form-data-deserialization/7656 "2025-01-27T14:36:11Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![Blacksmoke16](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/blacksmoke16/32/1241_2.png) [@Blacksmoke16](https://forum.crystal-lang.org/u/Blacksmoke16)\
**Post date:** [January 27, 2025, 2:36pm UTC](https://forum.crystal-lang.org/t/updates-in-the-athenaverse-new-component-proxy-support-form-data-deserialization/7656/1 "2025-01-27T14:36:11Z")

</div>

# Updates in the Athenaverse

It’s been a while since the last update, but the wait is over! This release cycle in the Athenaverse focuses on continuing to evolve the framework:

- Initial release of the new [MIME](https://athenaframework.org/MIME/) component
- Native support for [Proxy & Load Balancers](https://athenaframework.org/guides/proxies/)
- Native support for [Trusted Hosts](https://athenaframework.org/Framework/Bundle/Schema/#Athena::Framework::Bundle::Schema#trusted_hosts)
- Normalized exception types
- Refactored query param implementation along with native support for deserializing `application/x-www-form-urlencoded` bodies via [`ATHR::RequestBody`](https://athenaframework.org/Framework/Controller/ValueResolvers/RequestBody/)

* * *

## The `MIME` Component

The highlight of this cycle is the introduction of a new `MIME` component that provides an RFC compliant way to create and manipulate MIME messages, or as they’re better known as, emails:

```crystal
email = AMIME::Email
  .new
  .from("me@example.com")
  .to("you@example.com")
  .cc("them@example.com")
  .bcc("other@example.com")
  .reply_to("me@example.com")
  .priority(:high)
  .subject("Important Notification")
  .text("Lorem ipsum...")
  .html("<h1>Lorem ipsum</h1> <p>...</p>")
  .attach_from_path("/path/to/file.pdf", "my-attachment.pdf")
  .embed_from_path("/path/to/logo.png")

```

This component does not handle _sending_ emails, but is intended to act as a base for future Athena components that could do so. It of course may also be used by other shards who would like a stable/standardized way of representing emails to be sent such that they could be inner-operable with any other shard also using it.

## Proxy & Load Balancer

It is a common practice to host a web application behind a load balancer or reverse proxy. This by itself is not a problem for Athena. However it does allow a way for that extra metadata to be exposed, which is not currently exposed on the request object. See [Expose connection metadata in HTTP::Server::Context · Issue #5784 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/5784), [Why HTTP::Request uri is private and scheme · Issue #7096 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/7096), and [[RFC] Effective request URL for `HTTP::Server` · Issue #10246 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/10246) for more details.

Even if it was, they would not expose the correct values since they’d be based on the reverse proxy/load balance and not the client’s original request. This release brings support for the various `x-forwarded-*` [Proxy Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Proxy_servers_and_tunneling#forwarding_client_information_through_proxies).

For example to to allow reading the port and scheme from requests originating from proxies under your control:

```crystal
ATH.configure({
  framework: {
    # The IP address (or range) of your proxy.
    trusted_proxies: ["192.0.0.1", "10.0.0.0/8"],

    # Trust only `x-forwarded-port` and `x-forwarded-proto` headers.
    trusted_headers: ATH::Request::ProxyHeader[:forwarded_port, :forwarded_proto]
  },
})

```

The implementation also supports dynamic IPs and custom header names for proxies that do not use the `x-forwarded-*` headers. See [Proxies & Load Balancers - Athena](https://athenaframework.org/guides/proxies/) for more details.

### Trusted Hosts

In a similar vein, this release also allows defining the host names that are considered [trusted](https://athenaframework.org/Framework/Bundle/Schema/#Athena::Framework::Bundle::Schema#trusted_hosts). This effectively prevents [host header attacks](https://www.skeletonscribe.net/2013/05/practical-http-host-header-attacks.html). When set, requests whose hostname does _NOT_ match any of the provided patterns will recieve a `400` response.

## Normalized Exception Types

Many of the Athena components defined an `Exceptions` namespace that custom exception types were included in. Some components additionally defined a base exception type like `ConsoleException` to use as the base of all other. The idea being the user would be able to `rescue` it in order to handle any exception raised from within the component itself. The problem there was that it prevented the custom error types from inheriting related stdlib exception types, such as `ArgumentError`.

With the release of [Allow rescuing exceptions that include a module by Blacksmoke16 · Pull Request #14553 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/pull/14553), all of the components had their exception namespace normalized to be `Exception`, and this module is then included in all custom exception types. Meaning it is now possible to do things like:

1. Rescue `ACON::Exception` to handle any exception type defined by the component itself
2. Rescue `ArgumentError` to handle any exception resulting from unexpected/invalid data either from the component, or Crystal stdlib
3. Rescue `ACON::Exception::InvalidArgument` to rescue only unexpected/invalid data exceptions from the component only

Ultimately this makes error handling a bit more standardized and flexible depending on the exact need.

## Refactored query param / form data deseralization support

Last but not least this PR completely refactors how query parameters and form data is handled. Previously the implementation was its own disparate thing. It is now implemented within the controller argument [value resolver](https://athenaframework.org/Framework/Controller/ValueResolvers/Interface/) implementation. This ultimately makes things easier to maintain.

Query parameters are now handled by annotating the related parameter directly, instead of the controller action method.

```crystal
class ExampleController < ATH::Controller
  @[ARTA::Get("/")]
  def index(@[ATHA::MapQueryParameter] page : Int32) : Int32
    page
  end
end

```

Additionally with the introduction of [https://github.com/crystal-lang/crystal/pull/14684](https://github.com/crystal-lang/crystal/pull/14684) into the stdlib, the `ATHR::RequestBody` value resolver now natively handles `x-www-form-urlencoded` request payloads:

```crystal
record LoginDTO, username : String, password : String do
  include URI::Params::Serializable
end

class ExampleController < ATH::Controller
  @[ARTA::Post("/login")]
  def login(@[ATHA::MapRequestBody] login : LoginDTO) : Nil
    # ...
  end
end

```

Which can in turn have validation constraints attached to them.

Lastly, the new [ATHA::MapQueryString](https://athenaframework.org/Framework/Annotations/MapQueryString/) annotation can do a similar thing, but for the query string of a request:

```crystal
record PaginationContext, page : Int32 = 1, per_page : Int32 = 100 do
  include URI::Params::Serializable
end

class ArticleController < ATH::Controller
  @[ARTA::Get("/articles")]
  def articles(
    @[ATHA::MapQueryString]
    pagination_context : PaginationContext,
  ) : Array(Article)
    # ...
  end
end

```

Such that you have a more reusable, testable, and flexible setup than using many `ATHA::MapQueryParameter` annotations.

## Bonus Section! Improving DX

### Code Coverage

As primarily a one person developer, I rely heavily on Athena’s test suite to ensure things are working as expected as I make changes. A behind-the-scenes track of work this cycle was a push to better integrate code coverage reporting is a direct result of this. The idea being it provides numbers to back of the fact that Athena is well tested.

The result of all this work is I’m pleased to say Athena’s test coverage is ~97.5% at time of writing. This can be explored in more detail on [Codecov](https://app.codecov.io/gh/athena-framework/athena). PRs are welcomed to improve this further ;) CI is setup to soft-require 100% coverage on _new_ code, but of course there are some cases where this is not possible, so mostly serves as a guideline not a hard requirement.

There are also some outstanding issues like [Missing DWARF debug information · Issue #15360 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/15360) that would likely improve coverage further as Crystal’s debug information gets more robust.

However, while this is good and all, the next major hurdle would be integrating [Macro Code Coverage · Issue #14880 · crystal-lang/crystal · GitHub](https://github.com/crystal-lang/crystal/issues/14880) when/if a solution is merged/released. This would ensure that Athena’s macro code, especially within the DI component, is working as expected as well.

### Local Development

I also spent some time replacing Athena’s monorepo’s `Makefile` with a [justfile](https://just.systems/man/en/) and revamping its `CONTRIBUTING.md` file. So there has never been a better time to [contribute](https://github.com/athena-framework/athena/contribute)!

* * *

And that’s it for this release! As usual feel free to join me in the Athena [Discord server](https://discord.gg/TmDVPb3dmr) if you have any suggestions, questions, or ideas. I’m also available via [Email](mailto:contact@athenaframework.org).
